Next.js 16 + TypeScript + Tailwind v4 + Motion project showcasing interface components built on semantic design tokens: - Light/dark/system theming from one CSS variable set, with a pre-paint script so a saved theme never flashes - Button, Switch and SegmentedControl with keyboard support, ARIA semantics and spring motion that respects reduced-motion - Vitest + Testing Library tests, including a token-coverage test - /tokens page documenting colour and motion tokens Co-Authored-By: Claude Opus 5.5 <[email protected]>
91 lines
2.1 KiB
CSS
91 lines
2.1 KiB
CSS
@import "tailwindcss";
|
|
|
|
/*
|
|
* Design tokens. These are the only place colours are written down.
|
|
* Components use the semantic names (bg-surface, text-muted, ...).
|
|
* They never use raw palette values.
|
|
* src/tokens/tokens.test.ts checks that every token in tokens.ts is defined
|
|
* here for both themes.
|
|
*/
|
|
|
|
:root,
|
|
[data-theme="light"] {
|
|
color-scheme: light;
|
|
--background: #fafafa;
|
|
--surface: #ffffff;
|
|
--surface-hover: #f2f2f3;
|
|
--border: #e4e4e7;
|
|
--foreground: #18181b;
|
|
--muted: #71717a;
|
|
--accent: #4f46e5;
|
|
--accent-hover: #4338ca;
|
|
--accent-foreground: #ffffff;
|
|
--danger: #dc2626;
|
|
--ring: #6366f1;
|
|
}
|
|
|
|
@media (prefers-color-scheme: dark) {
|
|
:root:not([data-theme="light"]) {
|
|
color-scheme: dark;
|
|
--background: #09090b;
|
|
--surface: #131316;
|
|
--surface-hover: #1c1c21;
|
|
--border: #2a2a30;
|
|
--foreground: #f4f4f5;
|
|
--muted: #a1a1aa;
|
|
--accent: #818cf8;
|
|
--accent-hover: #a5b4fc;
|
|
--accent-foreground: #0b0b1a;
|
|
--danger: #f87171;
|
|
--ring: #a5b4fc;
|
|
}
|
|
}
|
|
|
|
[data-theme="dark"] {
|
|
color-scheme: dark;
|
|
--background: #09090b;
|
|
--surface: #131316;
|
|
--surface-hover: #1c1c21;
|
|
--border: #2a2a30;
|
|
--foreground: #f4f4f5;
|
|
--muted: #a1a1aa;
|
|
--accent: #818cf8;
|
|
--accent-hover: #a5b4fc;
|
|
--accent-foreground: #0b0b1a;
|
|
--danger: #f87171;
|
|
--ring: #a5b4fc;
|
|
}
|
|
|
|
@theme inline {
|
|
--color-background: var(--background);
|
|
--color-surface: var(--surface);
|
|
--color-surface-hover: var(--surface-hover);
|
|
--color-border: var(--border);
|
|
--color-foreground: var(--foreground);
|
|
--color-muted: var(--muted);
|
|
--color-accent: var(--accent);
|
|
--color-accent-hover: var(--accent-hover);
|
|
--color-accent-foreground: var(--accent-foreground);
|
|
--color-danger: var(--danger);
|
|
--color-ring: var(--ring);
|
|
|
|
--font-sans: var(--font-geist-sans);
|
|
--font-mono: var(--font-geist-mono);
|
|
|
|
--ease-snappy: cubic-bezier(0.2, 0.8, 0.2, 1);
|
|
}
|
|
|
|
body {
|
|
background: var(--background);
|
|
color: var(--foreground);
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
*,
|
|
*::before,
|
|
*::after {
|
|
animation-duration: 0.01ms !important;
|
|
transition-duration: 0.01ms !important;
|
|
}
|
|
}
|