@fluid-theme/core 0.4.3 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/AGENTS.md +6 -29
  2. package/CHANGELOG.md +564 -2
  3. package/CLAUDE.md +201 -14
  4. package/GEMINI.md +6 -45
  5. package/README.md +17 -8
  6. package/assets/js/compare.js +77 -0
  7. package/assets/js/fit-text.js +103 -0
  8. package/assets/js/lightbox.js +153 -0
  9. package/assets/js/scroll-reveal.js +72 -0
  10. package/assets/js/toc-build.mjs +86 -0
  11. package/assets/js/toc.js +384 -0
  12. package/dist/fluid-1x.min.css +4 -0
  13. package/dist/fluid-fonts-archivo.min.css +2 -0
  14. package/dist/fluid-fonts-bodoni.min.css +2 -0
  15. package/dist/fluid-fonts.min.css +2 -0
  16. package/dist/fluid-legacy-1x.css +3 -0
  17. package/dist/fluid-nav.min.css +3 -0
  18. package/dist/fluid-toc.min.css +3 -0
  19. package/dist/fluid-zero.min.css +3 -0
  20. package/dist/fluid.min.css +3 -2
  21. package/dist/fonts/OFL-archivo.txt +93 -0
  22. package/dist/fonts/OFL-bodoni-moda.txt +93 -0
  23. package/dist/fonts/OFL-hanken-grotesk.txt +93 -0
  24. package/dist/fonts/OFL-newsreader.txt +93 -0
  25. package/dist/fonts/OFL-schibsted-grotesk.txt +93 -0
  26. package/dist/fonts/archivo-latin-wdth-italic.woff2 +0 -0
  27. package/dist/fonts/archivo-latin-wdth-normal.woff2 +0 -0
  28. package/dist/fonts/bodoni-moda-latin-opsz-italic.woff2 +0 -0
  29. package/dist/fonts/bodoni-moda-latin-opsz-normal.woff2 +0 -0
  30. package/dist/fonts/hanken-grotesk-latin-wght-italic.woff2 +0 -0
  31. package/dist/fonts/hanken-grotesk-latin-wght-normal.woff2 +0 -0
  32. package/dist/fonts/newsreader-latin-opsz-italic.woff2 +0 -0
  33. package/dist/fonts/newsreader-latin-opsz-normal.woff2 +0 -0
  34. package/dist/fonts/schibsted-grotesk-latin-wght-italic.woff2 +0 -0
  35. package/dist/fonts/schibsted-grotesk-latin-wght-normal.woff2 +0 -0
  36. package/fluid-agent-kit/README.md +1 -0
  37. package/fluid-agent-kit/skills/accessibility.md +3 -1
  38. package/fluid-agent-kit/skills/css-conventions.md +22 -5
  39. package/fluid-agent-kit/skills/image-optimization.md +2 -0
  40. package/fluid-agent-kit/skills/preset-composition.md +108 -0
  41. package/package.json +36 -11
  42. package/src/animations/scroll-reveal.css +75 -193
  43. package/src/components/accordion.css +6 -47
  44. package/src/components/alert.css +47 -88
  45. package/src/components/avatar.css +4 -20
  46. package/src/components/badge.css +25 -48
  47. package/src/components/button.css +69 -175
  48. package/src/components/card.css +146 -481
  49. package/src/components/chip.css +46 -12
  50. package/src/components/compare.css +234 -0
  51. package/src/components/dropdown.css +10 -67
  52. package/src/components/empty-state.css +4 -0
  53. package/src/components/form.css +113 -81
  54. package/src/components/gallery.css +252 -0
  55. package/src/components/hero.css +203 -88
  56. package/src/components/indicator.css +23 -117
  57. package/src/components/loading.css +7 -29
  58. package/src/components/modal.css +12 -65
  59. package/src/components/nav-modes.css +261 -0
  60. package/src/components/nav.css +324 -392
  61. package/src/components/search-input.css +4 -2
  62. package/src/components/step-indicator.css +26 -27
  63. package/src/components/table.css +64 -171
  64. package/src/components/toc.css +254 -0
  65. package/src/components/tooltip.css +5 -46
  66. package/src/core/base.css +393 -547
  67. package/src/core/reset.css +17 -5
  68. package/src/core/tokens.css +426 -2276
  69. package/src/fluid.css +258 -17
  70. package/src/fonts/OFL-newsreader.txt +93 -0
  71. package/src/fonts/OFL-schibsted-grotesk.txt +93 -0
  72. package/src/fonts/fluid-fonts.css +55 -0
  73. package/src/fonts/newsreader-latin-opsz-italic.woff2 +0 -0
  74. package/src/fonts/newsreader-latin-opsz-normal.woff2 +0 -0
  75. package/src/fonts/schibsted-grotesk-latin-wght-italic.woff2 +0 -0
  76. package/src/fonts/schibsted-grotesk-latin-wght-normal.woff2 +0 -0
  77. package/src/layouts/positioning.css +6 -3
  78. package/src/legacy-1x.css +61 -0
  79. package/src/sections/cta.css +33 -101
  80. package/src/sections/features.css +42 -219
  81. package/src/sections/footer.css +53 -134
  82. package/src/sections/stats.css +40 -153
  83. package/src/sections/team.css +24 -113
  84. package/src/sections/testimonials.css +34 -148
  85. package/src/zero/base.css +166 -0
  86. package/src/zero/tokens.css +64 -0
  87. package/src/zero.css +20 -0
  88. package/starters/workflows/README.md +40 -0
  89. package/starters/workflows/dependabot.yml +21 -0
  90. package/starters/workflows/update-fluid-theme.yml +296 -0
  91. package/themes/fonts/OFL-archivo.txt +93 -0
  92. package/themes/fonts/OFL-bodoni-moda.txt +93 -0
  93. package/themes/fonts/OFL-hanken-grotesk.txt +93 -0
  94. package/themes/fonts/archivo-latin-wdth-italic.woff2 +0 -0
  95. package/themes/fonts/archivo-latin-wdth-normal.woff2 +0 -0
  96. package/themes/fonts/archivo.css +20 -0
  97. package/themes/fonts/bodoni-moda-latin-opsz-italic.woff2 +0 -0
  98. package/themes/fonts/bodoni-moda-latin-opsz-normal.woff2 +0 -0
  99. package/themes/fonts/bodoni.css +35 -0
  100. package/themes/fonts/hanken-grotesk-latin-wght-italic.woff2 +0 -0
  101. package/themes/fonts/hanken-grotesk-latin-wght-normal.woff2 +0 -0
  102. package/themes/gallery.css +105 -0
  103. package/themes/swiss.css +53 -0
  104. package/src/components/scroll-progress.css +0 -104
  105. package/src/components/settings-panel.css +0 -603
  106. package/src/components/themed-images.css +0 -229
  107. package/src/components/version-badge.css +0 -70
@@ -1,167 +1,219 @@
1
1
  /**
2
- * Fluid Framework - Design Tokens
2
+ * Fluid Theme - Design Tokens
3
3
  * @layer tokens
4
4
  *
5
- * The single source of truth. Every value flows from here.
5
+ * Three kinds of rule, in this order:
6
+ * 1. KNOBS (the first :root rule, knobs only): the values a site, or an AI
7
+ * editing a site, is meant to change. Set them on :root in the site's
8
+ * own stylesheet (`themes` layer) or the stack's injected tokens.
9
+ * 2. SCALES (a plain :root rule): type, space, radius, motion, shadow and
10
+ * z-index steps the knobs and the components draw on.
11
+ * 3. THE ROLE SCOPE: every colour role, declared once, on :root AND on each
12
+ * dark band (.section[data-bg="inverse"], .hero[data-theme="dark"],
13
+ * .site-footer[data-dark]) and on the nav, inverse by default (not
14
+ * data-bg="plain" or transparent).
15
+ * The build lowers light-dark() to variables that resolve where a role
16
+ * is declared, so a role declared only on :root would not flip on a band
17
+ * that sets color-scheme: dark. Declaring the roles on the band too
18
+ * re-resolves the same light-dark() values there.
6
19
  *
7
- * Color philosophy: OKLCH-based, black & white foundation.
8
- * - Perceptually uniform lightness (50% gray LOOKS like 50%)
9
- * - Themes need only 2 colors: light and dark
10
- * - All other colors derive mathematically
20
+ * Colour is OKLCH, derived from --accent and --neutral-tint. Dark mode follows
21
+ * the OS; <html data-theme="light"> or "dark" overrides it (read on <html>).
11
22
  */
12
23
 
13
24
  @layer tokens {
14
25
  :root {
26
+ color-scheme: light dark;
27
+
15
28
  /* ========================================
16
- OKLCH COLOR SYSTEM
17
- Perceptually uniform, mathematically derived
29
+ KNOBS
18
30
  ======================================== */
19
31
 
20
- /* === FOUNDATION: Pure Black & White === */
21
- --black: oklch(0% 0 0);
22
- --white: oklch(100% 0 0);
23
-
24
-
25
- /* === GRAY SCALE: Perceptually Even Steps ===
26
- In OKLCH, 50% lightness actually LOOKS 50% gray.
27
- These steps are mathematically uniform to human vision. */
28
- --gray-5: oklch(5% 0 0);
29
- --gray-10: oklch(10% 0 0);
30
- --gray-15: oklch(15% 0 0);
31
- --gray-20: oklch(20% 0 0);
32
- --gray-25: oklch(25% 0 0);
33
- --gray-30: oklch(30% 0 0);
34
- --gray-35: oklch(35% 0 0);
35
- --gray-40: oklch(40% 0 0);
36
- --gray-45: oklch(45% 0 0);
37
- --gray-50: oklch(50% 0 0);
38
- --gray-55: oklch(55% 0 0);
39
- --gray-60: oklch(60% 0 0);
40
- --gray-65: oklch(65% 0 0);
41
- --gray-70: oklch(70% 0 0);
42
- --gray-75: oklch(75% 0 0);
43
- --gray-80: oklch(80% 0 0);
44
- --gray-85: oklch(85% 0 0);
45
- --gray-90: oklch(90% 0 0);
46
- --gray-95: oklch(95% 0 0);
47
- --gray-98: oklch(98% 0 0);
48
-
49
-
50
- /* === THEME FOUNDATION ===
51
- Themes override ONLY these two variables.
52
- Everything else derives automatically. */
53
- --theme-dark: var(--black);
54
- --theme-light: var(--white);
55
-
56
- /* Note: --theme-chroma/--theme-hue and --blend-* scale removed (unused).
57
- Semantic color tokens (--color-text, --color-surface, etc.) are used instead. */
58
-
59
-
60
- /* === ACCENT COLOR ===
61
- Default: a mid-gray. Themes can override with any color.
62
- Variants auto-calculate lighter/darker versions. */
63
- --accent: var(--gray-40);
64
- --accent-subtle: color-mix(in oklch, var(--accent) 15%, var(--theme-light));
65
- --accent-hover: color-mix(in oklch, var(--accent) 85%, var(--theme-dark));
66
- --accent-active: color-mix(in oklch, var(--accent) 70%, var(--theme-dark));
67
-
68
-
69
- /* === SEMANTIC COLORS: Light Mode ===
70
- These map the foundation to meaningful roles. */
71
- --color-text: var(--gray-15);
72
- --color-text-muted: var(--gray-40);
73
- --color-text-subtle: var(--gray-55);
74
-
75
- --color-background: var(--white);
76
- --color-surface: var(--gray-98);
77
- --color-surface-raised: var(--white);
78
-
79
- --color-border: var(--gray-85);
80
- --color-border-subtle: var(--gray-90);
81
-
82
- --color-focus: var(--accent);
83
- --color-selection: var(--accent-subtle);
84
-
85
-
86
- /* === FEEDBACK COLORS ===
87
- Muted, not alarming. OKLCH for perceptual consistency. */
88
- --color-success: oklch(55% 0.15 145);
89
- --color-warning: oklch(70% 0.15 70);
90
- --color-error: oklch(55% 0.18 25);
91
- --color-info: oklch(55% 0.12 240);
32
+ /* --- Colour ---
33
+ --accent the brand colour as a FILL (primary button, accent-strong
34
+ band, active marks). Any OKLCH colour; a spice, never a
35
+ default background.
36
+ --on-accent text ON an accent fill: white below L 0.575, near black
37
+ above. An accent between L 0.55 and 0.60 clears 4.5:1
38
+ with neither for some hues: avoid that band or set this.
39
+ --accent-ink the accent as TEXT and focus ring (derived; it lives in
40
+ the role scope below so it flips on dark bands).
41
+ --neutral-tint the colour the greys lean toward. Only its hue and chroma
42
+ are used; surfaces set their own lightness. */
43
+ --accent: oklch(42% 0.15 22); /* deep oxblood; white on it ≈ 8.5:1 */
44
+ --on-accent: oklch(from var(--accent) clamp(0, (0.575 - l) * 1000, 1) 0 0);
45
+ --neutral-tint: oklch(50% 0.01 75); /* warm paper */
46
+
47
+ /* --- Type ---
48
+ --font-heading the main face knob: display, h1-h3, lede, card titles,
49
+ stat numerals, nav brand. Newsreader when the fonts file
50
+ (dist/fluid-fonts.min.css) is linked; a system serif without it.
51
+ --font-display the display face; follows --font-heading. Set it only
52
+ when the display face differs from the heading face.
53
+ --font-body text, h4-h6, labels, nav links, buttons, forms, tables.
54
+ --text-min / --text-max body size at 360px and at 1440px, in rem
55
+ (unitless); every step of the scale is built on them.
56
+ --type-ratio the desktop scale ratio (phones use min(1.25, ratio)).
57
+ --weight-display weight of display, h1 and h2 (h3 is +100, at most
58
+ 700 unless the display is heavier).
59
+ --weight-heading (optional, no default) one weight for h2 and h3
60
+ instead of the display ladder; legacy-1x sets it.
61
+ --h1-size, --h2-size, --h3-size (optional, no default) a heading's
62
+ size outright, instead of its step; legacy-1x sets them.
63
+ --display-max the cap on a display headline's size. --fit 8 at
64
+ 10rem fills an 80rem column exactly, so the cap and
65
+ --container-content agree; a headline straight in a
66
+ .content-grid fits the reading column instead.
67
+ --measure the reading width of prose (58ch is about 72
68
+ characters of the body face).
69
+ --hero-headline-size has NO default in v2: .display and .hero__headline
70
+ fit their column (--fit). Setting it on :root still wins. */
71
+ --font-heading: "Newsreader Variable", "Newsreader Fallback", "Iowan Old Style", "Sitka Heading",
72
+ Charter, "Palatino Linotype", "URW Palladio L", P052, Georgia, serif;
73
+ --font-display: var(--font-heading);
74
+ --font-body: "Schibsted Grotesk Variable", "Schibsted Fallback", "Helvetica Neue",
75
+ "Segoe UI Variable Text", "Arial Nova", "Nimbus Sans", Arial, sans-serif;
76
+ --text-min: 1.0625; /* 17px */
77
+ --text-max: 1.25; /* 20px */
78
+ --type-ratio: 1.414;
79
+ --weight-display: 400;
80
+ --display-max: 10rem;
81
+ --measure: 58ch;
82
+
83
+ /* --- Shape --- */
84
+ --radius-card: 0;
85
+ --radius-control: 0; /* buttons, inputs, selects, nav links, tabs, badges */
86
+ --radius-pill: 9999px; /* chips, data-pill buttons */
87
+ --border-width: 1px;
88
+ --shadow-style: 0; /* 0 = none, 1 = soft, 2 = hard (offset, no blur) */
89
+ --shadow-offset: 4px; /* how far a hard shadow sits from its box */
92
90
 
93
- /* Subtle backgrounds for feedback */
94
- --color-success-subtle: oklch(95% 0.03 145);
95
- --color-warning-subtle: oklch(95% 0.04 70);
96
- --color-error-subtle: oklch(95% 0.03 25);
97
- --color-info-subtle: oklch(95% 0.02 240);
91
+ /* --- Space --- */
92
+ --space-section: var(--space-2xl); /* block padding of a band: 72 → 160px */
93
+ /* --space-gutter is the 1.x knob: the page edge on BOTH sides together.
94
+ Each side gets half of it, never less than 3vi (--page-edge). */
95
+ --space-gutter: clamp(2.5rem, 1rem + 6.6667vi, 7rem); /* 40 → 112px, so 20 → 56px a side */
96
+ --container-content: 80rem;
97
+ --rail: 0rem; /* start-side label track of .content-grid; widen it to hang labels */
98
98
 
99
+ /* --- Motion --- (speed per element: data-motion="reduced|slow|fast") */
100
+ --motion-base: 320ms;
99
101
 
100
- /* === SHADOW COLOR ===
101
- Uses theme dark for natural shadows */
102
- --shadow-color: var(--theme-dark);
103
- --shadow-opacity: 0.08;
102
+ /* The inverse band's paint, resolved here on purpose (a band cannot
103
+ resolve its own background from its flipped roles). */
104
+ --band-inverse: light-dark(
105
+ oklch(from var(--neutral-tint) 0.17 c h),
106
+ oklch(from var(--neutral-tint) 0.09 c h)
107
+ );
108
+ }
104
109
 
110
+ :root {
111
+ font-synthesis: none;
112
+ font-kerning: normal;
113
+ font-optical-sizing: auto;
105
114
 
106
115
  /* ========================================
107
- TYPOGRAPHY SYSTEM
108
- Fluid scales that breathe
116
+ TYPOGRAPHY
109
117
  ======================================== */
110
118
 
111
- /* Font stacks */
112
119
  --font-system: system-ui, -apple-system, "Segoe UI", Roboto,
113
120
  "Helvetica Neue", Arial, sans-serif;
114
121
  --font-serif: "Iowan Old Style", "Palatino Linotype", Palatino,
115
122
  Georgia, serif;
116
- --font-mono: ui-monospace, "SF Mono", "Cascadia Code",
117
- "Source Code Pro", Menlo, monospace;
118
-
119
- /* Base size - flows smoothly with viewport */
120
- --font-size-base: clamp(1rem, 0.95rem + 0.25vw, 1.125rem);
121
-
122
- /* Fluid type scale — each size has its own clamp() range.
123
- Larger sizes compress more on small screens, expand more on large.
124
- This preserves typographic hierarchy at every viewport width.
125
- Calculated for 320px → 1440px fluid range. */
126
- --font-size-xs: clamp(0.75rem, 0.73rem + 0.09vw, 0.8125rem);
127
- --font-size-sm: clamp(0.875rem, 0.84rem + 0.18vw, 0.9375rem);
128
- --font-size-md: var(--font-size-base);
129
- --font-size-lg: clamp(1.125rem, 1.05rem + 0.36vw, 1.25rem);
123
+ --font-mono: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas, monospace;
124
+
125
+ /* The scale: one generator, one knob. Fluid from 360px to 1440px, flat
126
+ outside. Phones use min(1.25, --type-ratio), desktops --type-ratio.
127
+ Every value keeps a rem term, so zoom works (WCAG 1.4.4). Steps 0-4
128
+ stay within max <= 2.5 × min; 5 and 6 are display-only.
129
+
130
+ Resolved at the default ratio 1.414 (px; phones use 1.25):
131
+ token 360px 1440px used for
132
+ --step--2 13 13 label voice, legal
133
+ --step--1 14 14 caption, meta, TOC
134
+ --step-0 17 20 body
135
+ --step-1 21.3 28.3 lede, h3, card title, blockquote
136
+ --step-2 26.6 40 h2 in prose
137
+ --step-3 33.2 56.5 section heading, pull quote
138
+ --step-4 41.5 80 h1, stat numeral, display floor
139
+ --step-5 51.9 113 large display
140
+ --step-6 64.8 160 the biggest display
141
+ At --type-ratio 1.25 the 1440px column is 20/25/31.3/39.1/48.8/61/76.3.
142
+ Do not register these (@property): they hold vi and must stay unresolved. */
143
+ --_r: min(1.25, var(--type-ratio)); /* phone ratio; never above the desktop one */
144
+ --_lo: var(--text-min); /* body at 360px, in rem */
145
+ --_hi: var(--text-max); /* body at 1440px, in rem */
146
+ --_t: calc((100vi - 22.5rem) / 67.5); /* 0 at 360px, 1rem at 1440px */
147
+ --step--2: 0.8125rem;
148
+ --step--1: 0.875rem;
149
+ --step-0: clamp(calc(var(--_lo) * 1rem), calc(var(--_lo) * 1rem + (var(--_hi) - var(--_lo)) * var(--_t)), calc(var(--_hi) * 1rem));
150
+ --step-1: clamp(
151
+ calc(var(--_lo) * pow(var(--_r), 1) * 1rem),
152
+ calc(var(--_lo) * pow(var(--_r), 1) * 1rem + (var(--_hi) * pow(var(--type-ratio), 1) - var(--_lo) * pow(var(--_r), 1)) * var(--_t)),
153
+ calc(var(--_hi) * pow(var(--type-ratio), 1) * 1rem));
154
+ --step-2: clamp(
155
+ calc(var(--_lo) * pow(var(--_r), 2) * 1rem),
156
+ calc(var(--_lo) * pow(var(--_r), 2) * 1rem + (var(--_hi) * pow(var(--type-ratio), 2) - var(--_lo) * pow(var(--_r), 2)) * var(--_t)),
157
+ calc(var(--_hi) * pow(var(--type-ratio), 2) * 1rem));
158
+ --step-3: clamp(
159
+ calc(var(--_lo) * pow(var(--_r), 3) * 1rem),
160
+ calc(var(--_lo) * pow(var(--_r), 3) * 1rem + (var(--_hi) * pow(var(--type-ratio), 3) - var(--_lo) * pow(var(--_r), 3)) * var(--_t)),
161
+ calc(var(--_hi) * pow(var(--type-ratio), 3) * 1rem));
162
+ --step-4: clamp(
163
+ calc(var(--_lo) * pow(var(--_r), 4) * 1rem),
164
+ calc(var(--_lo) * pow(var(--_r), 4) * 1rem + (var(--_hi) * pow(var(--type-ratio), 4) - var(--_lo) * pow(var(--_r), 4)) * var(--_t)),
165
+ calc(var(--_hi) * pow(var(--type-ratio), 4) * 1rem));
166
+ --step-5: clamp(
167
+ calc(var(--_lo) * pow(var(--_r), 5) * 1rem),
168
+ calc(var(--_lo) * pow(var(--_r), 5) * 1rem + (var(--_hi) * pow(var(--type-ratio), 5) - var(--_lo) * pow(var(--_r), 5)) * var(--_t)),
169
+ calc(var(--_hi) * pow(var(--type-ratio), 5) * 1rem));
170
+ --step-6: clamp(
171
+ calc(var(--_lo) * pow(var(--_r), 6) * 1rem),
172
+ calc(var(--_lo) * pow(var(--_r), 6) * 1rem + (var(--_hi) * pow(var(--type-ratio), 6) - var(--_lo) * pow(var(--_r), 6)) * var(--_t)),
173
+ calc(var(--_hi) * pow(var(--type-ratio), 6) * 1rem));
174
+
175
+ /* Legacy names, still read by consumers. xs-lg follow the new scale;
176
+ xl-5xl keep their 1.x values so UI and admin text does not drift
177
+ (headings use --step-*). */
178
+ --font-size-xs: var(--step--2);
179
+ --font-size-sm: var(--step--1);
180
+ --font-size-base: var(--step-0);
181
+ --font-size-lg: clamp(1.1875rem, 1.125rem + 0.2778vi, 1.375rem);
130
182
  --font-size-xl: clamp(1.25rem, 1.12rem + 0.63vw, 1.5rem);
131
183
  --font-size-2xl: clamp(1.5rem, 1.29rem + 1.04vw, 1.875rem);
132
184
  --font-size-3xl: clamp(1.75rem, 1.43rem + 1.61vw, 2.375rem);
133
185
  --font-size-4xl: clamp(2rem, 1.5rem + 2.5vw, 3rem);
134
186
  --font-size-5xl: clamp(2.25rem, 1.5rem + 3.75vw, 3.75rem);
135
187
 
136
- /* Line heights - Room to breathe */
137
188
  --leading-none: 1;
138
189
  --leading-tight: 1.2;
139
190
  --leading-snug: 1.35;
140
- --leading-normal: 1.6;
141
- --leading-relaxed: 1.75;
142
- --leading-loose: 2;
191
+ --leading-normal: 1.65;
192
+ --leading-relaxed: 1.8;
193
+ --leading-body: 1.6;
194
+ --leading-heading: calc(1em + 0.2rem); /* tightens as size grows: 1.11 at 28px, 1.02 at 160px */
143
195
 
144
- /* Letter spacing */
145
196
  --tracking-tight: -0.025em;
146
- --tracking-normal: 0;
147
- --tracking-wide: 0.025em;
197
+ --tracking-normal: -0.01em;
198
+ --tracking-wide: 0.04em;
148
199
  --tracking-wider: 0.05em;
200
+ --tracking-display: -0.03em;
201
+ --tracking-heading: -0.015em;
202
+ --tracking-caps: 0.14em;
149
203
 
150
- /* Font weights */
151
- --weight-light: 300;
204
+ --weight-light: 350;
152
205
  --weight-normal: 400;
153
206
  --weight-medium: 500;
154
207
  --weight-semibold: 600;
155
208
  --weight-bold: 700;
209
+ --_dark-weight: 0; /* 30 in dark mode and on dark bands: thin strokes survive light-on-dark */
156
210
 
157
211
 
158
212
  /* ========================================
159
- SPACING SYSTEM
160
- Fibonacci-inspired for natural rhythm
213
+ SPACING
214
+ Fixed 4px steps for components; fluid steps (360 → 1440px) for layout.
161
215
  ======================================== */
162
216
 
163
- --space-0: 0;
164
- --space-px: 1px;
165
217
  --space-0-5: 0.125rem; /* 2px */
166
218
  --space-1: 0.25rem; /* 4px */
167
219
  --space-1-5: 0.375rem; /* 6px */
@@ -177,131 +229,82 @@
177
229
  --space-20: 5rem; /* 80px */
178
230
  --space-24: 6rem; /* 96px */
179
231
 
180
- /* Semantic spacing */
181
- --space-gutter: var(--space-5);
182
- --space-section: var(--space-16);
232
+ --space-xs: clamp(0.5rem, 0.4583rem + 0.1852vi, 0.625rem); /* 8 → 10 */
233
+ --space-s: clamp(0.75rem, 0.6667rem + 0.3704vi, 1rem); /* 12 → 16 */
234
+ --space-m: clamp(1.25rem, 1.0833rem + 0.7407vi, 1.75rem); /* 20 → 28 */
235
+ --space-l: clamp(2rem, 1.6667rem + 1.4815vi, 3rem); /* 32 → 48 */
236
+ --space-xl: clamp(3rem, 2.3333rem + 2.963vi, 5rem); /* 48 → 80 */
237
+ --space-2xl: clamp(4.5rem, 2.6667rem + 8.1481vi, 10rem); /* 72 → 160 */
238
+ /* Per side, everywhere: half the gutter knob, with a 3vi floor (so at
239
+ 1440px a gutter under ~86px total has no effect). */
240
+ --page-edge: max(var(--space-gutter) / 2, 3vi);
183
241
 
184
242
 
185
243
  /* ========================================
186
- CONTAINER SYSTEM
187
- Content width constraints
244
+ CONTAINERS
188
245
  ======================================== */
189
246
 
190
- --container-xs: 20rem; /* 320px - Narrow text */
191
- --container-sm: 32rem; /* 512px - Comfortable reading */
192
- --container-md: 48rem; /* 768px - Articles */
193
- --container-lg: 64rem; /* 1024px - Wide content */
194
- --container-xl: 80rem; /* 1280px - Full layouts */
195
- --container-2xl: 96rem; /* 1536px - Ultra wide */
247
+ --container-sm: 32rem; /* 512px */
248
+ --container-md: 48rem; /* 768px */
249
+ --container-lg: 64rem; /* 1024px */
250
+ --container-xl: 80rem; /* 1280px */
251
+ --container-2xl: 96rem; /* 1536px */
252
+ --container-prose: var(--measure);
196
253
 
197
- /* Optimal reading width */
198
- --container-prose: 65ch;
199
254
 
200
-
201
- /* ========================================
202
- ANIMATION TOKENS
203
- Smooth, unhurried, like flowing water
204
-
205
- The "NSW Ratio" (0.73) creates proportional harmony.
206
- Base duration × 0.73 = fast, × 1.37 = slow
207
- This creates a golden-ratio-like relationship between speeds.
208
- ======================================== */
209
-
210
- /* Base timing - the heartbeat of all motion */
211
- --motion-base: 467ms; /* ~0.5s feels distinctly fluid */
212
-
213
- /* Durations - Proportionally related via NSW ratio (0.73) */
214
- --duration-instant: 137ms; /* Micro-interactions */
215
- --duration-fast: calc(var(--motion-base) * 0.73); /* ~341ms - Quick feedback */
216
- --duration-normal: var(--motion-base); /* ~467ms - Standard transitions */
217
- --duration-slow: calc(var(--motion-base) * 1.37); /* ~640ms - Deliberate motion */
218
- --duration-slower: calc(var(--motion-base) * 1.73); /* ~808ms - Emphasis */
219
- --duration-slowest: calc(var(--motion-base) * 2.37); /* ~1107ms - Grand gestures */
220
-
221
- /* Easing - Natural, organic movement */
222
- --ease-default: cubic-bezier(0.4, 0, 0.2, 1);
223
- --ease-in: cubic-bezier(0.4, 0, 1, 1);
224
- --ease-out: cubic-bezier(0, 0, 0.2, 1);
225
- --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
226
-
227
- /* Fluid motion - like water flowing, unhurried */
255
+ /* MOTION: the durations are in their own rule below (data-motion). */
228
256
  --ease-fluid: cubic-bezier(0.33, 0, 0.2, 1);
229
- --ease-fluid-in: cubic-bezier(0.55, 0, 1, 0.45);
230
- --ease-fluid-out: cubic-bezier(0, 0.55, 0.45, 1);
231
-
232
- /* Gentle settle - like a leaf landing on still water */
233
257
  --ease-settle: cubic-bezier(0.22, 1, 0.36, 1);
234
258
 
235
259
 
236
260
  /* ========================================
237
- MOTION INTENSITY - Water Themes
238
- Scale motion from calm to dynamic
239
- Usage: <html data-motion="trickling">
261
+ RADIUS SCALE
262
+ The steps follow the knobs; --radius-full stays for true circles
263
+ (avatars, radio buttons, dots, spinners).
240
264
  ======================================== */
241
265
 
242
- /* Note: --motion-scale and --motion-instant/fast/normal/slow removed (unused).
243
- Components use the --duration-* and --motion-base system instead. */
266
+ --radius-sm: var(--radius-control);
267
+ --radius-md: var(--radius-control);
268
+ --radius-lg: var(--radius-card);
269
+ --radius-xl: var(--radius-card);
270
+ --radius-full: 9999px;
244
271
 
245
-
246
- /* ========================================
247
- BORDERS & RADII
248
- Soft edges, never harsh
249
- ======================================== */
250
-
251
- --radius-none: 0;
252
- --radius-sm: 0.25rem; /* 4px - Subtle */
253
- --radius-md: 0.5rem; /* 8px - Default */
254
- --radius-lg: 0.75rem; /* 12px - Prominent */
255
- --radius-xl: 1rem; /* 16px - Cards */
256
- --radius-2xl: 1.5rem; /* 24px - Modals */
257
- --radius-full: 9999px; /* Pills */
258
-
259
- --border-width: 1px;
260
- --border-width-thick: 2px;
272
+ --border-width-thick: calc(var(--border-width) * 2);
273
+ /* Every focus outline: var(--focus-ring-width) solid var(--color-focus).
274
+ Only the width is a token: the colour must resolve where it is used,
275
+ so a band that re-points --color-focus (accent-strong) is followed. */
276
+ --focus-ring-width: 2px;
261
277
 
262
278
 
263
279
  /* ========================================
264
- SHADOWS & ELEVATION
265
- Using theme color for natural shadows
280
+ SHADOWS
281
+ --shadow-style picks the family: each step is a soft (blurred) part
282
+ scaled by --_soft plus a hard (offset) part scaled by --_hard, so
283
+ 0 (the v2 default) turns both off, 1 is soft only and 2 is hard only.
266
284
  ======================================== */
267
285
 
268
- --shadow-xs:
269
- 0 1px 2px oklch(from var(--shadow-color) l c h / 0.04),
270
- 0 1px 3px oklch(from var(--shadow-color) l c h / 0.06);
271
-
272
- --shadow-sm:
273
- 0 2px 4px oklch(from var(--shadow-color) l c h / 0.04),
274
- 0 3px 6px oklch(from var(--shadow-color) l c h / 0.06);
286
+ --_soft: clamp(0, min(var(--shadow-style), 2 - var(--shadow-style)), 1);
287
+ --_hard: clamp(0, var(--shadow-style) - 1, 1);
288
+ --shadow-color: oklch(from var(--neutral-tint) 0.22 calc(c * 7) h);
289
+ --_hard-2: calc(var(--shadow-offset) * var(--_hard));
275
290
 
291
+ /* v2 reads only --shadow-md (the elevated card; compare.css), so
292
+ --shadow-style changes only that. The other 1.x steps had no reader
293
+ in core and were removed (base v2): --shadow-xs, --shadow-sm,
294
+ --shadow-lg, --shadow-xl, --shadow-glow, --_hard-1, --_hard-3. A page
295
+ that still reads one gets no shadow, as it did at --shadow-style 0. */
276
296
  --shadow-md:
277
- 0 4px 8px oklch(from var(--shadow-color) l c h / 0.04),
278
- 0 6px 12px oklch(from var(--shadow-color) l c h / 0.08);
279
-
280
- --shadow-lg:
281
- 0 8px 16px oklch(from var(--shadow-color) l c h / 0.04),
282
- 0 12px 24px oklch(from var(--shadow-color) l c h / 0.08);
283
-
284
- --shadow-xl:
285
- 0 16px 32px oklch(from var(--shadow-color) l c h / 0.06),
286
- 0 24px 48px oklch(from var(--shadow-color) l c h / 0.1);
287
-
288
- /* Inset shadow for depth */
289
- --shadow-inset: inset 0 2px 4px oklch(from var(--shadow-color) l c h / 0.06);
290
-
291
- /* Glow for focus states */
292
- --shadow-glow: 0 0 0 3px color-mix(in oklch, var(--accent) 30%, transparent);
293
-
294
- /* Offset shadow for rapids/brutalist theme */
295
- --shadow-offset: 4px 4px 0 oklch(0 0 0 / 0.12);
296
- --shadow-offset-hover: 6px 6px 0 oklch(0 0 0 / 0.15);
297
-
298
- /* Rating color (stars, reviews) */
299
- --color-rating: oklch(75% 0.18 85);
297
+ 0 4px 8px oklch(from var(--shadow-color) l c h / calc(0.04 * var(--_soft))),
298
+ 0 8px 20px oklch(from var(--shadow-color) l c h / calc(0.07 * var(--_soft))),
299
+ var(--_hard-2) var(--_hard-2) 0 var(--_hard-color);
300
+ --_hard-color: light-dark(
301
+ oklch(from var(--neutral-tint) 0.18 c h),
302
+ oklch(from var(--neutral-tint) 0.45 c h)
303
+ );
300
304
 
301
305
 
302
306
  /* ========================================
303
- Z-INDEX SCALE
304
- Predictable stacking
307
+ Z-INDEX
305
308
  ======================================== */
306
309
 
307
310
  --z-base: 0;
@@ -314,2103 +317,250 @@
314
317
  --z-toast: 1500;
315
318
  --z-tooltip: 1600;
316
319
  --z-max: 9999;
317
-
318
-
319
- /* Note: --opacity-* scale removed (unused).
320
- Opacity values are applied inline where needed. */
321
320
  }
322
321
 
323
322
 
324
323
  /* ========================================
325
- DARK MODE
326
- Simply invert the semantic mappings
324
+ COLOUR ROLES: THE ROLE SCOPE
325
+ Every role is declared here, once. To add a dark surface later, add its
326
+ selector here and to the band rule below; nothing else changes.
327
327
  ======================================== */
328
328
 
329
- @media (prefers-color-scheme: dark) {
330
- :root {
331
- /* Base dark mode: subtle cool tint (not pure black) */
332
- --color-text: oklch(92% 0.01 220);
333
- --color-text-muted: oklch(65% 0.01 220);
334
- --color-text-subtle: oklch(50% 0.01 220);
335
-
336
- --color-background: oklch(13% 0.02 230);
337
- --color-surface: oklch(17% 0.02 230);
338
- --color-surface-raised: oklch(21% 0.02 230);
339
-
340
- --color-border: oklch(25% 0.025 230);
341
- --color-border-subtle: oklch(20% 0.02 230);
342
-
343
- /* Deeper shadows in dark mode */
344
- --shadow-opacity: 0.25;
345
-
346
- /* Slightly brighter feedback colors for dark backgrounds */
347
- --color-success: oklch(65% 0.15 145);
348
- --color-warning: oklch(75% 0.15 70);
349
- --color-error: oklch(65% 0.18 25);
350
- --color-info: oklch(65% 0.12 240);
351
-
352
- /* Darker subtle backgrounds */
353
- --color-success-subtle: oklch(20% 0.04 145);
354
- --color-warning-subtle: oklch(20% 0.05 70);
355
- --color-error-subtle: oklch(20% 0.04 25);
356
- --color-info-subtle: oklch(20% 0.03 240);
357
-
358
- /* Stronger offset shadows for dark backgrounds */
359
- --shadow-offset: 4px 4px 0 oklch(0% 0 0 / 0.4);
360
- --shadow-offset-hover: 6px 6px 0 oklch(0% 0 0 / 0.5);
361
- }
362
- }
363
-
364
- /* Manual dark mode override (base — presets override below) */
365
- [data-theme="dark"] {
366
- --color-text: oklch(92% 0.01 220);
367
- --color-text-muted: oklch(65% 0.01 220);
368
- --color-text-subtle: oklch(50% 0.01 220);
369
-
370
- --color-background: oklch(13% 0.02 230);
371
- --color-surface: oklch(17% 0.02 230);
372
- --color-surface-raised: oklch(21% 0.02 230);
373
-
374
- --color-border: oklch(25% 0.025 230);
375
- --color-border-subtle: oklch(20% 0.02 230);
376
-
377
- --shadow-opacity: 0.25;
378
-
379
- --color-success: oklch(65% 0.15 145);
380
- --color-warning: oklch(75% 0.15 70);
381
- --color-error: oklch(65% 0.18 25);
382
- --color-info: oklch(65% 0.12 240);
383
-
384
- --color-success-subtle: oklch(20% 0.04 145);
385
- --color-warning-subtle: oklch(20% 0.05 70);
386
- --color-error-subtle: oklch(20% 0.04 25);
387
- --color-info-subtle: oklch(20% 0.03 240);
388
-
389
- --shadow-offset: 4px 4px 0 oklch(0% 0 0 / 0.4);
390
- --shadow-offset-hover: 6px 6px 0 oklch(0% 0 0 / 0.5);
391
- }
392
-
393
- /* Manual light mode override */
394
- [data-theme="light"] {
395
- --color-text: var(--gray-15);
396
- --color-text-muted: var(--gray-40);
397
- --color-text-subtle: var(--gray-55);
398
-
399
- --color-background: var(--white);
400
- --color-surface: var(--gray-98);
401
- --color-surface-raised: var(--white);
402
-
403
- --color-border: var(--gray-85);
404
- --color-border-subtle: var(--gray-90);
329
+ :root,
330
+ :is(.section, section)[data-bg="inverse"],
331
+ .hero[data-theme="dark"],
332
+ .site-footer[data-dark],
333
+ .nav:not([data-bg="plain"], [data-variant="transparent"]), .nav[data-bg="inverse"] {
334
+ --black: oklch(0% 0 0);
335
+ --white: oklch(100% 0 0);
405
336
 
406
- --shadow-opacity: 0.08;
407
- }
337
+ /* Dark: lifted to L 0.78 with the chroma capped, so a deep accent
338
+ reads as a dusty tint of itself, not salmon. */
339
+ --accent-ink: light-dark(
340
+ oklch(from var(--accent) min(l, 0.45) c h),
341
+ oklch(from var(--accent) max(l, 0.78) min(c, 0.08) h)
342
+ );
343
+
344
+ --color-text: light-dark(
345
+ oklch(from var(--neutral-tint) 0.17 c h),
346
+ oklch(from var(--neutral-tint) 0.93 c h)
347
+ );
348
+ --color-text-muted: light-dark(
349
+ oklch(from var(--neutral-tint) 0.44 c h),
350
+ oklch(from var(--neutral-tint) 0.74 c h)
351
+ );
352
+ --color-text-subtle: light-dark(
353
+ oklch(from var(--neutral-tint) 0.48 c h),
354
+ oklch(from var(--neutral-tint) 0.68 c h)
355
+ );
356
+
357
+ --color-background: light-dark(
358
+ oklch(from var(--neutral-tint) 0.99 calc(c * 0.75) h),
359
+ oklch(from var(--neutral-tint) 0.145 calc(c * 1.5) h)
360
+ );
361
+ --color-surface: light-dark(
362
+ oklch(from var(--neutral-tint) 0.955 c h),
363
+ oklch(from var(--neutral-tint) 0.19 calc(c * 1.5) h)
364
+ );
365
+ --color-surface-raised: light-dark(
366
+ oklch(from var(--neutral-tint) 1 calc(c * 0.4) h),
367
+ oklch(from var(--neutral-tint) 0.22 calc(c * 1.5) h)
368
+ );
369
+
370
+ /* --color-border is decoration (hairlines); --color-border-strong is ink:
371
+ rules that mean something, and field edges. */
372
+ --color-border: light-dark(
373
+ oklch(from var(--neutral-tint) 0.86 c h),
374
+ oklch(from var(--neutral-tint) 0.30 calc(c * 1.75) h)
375
+ );
376
+ --color-border-subtle: light-dark(
377
+ oklch(from var(--neutral-tint) 0.92 calc(c * 0.75) h),
378
+ oklch(from var(--neutral-tint) 0.22 calc(c * 1.5) h)
379
+ );
380
+ --color-border-strong: light-dark(
381
+ oklch(from var(--neutral-tint) 0.17 c h),
382
+ oklch(from var(--neutral-tint) 0.93 c h)
383
+ );
384
+
385
+ /* A warm wash, not a pink: 7% (oklab: an oklch mix takes the
386
+ near-neutral hue path; blue + warm paper gave olive). */
387
+ --accent-subtle: color-mix(in oklab, var(--accent) 7%, var(--color-background));
388
+ /* Hover moves the fill away from its text: darker under white text,
389
+ lighter under dark text. */
390
+ --accent-hover: oklch(from var(--accent) calc(l - 0.06 + 0.12 * clamp(0, (l - 0.575) * 1000, 1)) c h);
391
+
392
+ /* The fill of a primary action (button, nav CTA). Light: the accent.
393
+ Dark: only an accent darker than L 0.5 is lifted, to 0.5, so it
394
+ stands off the near-black page and stays deep (0.62 turned oxblood
395
+ salmon and moved every brand at 0.45-0.6). Its text is --on-accent
396
+ in both schemes: white clears 4.5:1 on any hue at L 0.5, and an
397
+ accent the lift leaves alone keeps the text its theme chose. */
398
+ --accent-fill: light-dark(var(--accent), oklch(from var(--accent) max(l, 0.5) c h));
399
+ --on-accent-fill: var(--on-accent);
400
+ --accent-fill-hover: oklch(from var(--accent-fill) calc(l - 0.06 + 0.12 * clamp(0, (l - 0.575) * 1000, 1)) c h);
401
+
402
+ --color-focus: var(--accent-ink);
403
+ --color-selection: var(--accent-subtle);
408
404
 
405
+ /* Feedback. The plain colour is for text and icons (it clears 4.5:1 on
406
+ the page); -fill is a solid background, -on its text; -subtle a tint. */
407
+ --color-success: light-dark(oklch(45% 0.13 145), oklch(78% 0.14 145));
408
+ --color-warning: light-dark(oklch(48% 0.11 70), oklch(82% 0.13 80));
409
+ --color-error: light-dark(oklch(48% 0.17 25), oklch(76% 0.13 25));
410
+ --color-info: light-dark(oklch(46% 0.12 240), oklch(78% 0.10 240));
409
411
 
410
- /* ========================================
411
- PRESET-SPECIFIC DARK MODE
412
- Each preset uses deep colors from its palette
413
- instead of generic black/gray.
414
- "Dark" = majority deep color, not black.
415
- ======================================== */
412
+ --color-success-fill: oklch(48% 0.13 145);
413
+ --color-warning-fill: oklch(80% 0.15 80);
414
+ --color-error-fill: oklch(52% 0.19 25);
415
+ --color-info-fill: oklch(50% 0.12 240);
416
416
 
417
- /* --- STILL: Deep cool slate ---
418
- Mono palette, hue 250. Quiet, disciplined dark. */
419
- [data-motion="still"][data-theme="dark"],
420
- [data-theme="dark"] [data-motion="still"],
421
- [data-theme="dark"]:root[data-motion="still"] {
422
- --color-background: oklch(12% 0.015 250);
423
- --color-surface: oklch(16% 0.015 250);
424
- --color-surface-raised: oklch(20% 0.015 250);
425
- --color-text: oklch(88% 0.01 250);
426
- --color-text-muted: oklch(65% 0.01 250);
427
- --color-text-subtle: oklch(50% 0.01 250);
428
- --color-border: oklch(25% 0.02 250);
429
- --color-border-subtle: oklch(20% 0.015 250);
430
- }
417
+ --color-success-on: var(--white);
418
+ --color-warning-on: oklch(20% 0.03 70);
419
+ --color-error-on: var(--white);
420
+ --color-info-on: var(--white);
431
421
 
432
- /* --- SERENE: Warm deep taupe ---
433
- Morandi palette, hue 60. Like a dimly lit gallery. */
434
- [data-motion="serene"][data-theme="dark"],
435
- [data-theme="dark"] [data-motion="serene"],
436
- [data-theme="dark"]:root[data-motion="serene"] {
437
- --color-background: oklch(13% 0.015 60);
438
- --color-surface: oklch(17% 0.02 60);
439
- --color-surface-raised: oklch(21% 0.02 60);
440
- --color-text: oklch(88% 0.01 60);
441
- --color-text-muted: oklch(65% 0.01 60);
442
- --color-text-subtle: oklch(50% 0.01 60);
443
- --color-border: oklch(25% 0.015 60);
444
- --color-border-subtle: oklch(20% 0.015 60);
445
- }
422
+ --color-success-subtle: light-dark(oklch(95% 0.03 145), oklch(24% 0.05 145));
423
+ --color-warning-subtle: light-dark(oklch(95% 0.04 70), oklch(24% 0.05 70));
424
+ --color-error-subtle: light-dark(oklch(95% 0.03 25), oklch(24% 0.05 25));
425
+ --color-info-subtle: light-dark(oklch(95% 0.02 240), oklch(24% 0.04 240));
446
426
 
447
- /* --- STREAM: Deep teal-gray ---
448
- Subtle palette, hue 200. Modern, cool, techy. */
449
- [data-motion="stream"][data-theme="dark"],
450
- [data-theme="dark"] [data-motion="stream"],
451
- [data-theme="dark"]:root[data-motion="stream"] {
452
- --color-background: oklch(12% 0.02 200);
453
- --color-surface: oklch(16% 0.02 200);
454
- --color-surface-raised: oklch(20% 0.02 200);
455
- --color-text: oklch(90% 0.01 200);
456
- --color-text-muted: oklch(65% 0.01 200);
457
- --color-text-subtle: oklch(50% 0.01 200);
458
- --color-border: oklch(24% 0.02 200);
459
- --color-border-subtle: oklch(19% 0.02 200);
460
- }
427
+ --color-rating: oklch(75% 0.18 85); /* star glyphs; decorative, paired with text */
461
428
 
462
- /* --- FLOWING: Deep ocean blue (default) ---
463
- Fluid palette, hue 230. Like deep water at dusk. */
464
- [data-motion="flowing"][data-theme="dark"],
465
- [data-theme="dark"] [data-motion="flowing"],
466
- [data-theme="dark"]:root[data-motion="flowing"],
467
- [data-theme="dark"]:root:not([data-motion]) {
468
- --color-background: oklch(13% 0.025 230);
469
- --color-surface: oklch(17% 0.025 230);
470
- --color-surface-raised: oklch(21% 0.025 230);
471
- --color-text: oklch(92% 0.01 220);
472
- --color-text-muted: oklch(68% 0.01 220);
473
- --color-text-subtle: oklch(52% 0.01 220);
474
- --color-border: oklch(25% 0.03 230);
475
- --color-border-subtle: oklch(20% 0.025 230);
476
429
  }
477
430
 
478
- /* --- CASCADE: Deep lavender ---
479
- Pastel palette, hue 280. Warm purple twilight. */
480
- [data-motion="cascade"][data-theme="dark"],
481
- [data-theme="dark"] [data-motion="cascade"],
482
- [data-theme="dark"]:root[data-motion="cascade"] {
483
- --color-background: oklch(14% 0.02 280);
484
- --color-surface: oklch(18% 0.025 280);
485
- --color-surface-raised: oklch(22% 0.025 280);
486
- --color-text: oklch(90% 0.01 280);
487
- --color-text-muted: oklch(66% 0.01 280);
488
- --color-text-subtle: oklch(50% 0.01 280);
489
- --color-border: oklch(26% 0.02 280);
490
- --color-border-subtle: oklch(21% 0.02 280);
431
+ /* The dark bands: the scheme and the weight boost only. The paint and
432
+ the text colour are in the layouts layer (src/fluid.css, the band
433
+ rules); the hero and the footer paint their own. The page colour
434
+ follows the band's paint, so fields, rings and --accent-subtle (mixed
435
+ with --color-background) match the surface they sit on. */
436
+ :is(.section, section)[data-bg="inverse"],
437
+ .hero[data-theme="dark"],
438
+ .site-footer[data-dark] {
439
+ color-scheme: dark;
440
+ --_dark-weight: 30;
441
+ --color-background: var(--band-inverse);
491
442
  }
492
443
 
493
- /* --- RAPIDS: Deep warm ember ---
494
- Bold palette, hue 25. Smoldering, aggressive, warm. */
495
- [data-motion="rapids"][data-theme="dark"],
496
- [data-theme="dark"] [data-motion="rapids"],
497
- [data-theme="dark"]:root[data-motion="rapids"] {
498
- --color-background: oklch(11% 0.03 25);
499
- --color-surface: oklch(15% 0.03 25);
500
- --color-surface-raised: oklch(19% 0.03 25);
501
- --color-text: oklch(92% 0.01 25);
502
- --color-text-muted: oklch(67% 0.01 25);
503
- --color-text-subtle: oklch(50% 0.01 25);
504
- --color-border: oklch(24% 0.04 25);
505
- --color-border-subtle: oklch(19% 0.03 25);
506
- }
444
+ /* The OS decides unless the page says otherwise. */
445
+ :root[data-theme="light"] { color-scheme: light; }
446
+ :root[data-theme="dark"] { color-scheme: dark; --_dark-weight: 30; }
507
447
 
508
- /* --- TSUNAMI: Deepest navy abyss ---
509
- Tsunami palette, hue 250. Ocean trench depth. */
510
- [data-motion="tsunami"][data-theme="dark"],
511
- [data-theme="dark"] [data-motion="tsunami"],
512
- [data-theme="dark"]:root[data-motion="tsunami"] {
513
- --color-background: oklch(10% 0.04 250);
514
- --color-surface: oklch(14% 0.04 250);
515
- --color-surface-raised: oklch(18% 0.04 250);
516
- --color-text: oklch(90% 0.015 200);
517
- --color-text-muted: oklch(65% 0.015 200);
518
- --color-text-subtle: oklch(48% 0.01 200);
519
- --color-border: oklch(22% 0.05 240);
520
- --color-border-subtle: oklch(17% 0.04 240);
448
+ @media (prefers-color-scheme: dark) {
449
+ :root:not([data-theme="light"]) { --_dark-weight: 30; }
521
450
  }
522
451
 
523
-
524
- /* ========================================
525
- HIGH CONTRAST ADJUSTMENTS
526
- Respect user preferences
527
- ======================================== */
528
-
529
- @media (prefers-contrast: high) {
530
- :root {
531
- --color-border: var(--gray-50);
532
- --color-text-muted: var(--color-text);
533
- }
452
+ /* The nav is the page's other scheme by default, roles and all: a dark
453
+ bar on a light page, a light bar on a dark one (not the band paint,
454
+ which stays dark in dark mode). data-bg="plain" or a transparent bar
455
+ keeps the page's colours; data-bg="inverse" still forces it. */
456
+ .nav:not([data-bg="plain"], [data-variant="transparent"]), .nav[data-bg="inverse"] {
457
+ color-scheme: dark;
458
+ --_dark-weight: 30;
459
+ /* Plain text and icons in the bar follow its ink (nav.css). */
460
+ color: var(--nav-ink, var(--color-text));
461
+ /* A band's button inversion (accent-strong) is not the bar's. */
462
+ --_btn-color: initial;
463
+ --_btn-bg: initial;
464
+ --_btn-bg-hover: initial;
534
465
  }
466
+ :root[data-theme="dark"] :is(.nav:not([data-bg="plain"], [data-variant="transparent"]), .nav[data-bg="inverse"]) { color-scheme: light; --_dark-weight: 0; }
535
467
 
536
-
537
- /* ========================================
538
- REDUCED MOTION
539
- Respect user preferences
540
- ======================================== */
541
-
542
- @media (prefers-reduced-motion: reduce) {
543
- :root {
544
- --duration-instant: 0ms;
545
- --duration-fast: 0ms;
546
- --duration-normal: 0ms;
547
- --duration-slow: 0ms;
548
- --duration-slower: 0ms;
549
- --duration-slowest: 0ms;
550
- --motion-scale: 0;
468
+ @media (prefers-color-scheme: dark) {
469
+ :root:not([data-theme="light"]) :is(.nav:not([data-bg="plain"], [data-variant="transparent"]), .nav[data-bg="inverse"]) { color-scheme: light; --_dark-weight: 0; }
470
+ }
471
+
472
+ /* Legacy floor. The roles derive from the knobs with relative colour
473
+ (Safari 18, Firefox 128, Chrome 119). An older browser cannot compute
474
+ them and would lose text and surface colours, so it gets plain light
475
+ values with the default tint instead: readable, not themed. Dark bands
476
+ go light with no paint. */
477
+ @supports not (color: oklch(from red min(l, 0.5) c h)) {
478
+ :root,
479
+ :root[data-theme],
480
+ :is(.section, section)[data-bg="inverse"],
481
+ .hero[data-theme="dark"],
482
+ .site-footer[data-dark],
483
+ :root :is(.nav:not([data-bg="plain"], [data-variant="transparent"]), .nav[data-bg="inverse"]) {
484
+ color-scheme: light;
485
+ --color-text: oklch(17% 0.01 75);
486
+ --color-text-muted: oklch(44% 0.01 75);
487
+ --color-text-subtle: oklch(48% 0.01 75);
488
+ --color-background: oklch(99% 0.0075 75);
489
+ --color-surface: oklch(95.5% 0.01 75);
490
+ --color-surface-raised: oklch(100% 0.004 75);
491
+ --color-border: oklch(86% 0.01 75);
492
+ --color-border-subtle: oklch(92% 0.0075 75);
493
+ --color-border-strong: oklch(17% 0.01 75);
494
+ --accent-ink: color-mix(in oklch, var(--accent) 45%, var(--color-text));
495
+ --on-accent: var(--white); /* a site with a light accent sets --on-accent itself */
496
+ --accent-hover: color-mix(in oklch, var(--accent) 85%, var(--black));
497
+ --accent-fill: var(--accent);
498
+ --on-accent-fill: var(--on-accent);
499
+ --accent-fill-hover: var(--accent-hover);
500
+ --_dark-weight: 0;
501
+ }
502
+
503
+ :is(.section, section)[data-bg="inverse"],
504
+ .hero[data-theme="dark"],
505
+ .site-footer[data-dark] {
506
+ --band-inverse: var(--color-background);
507
+ background: none;
551
508
  }
552
509
  }
553
510
 
554
511
 
555
512
  /* ========================================
556
- MOTION INTENSITY PRESETS - Water Themes
557
- Apply to root: <html data-motion="flowing">
558
-
559
- Each level defines:
560
- - Timing (durations)
561
- - Easing (curves)
562
- - Direction (page transitions)
563
- - Character (how elements move)
564
- ======================================== */
565
-
566
- /* === STILL: Instant, no animation ===
567
- Like perfectly still water - no movement at all.
568
- For users who prefer no motion or maximum performance.
569
- Layout: Swiss design, structured, grid-based, minimal decoration.
570
- Character: Precise, no-nonsense, Müller-Brockmann. */
571
- :root[data-motion="still"],
572
- [data-motion="still"] {
573
- --motion-scale: 0;
574
- --duration-instant: 0ms;
575
- --duration-fast: 0ms;
576
- --duration-normal: 0ms;
577
- --duration-slow: 0ms;
578
- --duration-slower: 0ms;
579
- --duration-slowest: 0ms;
580
- --ease-fluid: linear;
581
- --ease-settle: linear;
582
-
583
- /* Typography: tight, precise, functional */
584
- --leading-normal: 1.5;
585
- --leading-tight: 1.15;
586
- --tracking-normal: 0;
587
- --tracking-wide: 0.05em;
588
-
589
- /* Layout: Swiss design, precise, grid-focused, compact */
590
- --space-section: var(--space-12);
591
- --space-gutter: var(--space-5);
592
- --container-content: var(--container-lg);
593
-
594
- /* Sharp corners — zero radius across the board */
595
- --radius-sm: 0;
596
- --radius-md: 2px;
597
- --radius-lg: 2px;
598
- --radius-xl: 4px;
599
- --radius-2xl: 4px;
600
- --radius-card: 0;
601
- --radius-button: 0;
602
- --radius-input: 2px;
603
-
604
- /* Flat, crisp — no soft shadows, only sharp lines */
605
- --shadow-xs: none;
606
- --shadow-sm: none;
607
- --shadow-md: 0 1px 2px oklch(from var(--shadow-color) l c h / 0.1);
608
- --shadow-lg: 0 2px 4px oklch(from var(--shadow-color) l c h / 0.1);
609
- --shadow-xl: 0 3px 6px oklch(from var(--shadow-color) l c h / 0.12);
610
-
611
- /* Thicker borders — structure through lines, not shadows */
612
- --border-width: 1.5px;
613
- --border-width-thick: 3px;
614
- }
615
-
616
- /* === SERENE: Ken Burns effect ===
617
- Like gazing at a reflection in still water.
618
- Very slow crossfades, gentle zoom on images.
619
- Meditative, contemplative motion.
620
- Layout: Generous whitespace, classical proportions, soft edges.
621
- Character: Classical, literary, spa-like, timeless. */
622
- :root[data-motion="serene"],
623
- [data-motion="serene"] {
624
- --motion-scale: 0.3;
625
- --duration-instant: 200ms;
626
- --duration-fast: 600ms;
627
- --duration-normal: 1000ms;
628
- --duration-slow: 1500ms;
629
- --duration-slower: 2000ms;
630
- --duration-slowest: 3000ms;
631
- --ease-fluid: cubic-bezier(0.25, 0.1, 0.25, 1); /* Very gentle */
632
- --ease-settle: cubic-bezier(0.25, 0.1, 0.25, 1);
633
- /* Ken Burns zoom amount for images */
634
- --kenburns-scale: 1.05;
635
-
636
- /* Typography: generous, literary, breathable */
637
- --leading-normal: 1.8;
638
- --leading-relaxed: 2;
639
- --tracking-normal: 0.01em;
640
- --tracking-wide: 0.08em;
641
-
642
- /* Layout: Editorial, soft rounded corners, NOT full width */
643
- --space-section: var(--space-24);
644
- --space-gutter: var(--space-10);
645
- --container-prose: 38rem;
646
- --container-content: var(--container-md); /* Narrower content */
647
- --container-xl: var(--container-lg); /* Constrain max width */
648
-
649
- /* Soft, pillowy corners — cloud-like */
650
- --radius-sm: 8px;
651
- --radius-md: 16px;
652
- --radius-lg: 24px;
653
- --radius-xl: 32px;
654
- --radius-2xl: 40px;
655
- --radius-card: 24px;
656
- --radius-button: 9999px; /* Pill buttons */
657
- --radius-input: 16px;
658
-
659
- /* Whisper-soft diffuse shadows — barely visible depth */
660
- --shadow-xs: 0 2px 8px oklch(from var(--shadow-color) l c h / 0.02);
661
- --shadow-sm: 0 4px 12px oklch(from var(--shadow-color) l c h / 0.03);
662
- --shadow-md: 0 8px 24px oklch(from var(--shadow-color) l c h / 0.05);
663
- --shadow-lg: 0 16px 48px oklch(from var(--shadow-color) l c h / 0.06);
664
- --shadow-xl: 0 24px 64px oklch(from var(--shadow-color) l c h / 0.07);
665
-
666
- /* Thin, delicate borders */
667
- --border-width: 1px;
668
- --border-width-thick: 1.5px;
669
- }
670
-
671
- /* === TRICKLING: Minimal, quick motion ===
672
- Like a small brook - slight movement, nothing dramatic.
673
- Quick, subtle feedback for interactions.
674
- Layout: Compact, efficient, subtle styling.
675
- Character: Understated, utility-first, developer-friendly. */
676
- :root[data-motion="trickling"],
677
- [data-motion="trickling"] {
678
- --motion-scale: 0.5;
679
- --duration-fast: 100ms;
680
- --duration-normal: 175ms;
681
- --duration-slow: 250ms;
682
- --ease-fluid: cubic-bezier(0.4, 0, 0.2, 1);
683
-
684
- /* Typography: compact, efficient */
685
- --leading-normal: 1.55;
686
- --tracking-normal: 0;
687
-
688
- /* Layout: Understated, efficient use of space */
689
- --space-section: var(--space-12);
690
- --space-gutter: var(--space-5);
691
- --container-content: var(--container-lg);
692
-
693
- /* Small, subtle radius — functional */
694
- --radius-sm: 3px;
695
- --radius-md: 6px;
696
- --radius-lg: 8px;
697
- --radius-xl: 12px;
698
- --radius-2xl: 16px;
699
- --radius-card: 8px;
700
- --radius-button: 6px;
701
- --radius-input: 6px;
702
-
703
- /* Minimal shadows — only when needed */
704
- --shadow-xs: 0 1px 2px oklch(from var(--shadow-color) l c h / 0.04);
705
- --shadow-sm: 0 1px 3px oklch(from var(--shadow-color) l c h / 0.06);
706
- --shadow-md: 0 2px 6px oklch(from var(--shadow-color) l c h / 0.06);
707
- --shadow-lg: 0 4px 12px oklch(from var(--shadow-color) l c h / 0.08);
708
- }
709
-
710
- /* === STREAM: Horizontal flow ===
711
- Like a gentle stream - content flows sideways.
712
- Modern, tech-forward feel.
713
- Layout: Clean lines, horizontal emphasis, full-width.
714
- Character: Open, approachable, tech-forward, SaaS-like. */
715
- :root[data-motion="stream"],
716
- [data-motion="stream"] {
717
- --motion-scale: 0.7;
718
- --duration-fast: 150ms;
719
- --duration-normal: 300ms;
720
- --duration-slow: 450ms;
721
- --ease-fluid: cubic-bezier(0.22, 1, 0.36, 1);
722
- --ease-settle: cubic-bezier(0.22, 1, 0.36, 1);
723
-
724
- /* Typography: open, readable, generous */
725
- --leading-normal: 1.6;
726
- --tracking-normal: 0.01em;
727
-
728
- /* Layout: Modern, wide, clean horizontal lines */
729
- --space-section: var(--space-16);
730
- --space-gutter: var(--space-6);
731
- --container-content: var(--container-xl);
732
-
733
- /* Medium, consistent radius — modern SaaS feel */
734
- --radius-sm: 4px;
735
- --radius-md: 8px;
736
- --radius-lg: 12px;
737
- --radius-xl: 16px;
738
- --radius-2xl: 20px;
739
- --radius-card: 12px;
740
- --radius-button: 8px;
741
- --radius-input: 8px;
742
-
743
- /* Subtle, diffuse shadows — glass-like depth */
744
- --shadow-sm: 0 1px 3px oklch(from var(--shadow-color) l c h / 0.03);
745
- --shadow-md:
746
- 0 2px 6px oklch(from var(--shadow-color) l c h / 0.04),
747
- 0 4px 16px oklch(from var(--shadow-color) l c h / 0.04);
748
- --shadow-lg:
749
- 0 4px 12px oklch(from var(--shadow-color) l c h / 0.04),
750
- 0 12px 36px oklch(from var(--shadow-color) l c h / 0.06);
751
- }
752
-
753
- /* === FLOWING: Connected horizontal flow (default) ===
754
- Like a gentle stream - content flows left to right.
755
- Old content pushes out as new content slides in.
756
- The default, balanced motion style.
757
- Layout: Balanced, organic, versatile.
758
- Character: Friendly, professional, the "just right" preset. */
759
- :root[data-motion="flowing"],
760
- [data-motion="flowing"],
761
- :root:not([data-motion]) {
762
- --motion-scale: 1;
763
- --ease-fluid: cubic-bezier(0.33, 0, 0.2, 1);
764
- --ease-settle: cubic-bezier(0.22, 1, 0.36, 1);
765
-
766
- /* Typography: comfortable, readable */
767
- --leading-normal: 1.65;
768
- --tracking-normal: 0;
769
-
770
- /* Layout: Balanced, roomy but not extravagant */
771
- --space-section: var(--space-16);
772
- --space-gutter: var(--space-6);
773
- --container-content: var(--container-xl);
774
-
775
- /* Organic rounded corners */
776
- --radius-sm: 6px;
777
- --radius-md: 10px;
778
- --radius-lg: 14px;
779
- --radius-xl: 18px;
780
- --radius-2xl: 24px;
781
- --radius-card: 14px;
782
- --radius-button: 10px;
783
- --radius-input: 10px;
784
-
785
- /* Layered, organic shadows */
786
- --shadow-sm:
787
- 0 2px 4px oklch(from var(--shadow-color) l c h / 0.04),
788
- 0 4px 8px oklch(from var(--shadow-color) l c h / 0.04);
789
- --shadow-md:
790
- 0 4px 8px oklch(from var(--shadow-color) l c h / 0.04),
791
- 0 8px 16px oklch(from var(--shadow-color) l c h / 0.06);
792
- --shadow-lg:
793
- 0 8px 16px oklch(from var(--shadow-color) l c h / 0.04),
794
- 0 16px 32px oklch(from var(--shadow-color) l c h / 0.08);
795
- }
796
-
797
- /* === CASCADE: Waterfall effect ===
798
- Like a waterfall - content cascades down in layers.
799
- Playful, friendly, staggered animations.
800
- Layout: Rounded, warm, approachable, bubbly.
801
- Character: Rounded, warm, Notion/Slack vibes. */
802
- :root[data-motion="cascade"],
803
- [data-motion="cascade"] {
804
- --motion-scale: 1.2;
805
- --duration-fast: 200ms;
806
- --duration-normal: 350ms;
807
- --duration-slow: 500ms;
808
- --ease-fluid: cubic-bezier(0.34, 1.56, 0.64, 1); /* Slight bounce */
809
- --ease-settle: cubic-bezier(0.22, 1.2, 0.36, 1);
810
-
811
- /* Typography: round, friendly, slightly loose */
812
- --leading-normal: 1.7;
813
- --tracking-normal: 0.01em;
814
-
815
- /* Layout: Friendly, bubbly, generous gaps */
816
- --space-section: var(--space-16);
817
- --space-gutter: var(--space-6);
818
- --container-content: var(--container-xl);
819
-
820
- /* Maximum roundness — bubble shapes */
821
- --radius-sm: 8px;
822
- --radius-md: 16px;
823
- --radius-lg: 24px;
824
- --radius-xl: 32px;
825
- --radius-2xl: 48px;
826
- --radius-card: 20px;
827
- --radius-button: 9999px; /* Pill buttons */
828
- --radius-input: 12px;
829
-
830
- /* Soft, colorful shadows — warm and friendly */
831
- --shadow-xs: 0 2px 6px oklch(from var(--shadow-color) l c h / 0.05);
832
- --shadow-sm: 0 3px 8px oklch(from var(--shadow-color) l c h / 0.06);
833
- --shadow-md: 0 4px 12px oklch(from var(--shadow-color) l c h / 0.08);
834
- --shadow-lg: 0 8px 24px oklch(from var(--shadow-color) l c h / 0.12);
835
- --shadow-xl: 0 16px 40px oklch(from var(--shadow-color) l c h / 0.14);
836
- }
837
-
838
- /* === RAPIDS: Vertical spring effect ===
839
- Like rushing water - new content rushes in from top,
840
- accelerates, then pulls the old content away.
841
- Energetic, bouncy, with overshoot.
842
- Layout: Compact, bold, athletic.
843
- Character: Athletic, bold, Stripe/Vercel energy. */
844
- :root[data-motion="rapids"],
845
- [data-motion="rapids"] {
846
- --motion-scale: 1.5;
847
- --duration-fast: 250ms;
848
- --duration-normal: 450ms;
849
- --duration-slow: 650ms;
850
- --ease-fluid: cubic-bezier(0.34, 1.56, 0.64, 1); /* Bouncy overshoot */
851
- --ease-settle: cubic-bezier(0.22, 1.4, 0.36, 1); /* Spring settle */
852
- /* Stagger delay for layered effect */
853
- --rapids-stagger: 80ms;
854
-
855
- /* Typography: tight, condensed, punchy */
856
- --leading-normal: 1.45;
857
- --leading-tight: 1.1;
858
- --tracking-normal: -0.01em;
859
- --tracking-wide: 0.08em;
860
-
861
- /* Layout: Tight, punchy, high-energy, full-width */
862
- --space-section: var(--space-12);
863
- --space-gutter: var(--space-4);
864
- --container-content: var(--container-2xl);
865
-
866
- /* Small, functional radius */
867
- --radius-sm: 4px;
868
- --radius-md: 8px;
869
- --radius-lg: 12px;
870
- --radius-xl: 16px;
871
- --radius-2xl: 20px;
872
- --radius-card: 12px;
873
- --radius-button: 8px;
874
- --radius-input: 6px;
875
-
876
- /* Hard, high-contrast shadows — strong depth */
877
- --shadow-xs: 0 1px 2px oklch(from var(--shadow-color) l c h / 0.1);
878
- --shadow-sm: 0 2px 4px oklch(from var(--shadow-color) l c h / 0.12);
879
- --shadow-md: 0 4px 8px oklch(from var(--shadow-color) l c h / 0.15);
880
- --shadow-lg: 0 8px 16px oklch(from var(--shadow-color) l c h / 0.2);
881
- --shadow-xl: 0 16px 32px oklch(from var(--shadow-color) l c h / 0.25);
882
-
883
- /* Bold borders */
884
- --border-width: 2px;
885
- --border-width-thick: 3px;
886
- }
887
-
888
- /* === TSUNAMI: Diagonal wave crash ===
889
- Maximum impact - content crashes in diagonally
890
- like a wave breaking. Strong overshoot, dramatic.
891
- For bold, attention-grabbing interfaces.
892
- Layout: Bold, edge-to-edge, asymmetric corners.
893
- Character: Imposing, cinematic, Hokusai wave. */
894
- :root[data-motion="tsunami"],
895
- [data-motion="tsunami"] {
896
- --motion-scale: 2;
897
- --duration-fast: 350ms;
898
- --duration-normal: 600ms;
899
- --duration-slow: 900ms;
900
- --ease-fluid: cubic-bezier(0.68, -0.55, 0.265, 1.55); /* Strong overshoot */
901
- --ease-settle: cubic-bezier(0.34, 1.8, 0.64, 1); /* Dramatic settle */
902
-
903
- /* Typography: bold, wide-set, commanding */
904
- --leading-normal: 1.5;
905
- --leading-tight: 1.05;
906
- --tracking-normal: 0;
907
- --tracking-wide: 0.15em;
908
-
909
- /* Layout: Bold, edge-to-edge, maximum impact */
910
- --space-section: var(--space-20);
911
- --space-gutter: var(--space-6);
912
- --container-content: 100%; /* Full width */
913
- --container-xl: 100%;
914
-
915
- /* Asymmetric corners: ONE rounded, THREE sharp */
916
- --radius-sm: 0;
917
- --radius-md: 0;
918
- --radius-lg: 0;
919
- --radius-xl: 0;
920
- --radius-2xl: 0;
921
- --radius-card: 0;
922
- --radius-button: 0;
923
- --radius-input: 0;
924
- /* Special asymmetric radius for cards/containers */
925
- --radius-asymmetric: 0 24px 0 0; /* Only top-right rounded */
926
- --radius-asymmetric-alt: 24px 0 0 0; /* Only top-left rounded */
927
- --radius-asymmetric-lg: 0 48px 0 0;
928
-
929
- /* Dramatic, heavy shadows — cinematic depth */
930
- --shadow-xs: 0 2px 4px oklch(from var(--shadow-color) l c h / 0.1);
931
- --shadow-sm: 0 4px 8px oklch(from var(--shadow-color) l c h / 0.12);
932
- --shadow-md: 0 8px 24px oklch(from var(--shadow-color) l c h / 0.2);
933
- --shadow-lg: 0 16px 48px oklch(from var(--shadow-color) l c h / 0.25);
934
- --shadow-xl: 0 24px 64px oklch(from var(--shadow-color) l c h / 0.3);
935
-
936
- /* Heavy borders — structural, bold */
937
- --border-width: 2px;
938
- --border-width-thick: 4px;
939
- }
940
-
941
-
942
- /* ========================================
943
- PAGE TRANSITIONS - View Transitions API
944
- Each motion style has its own transition
513
+ USER PREFERENCES
945
514
  ======================================== */
946
515
 
947
- /* Enable cross-document view transitions */
948
- @view-transition {
949
- navigation: auto;
950
- }
951
-
952
- /* --- Keyframes library --- */
953
-
954
- /* Fade */
955
- @keyframes fade-out {
956
- to { opacity: 0; }
957
- }
958
-
959
- @keyframes fade-in {
960
- from { opacity: 0; }
961
- }
962
-
963
- /* Horizontal slide (Flowing) - Smooth L→R with organic ease */
964
- @keyframes flowing-out {
965
- 0% {
966
- transform: translateX(0);
967
- opacity: 1;
968
- }
969
- /* Gentle start */
970
- 30% {
971
- transform: translateX(15%);
972
- opacity: 0.9;
973
- }
974
- /* Peak momentum */
975
- 60% {
976
- transform: translateX(50%);
977
- opacity: 0.6;
978
- }
979
- /* Slow, organic finish */
980
- 100% {
981
- transform: translateX(100%);
982
- opacity: 0;
983
- }
984
- }
985
-
986
- @keyframes flowing-in {
987
- 0% {
988
- transform: translateX(-100%);
989
- opacity: 0;
990
- }
991
- /* Gentle start from left */
992
- 30% {
993
- transform: translateX(-50%);
994
- opacity: 0.4;
995
- }
996
- /* Peak momentum */
997
- 60% {
998
- transform: translateX(-15%);
999
- opacity: 0.8;
1000
- }
1001
- /* Slow, organic settle - ease out gently */
1002
- 85% {
1003
- transform: translateX(-3%);
1004
- opacity: 0.95;
1005
- }
1006
- 100% {
1007
- transform: translateX(0);
1008
- opacity: 1;
1009
- }
1010
- }
1011
-
1012
- /* Rapids: Content rushes toward you (zoom out), new loads behind */
1013
- @keyframes rapids-old {
1014
- 0% {
1015
- transform: scale(1);
1016
- opacity: 1;
1017
- }
1018
- /* Begins to rush toward viewer */
1019
- 30% {
1020
- transform: scale(1.3);
1021
- opacity: 0.9;
1022
- }
1023
- /* Rushes past - expanding rapidly */
1024
- 60% {
1025
- transform: scale(2);
1026
- opacity: 0.5;
1027
- }
1028
- /* Exits view quickly */
1029
- 100% {
1030
- transform: scale(3);
1031
- opacity: 0;
1032
- }
1033
- }
1034
-
1035
- @keyframes rapids-new {
1036
- 0% {
1037
- /* Starts behind, smaller */
1038
- transform: scale(0.5);
1039
- opacity: 0;
1040
- }
1041
- /* Quickly becomes visible */
1042
- 20% {
1043
- transform: scale(0.7);
1044
- opacity: 0.5;
1045
- }
1046
- /* Rushes into place */
1047
- 60% {
1048
- transform: scale(0.95);
1049
- opacity: 0.9;
1050
- }
1051
- /* Final position - direct */
1052
- 100% {
1053
- transform: scale(1);
1054
- opacity: 1;
1055
- }
1056
- }
1057
-
1058
- /* Tsunami: Wave arc - comes from left, rises up 10%, then sweeps down at angle */
1059
- @keyframes wave-out {
1060
- 0% {
1061
- transform: translate(0, 0) scale(1);
1062
- opacity: 1;
1063
- }
1064
- /* Rise up - wave building */
1065
- 25% {
1066
- transform: translate(5%, -10%) scale(1.02);
1067
- opacity: 1;
1068
- }
1069
- /* Peak of wave arc */
1070
- 45% {
1071
- transform: translate(15%, -12%) rotate(-2deg) scale(1.03);
1072
- opacity: 0.9;
1073
- }
1074
- /* Sweeping down and away at angle */
1075
- 70% {
1076
- transform: translate(40%, 15%) rotate(-5deg) scale(0.95);
1077
- opacity: 0.5;
1078
- }
1079
- 100% {
1080
- /* Exit at angle */
1081
- transform: translate(80%, 40%) rotate(-8deg) scale(0.85);
1082
- opacity: 0;
1083
- }
1084
- }
1085
-
1086
- @keyframes wave-in {
1087
- 0% {
1088
- /* Starting from left, below horizon */
1089
- transform: translate(-100%, 20%) scale(0.9);
1090
- opacity: 0;
1091
- }
1092
- /* Rising up as it comes in */
1093
- 30% {
1094
- transform: translate(-40%, -8%) scale(0.98);
1095
- opacity: 0.6;
1096
- }
1097
- /* Cresting - peak of wave */
1098
- 50% {
1099
- transform: translate(-10%, -12%) scale(1.02);
1100
- opacity: 0.9;
1101
- }
1102
- /* Coming down from peak */
1103
- 70% {
1104
- transform: translate(5%, -5%) scale(1.01);
1105
- opacity: 1;
1106
- }
1107
- /* Settling with slight overshoot */
1108
- 85% {
1109
- transform: translate(-2%, 2%) scale(1);
1110
- opacity: 1;
1111
- }
1112
- 100% {
1113
- transform: translate(0, 0) scale(1);
1114
- opacity: 1;
1115
- }
1116
- }
1117
-
1118
- /* Ken Burns zoom (Serene) */
1119
- @keyframes kenburns-out {
1120
- from { transform: scale(1); opacity: 1; }
1121
- to { transform: scale(var(--kenburns-scale, 1.05)); opacity: 0; }
1122
- }
1123
-
1124
- @keyframes kenburns-in {
1125
- from { transform: scale(var(--kenburns-scale, 1.05)); opacity: 0; }
1126
- to { transform: scale(1); opacity: 1; }
1127
- }
1128
-
1129
-
1130
- /* --- Default transition (Flowing) --- */
1131
- /* Smooth left-to-right with organic ease-in-out */
1132
- ::view-transition-old(root) {
1133
- animation: var(--duration-slower) cubic-bezier(0.4, 0, 0.2, 1) both flowing-out;
1134
- }
1135
-
1136
- ::view-transition-new(root) {
1137
- animation: var(--duration-slower) cubic-bezier(0.4, 0, 0.2, 1) both flowing-in;
1138
- }
1139
-
1140
-
1141
- /* --- Still: Instant cut --- */
1142
- :root[data-motion="still"] {
1143
- &::view-transition-old(root),
1144
- &::view-transition-new(root) {
1145
- animation: none;
1146
- }
1147
- }
1148
-
1149
-
1150
- /* --- Serene: Slow crossfade (Ken Burns) --- */
1151
- :root[data-motion="serene"] {
1152
- &::view-transition-old(root) {
1153
- animation: var(--duration-slowest) var(--ease-fluid) both kenburns-out;
1154
- }
1155
-
1156
- &::view-transition-new(root) {
1157
- animation: var(--duration-slowest) var(--ease-fluid) both kenburns-in;
1158
- }
1159
- }
1160
-
1161
-
1162
- /* --- Trickling: Quick subtle fade --- */
1163
- :root[data-motion="trickling"] {
1164
- &::view-transition-old(root) {
1165
- animation: var(--duration-slow) var(--ease-fluid) both fade-out;
1166
- }
1167
-
1168
- &::view-transition-new(root) {
1169
- animation: var(--duration-slow) var(--ease-fluid) both fade-in;
1170
- }
1171
- }
1172
-
1173
-
1174
- /* --- Rapids: Fluid dynamics effect ---
1175
- New content pushes up from bottom, overlaps old briefly,
1176
- friction slows it, then it gains traction and pulls old content away */
1177
- :root[data-motion="rapids"] {
1178
- &::view-transition-old(root) {
1179
- animation: var(--duration-slowest) cubic-bezier(0.4, 0, 0.6, 1) both rapids-old;
1180
- }
1181
-
1182
- &::view-transition-new(root) {
1183
- animation: var(--duration-slowest) cubic-bezier(0.2, 0, 0.4, 1) both rapids-new;
1184
- }
1185
- }
1186
-
1187
-
1188
- /* --- Tsunami: Diagonal wave crash --- */
1189
- :root[data-motion="tsunami"] {
1190
- &::view-transition-old(root) {
1191
- animation: var(--duration-slower) var(--ease-settle) both wave-out;
1192
- }
1193
-
1194
- &::view-transition-new(root) {
1195
- animation: var(--duration-slower) var(--ease-fluid) both wave-in;
516
+ @media (prefers-contrast: more) {
517
+ :root,
518
+ :is(.section, section)[data-bg="inverse"],
519
+ .hero[data-theme="dark"],
520
+ .site-footer[data-dark],
521
+ .nav:not([data-bg="plain"], [data-variant="transparent"]), .nav[data-bg="inverse"] {
522
+ --color-border: var(--color-border-strong);
523
+ --color-text-muted: var(--color-text);
524
+ --color-text-subtle: var(--color-text);
1196
525
  }
1197
526
  }
1198
527
 
1199
-
1200
528
  /* ========================================
1201
- ADDITIONAL PRESETS
1202
- Same HTML, completely different visual identity.
529
+ MOTION
530
+ Every duration derives from --motion-base (fast ×0.73, slow ×1.37),
531
+ times a speed (--_motion) set per page or section by data-motion:
532
+ <html data-motion="slow"> or <section data-motion="fast">.
533
+ reduced 0 (all durations 0ms)
534
+ slow 1.5× (deliberate)
535
+ fast 0.5× (snappy)
536
+ Timing only; prefers-reduced-motion always wins. The durations are
537
+ declared again on each [data-motion] element: a custom property
538
+ inherits its resolved value, so a speed set lower down needs them
539
+ recomputed where it is set.
1203
540
  ======================================== */
1204
541
 
1205
- /* === BRUTALIST: Raw, uncompromising, technical ===
1206
- Monospace typography, zero decoration, grid-locked.
1207
- Inspired by Swiss design and brutalist architecture.
1208
- For: Developer portfolios, tech products, manifestos */
1209
- :root[data-motion="brutalist"],
1210
- [data-motion="brutalist"] {
1211
- /* No motion - instant, mechanical */
1212
- --motion-scale: 0;
1213
- --duration-instant: 0ms;
1214
- --duration-fast: 0ms;
1215
- --duration-normal: 0ms;
1216
- --duration-slow: 0ms;
1217
- --duration-slower: 0ms;
1218
- --duration-slowest: 0ms;
1219
- --ease-fluid: linear;
1220
- --ease-settle: linear;
1221
-
1222
- /* Typography: Space Mono — geometric, raw, industrial */
1223
- --font-body: 'Space Mono', var(--font-mono);
1224
- --font-heading: 'Space Mono', var(--font-mono);
1225
- --font-ui: 'Space Mono', var(--font-mono);
1226
- --leading-normal: 1.5;
1227
- --leading-tight: 1.3;
1228
- --tracking-normal: -0.02em;
1229
-
1230
- /* Layout: Narrow, focused, dense */
1231
- --space-section: var(--space-8);
1232
- --space-gutter: var(--space-3);
1233
- --container-content: 600px;
1234
- --container-prose: 55ch;
1235
-
1236
- /* Zero radius - sharp edges only */
1237
- --radius-none: 0;
1238
- --radius-sm: 0;
1239
- --radius-md: 0;
1240
- --radius-lg: 0;
1241
- --radius-xl: 0;
1242
- --radius-2xl: 0;
1243
- --radius-full: 0;
1244
- --radius-card: 0;
1245
- --radius-button: 0;
1246
- --radius-input: 0;
1247
-
1248
- /* High-contrast borders, no soft shadows */
1249
- --border-width: 2px;
1250
- --border-width-thick: 4px;
1251
- --shadow-xs: none;
1252
- --shadow-sm: none;
1253
- --shadow-md: 4px 4px 0 var(--color-border);
1254
- --shadow-lg: 8px 8px 0 var(--color-border);
1255
- --shadow-xl: 12px 12px 0 var(--color-border);
1256
-
1257
- /* High contrast colors */
1258
- --color-text: var(--black);
1259
- --color-background: var(--white);
1260
- --color-border: var(--black);
1261
- --color-border-subtle: var(--gray-30);
1262
- --accent: var(--black);
1263
- }
1264
-
1265
- /* Brutalist dark mode */
1266
- [data-motion="brutalist"][data-theme="dark"],
1267
- [data-theme="dark"] [data-motion="brutalist"] {
1268
- --color-text: var(--white);
1269
- --color-background: var(--black);
1270
- --color-border: var(--white);
1271
- --color-border-subtle: var(--gray-70);
1272
- --accent: var(--white);
1273
- --shadow-md: 4px 4px 0 var(--color-border);
1274
- --shadow-lg: 8px 8px 0 var(--color-border);
1275
- }
1276
-
1277
-
1278
- /* === EDITORIAL: Luxurious, literary, contemplative ===
1279
- Serif typography, generous whitespace, classical proportions.
1280
- Inspired by high-end magazines and book design.
1281
- For: Magazines, blogs, luxury brands, essays */
1282
- :root[data-motion="editorial"],
1283
- [data-motion="editorial"] {
1284
- /* Very slow, contemplative motion */
1285
- --motion-scale: 0.2;
1286
- --duration-instant: 300ms;
1287
- --duration-fast: 800ms;
1288
- --duration-normal: 1200ms;
1289
- --duration-slow: 2000ms;
1290
- --duration-slower: 3000ms;
1291
- --duration-slowest: 4000ms;
1292
- --ease-fluid: cubic-bezier(0.25, 0.1, 0.25, 1);
1293
- --ease-settle: cubic-bezier(0.25, 0.1, 0.25, 1);
1294
-
1295
- /* Typography: Playfair Display + Libre Baskerville — magazine editorial */
1296
- --font-body: 'Libre Baskerville', var(--font-serif);
1297
- --font-heading: 'Playfair Display', var(--font-serif);
1298
- --font-ui: var(--font-system);
1299
- --leading-normal: 1.85;
1300
- --leading-relaxed: 2;
1301
- --tracking-normal: 0.01em;
1302
- --tracking-wide: 0.1em;
1303
-
1304
- /* Layout: Narrow column, extreme whitespace */
1305
- --space-section: 8rem;
1306
- --space-gutter: var(--space-12);
1307
- --container-content: 38rem;
1308
- --container-prose: 32rem;
1309
- --container-xl: 48rem;
1310
-
1311
- /* Soft, subtle radius */
1312
- --radius-sm: 2px;
1313
- --radius-md: 4px;
1314
- --radius-lg: 6px;
1315
- --radius-xl: 8px;
1316
- --radius-2xl: 12px;
1317
- --radius-card: 4px;
1318
- --radius-button: 2px;
1319
- --radius-input: 4px;
1320
-
1321
- /* No shadows - flat, clean, sophisticated */
1322
- --shadow-xs: none;
1323
- --shadow-sm: none;
1324
- --shadow-md: none;
1325
- --shadow-lg: none;
1326
- --shadow-xl: none;
1327
-
1328
- /* Muted, warm color palette */
1329
- --color-text: oklch(25% 0.02 50);
1330
- --color-text-muted: oklch(45% 0.02 50);
1331
- --color-background: oklch(98% 0.01 60);
1332
- --color-surface: oklch(96% 0.015 60);
1333
- --color-border: oklch(85% 0.02 60);
1334
- --color-border-subtle: oklch(90% 0.015 60);
1335
- --accent: oklch(40% 0.08 30);
1336
- }
1337
-
1338
- /* Editorial dark mode - ink on paper feel */
1339
- [data-motion="editorial"][data-theme="dark"],
1340
- [data-theme="dark"] [data-motion="editorial"] {
1341
- --color-text: oklch(90% 0.01 60);
1342
- --color-text-muted: oklch(70% 0.01 60);
1343
- --color-background: oklch(12% 0.01 60);
1344
- --color-surface: oklch(15% 0.015 60);
1345
- --color-border: oklch(25% 0.02 60);
1346
- --accent: oklch(70% 0.06 40);
1347
- }
1348
-
1349
-
1350
- /* === NEON: Electric, futuristic, high-energy ===
1351
- Geometric sans-serif, dark backgrounds, glowing accents.
1352
- Inspired by cyberpunk, gaming, night clubs.
1353
- For: Gaming, tech startups, entertainment, events */
1354
- :root[data-motion="neon"],
1355
- [data-motion="neon"] {
1356
- /* Fast, snappy, energetic motion */
1357
- --motion-scale: 1.8;
1358
- --duration-instant: 80ms;
1359
- --duration-fast: 150ms;
1360
- --duration-normal: 280ms;
1361
- --duration-slow: 400ms;
1362
- --duration-slower: 550ms;
1363
- --duration-slowest: 750ms;
1364
- --ease-fluid: cubic-bezier(0.34, 1.56, 0.64, 1);
1365
- --ease-settle: cubic-bezier(0.22, 1.6, 0.36, 1);
1366
-
1367
- /* Typography: Orbitron + Exo 2 — cyberpunk, futuristic */
1368
- --font-body: 'Exo 2', var(--font-system);
1369
- --font-heading: 'Orbitron', var(--font-system);
1370
- --font-ui: 'Exo 2', var(--font-system);
1371
- --leading-normal: 1.5;
1372
- --leading-tight: 1.15;
1373
- --tracking-normal: 0;
1374
- --tracking-wide: 0.15em;
1375
-
1376
- /* Layout: Wide, immersive, edge-to-edge */
1377
- --space-section: var(--space-20);
1378
- --space-gutter: var(--space-6);
1379
- --container-content: 100%;
1380
- --container-xl: 100%;
1381
-
1382
- /* Full pill shapes */
1383
- --radius-sm: 9999px;
1384
- --radius-md: 9999px;
1385
- --radius-lg: 9999px;
1386
- --radius-xl: 9999px;
1387
- --radius-2xl: 9999px;
1388
- --radius-card: 24px;
1389
- --radius-button: 9999px;
1390
- --radius-input: 9999px;
1391
-
1392
- /* Glowing shadows */
1393
- --shadow-xs: 0 0 8px var(--accent);
1394
- --shadow-sm: 0 0 12px var(--accent);
1395
- --shadow-md: 0 0 20px var(--accent), 0 0 40px color-mix(in oklch, var(--accent) 50%, transparent);
1396
- --shadow-lg: 0 0 30px var(--accent), 0 0 60px color-mix(in oklch, var(--accent) 50%, transparent);
1397
- --shadow-xl: 0 0 40px var(--accent), 0 0 80px color-mix(in oklch, var(--accent) 40%, transparent);
1398
- --shadow-glow: 0 0 0 2px var(--accent), 0 0 20px var(--accent);
1399
-
1400
- /* Dark by default with neon accent */
1401
- --color-text: oklch(95% 0 0);
1402
- --color-text-muted: oklch(70% 0.02 280);
1403
- --color-background: oklch(8% 0.02 280);
1404
- --color-surface: oklch(12% 0.03 280);
1405
- --color-surface-raised: oklch(16% 0.03 280);
1406
- --color-border: oklch(25% 0.05 280);
1407
- --color-border-subtle: oklch(18% 0.04 280);
1408
-
1409
- /* Electric neon accent */
1410
- --accent: oklch(70% 0.25 320);
1411
- --accent-subtle: oklch(20% 0.15 320);
1412
- --accent-hover: oklch(80% 0.28 320);
1413
- --accent-active: oklch(60% 0.22 320);
542
+ :root,
543
+ [data-motion] {
544
+ --duration-fast: calc(var(--motion-base) * 0.73 * var(--_motion, 1));
545
+ --duration-normal: calc(var(--motion-base) * var(--_motion, 1));
546
+ --duration-slow: calc(var(--motion-base) * 1.37 * var(--_motion, 1));
547
+ --duration-slower: calc(var(--motion-base) * 1.73 * var(--_motion, 1));
1414
548
  }
1415
549
 
1416
- /* Neon "light mode" - inverted but still vibrant */
1417
- [data-motion="neon"][data-theme="light"],
1418
- [data-theme="light"] [data-motion="neon"] {
1419
- --color-text: oklch(15% 0.02 280);
1420
- --color-background: oklch(98% 0.01 280);
1421
- --color-surface: oklch(95% 0.02 280);
1422
- --accent: oklch(55% 0.30 320);
1423
- --shadow-md: 0 0 15px color-mix(in oklch, var(--accent) 40%, transparent);
1424
- --shadow-lg: 0 0 25px color-mix(in oklch, var(--accent) 50%, transparent);
1425
- }
1426
-
1427
-
1428
- /* --- Signature Preset Page Transitions --- */
550
+ [data-motion="reduced"] { --_motion: 0; }
551
+ [data-motion="slow"] { --_motion: 1.5; }
552
+ [data-motion="fast"] { --_motion: 0.5; }
1429
553
 
1430
- /* Brutalist: Hard cut, no transition */
1431
- :root[data-motion="brutalist"] {
1432
- &::view-transition-old(root),
1433
- &::view-transition-new(root) {
1434
- animation: none;
1435
- }
1436
- }
1437
-
1438
- /* Editorial: Very slow crossfade */
1439
- @keyframes editorial-fade-out {
1440
- from { opacity: 1; }
1441
- to { opacity: 0; }
1442
- }
1443
-
1444
- @keyframes editorial-fade-in {
1445
- from { opacity: 0; }
1446
- to { opacity: 1; }
1447
- }
1448
-
1449
- :root[data-motion="editorial"] {
1450
- &::view-transition-old(root) {
1451
- animation: 1.5s cubic-bezier(0.25, 0.1, 0.25, 1) both editorial-fade-out;
1452
- }
1453
-
1454
- &::view-transition-new(root) {
1455
- animation: 1.5s cubic-bezier(0.25, 0.1, 0.25, 1) both editorial-fade-in;
1456
- }
1457
- }
1458
-
1459
- /* Neon: Fast glitch/flash effect */
1460
- @keyframes neon-out {
1461
- 0% { opacity: 1; transform: scale(1); filter: brightness(1); }
1462
- 50% { opacity: 0.8; transform: scale(1.02); filter: brightness(2); }
1463
- 100% { opacity: 0; transform: scale(1.05); filter: brightness(0); }
1464
- }
1465
-
1466
- @keyframes neon-in {
1467
- 0% { opacity: 0; transform: scale(0.95); filter: brightness(0); }
1468
- 30% { opacity: 0.5; transform: scale(0.98); filter: brightness(2); }
1469
- 60% { opacity: 0.9; transform: scale(1.01); filter: brightness(1.5); }
1470
- 100% { opacity: 1; transform: scale(1); filter: brightness(1); }
1471
- }
1472
-
1473
- :root[data-motion="neon"] {
1474
- &::view-transition-old(root) {
1475
- animation: 280ms cubic-bezier(0.34, 1.56, 0.64, 1) both neon-out;
1476
- }
1477
-
1478
- &::view-transition-new(root) {
1479
- animation: 350ms cubic-bezier(0.22, 1.6, 0.36, 1) both neon-in;
1480
- }
1481
- }
1482
-
1483
-
1484
- /* Respect reduced motion preference */
1485
554
  @media (prefers-reduced-motion: reduce) {
1486
- ::view-transition-old(root),
1487
- ::view-transition-new(root) {
1488
- animation: none;
1489
- }
555
+ :root,
556
+ [data-motion] { --_motion: 0; }
1490
557
  }
1491
- }
1492
-
1493
-
1494
- /* ========================================
1495
- COLOR PRESETS
1496
- Ready-to-use accent color themes
1497
- Apply via: <html data-accent="coral">
1498
- ======================================== */
1499
-
1500
- /* Default - Sophisticated neutral gray */
1501
- [data-accent="default"],
1502
- :root:not([data-accent]) {
1503
- --accent: var(--gray-40);
1504
- }
1505
-
1506
- /* Coral - Warm, vibrant energy */
1507
- [data-accent="coral"] {
1508
- --accent: oklch(65% 0.18 25);
1509
- --accent-subtle: oklch(95% 0.04 25);
1510
- --accent-hover: oklch(55% 0.20 25);
1511
- --accent-active: oklch(45% 0.18 25);
1512
- }
1513
-
1514
- /* Vapor - Soft, ethereal purple */
1515
- [data-accent="vapor"] {
1516
- --accent: oklch(55% 0.15 300);
1517
- --accent-subtle: oklch(95% 0.03 300);
1518
- --accent-hover: oklch(45% 0.17 300);
1519
- --accent-active: oklch(40% 0.15 300);
1520
- }
1521
-
1522
- /* Tide - Ocean blue-green */
1523
- [data-accent="tide"] {
1524
- --accent: oklch(55% 0.12 200);
1525
- --accent-subtle: oklch(95% 0.025 200);
1526
- --accent-hover: oklch(45% 0.14 200);
1527
- --accent-active: oklch(40% 0.12 200);
1528
- }
1529
-
1530
- /* Forest - Deep natural green */
1531
- [data-accent="forest"] {
1532
- --accent: oklch(50% 0.12 145);
1533
- --accent-subtle: oklch(95% 0.025 145);
1534
- --accent-hover: oklch(40% 0.14 145);
1535
- --accent-active: oklch(35% 0.12 145);
1536
- }
1537
-
1538
- /* Sunset - Golden warmth */
1539
- [data-accent="sunset"] {
1540
- --accent: oklch(70% 0.16 55);
1541
- --accent-subtle: oklch(95% 0.035 55);
1542
- --accent-hover: oklch(60% 0.18 55);
1543
- --accent-active: oklch(55% 0.16 55);
1544
- }
1545
-
1546
- /* Midnight - Deep sophisticated blue */
1547
- [data-accent="midnight"] {
1548
- --accent: oklch(45% 0.12 260);
1549
- --accent-subtle: oklch(95% 0.025 260);
1550
- --accent-hover: oklch(35% 0.14 260);
1551
- --accent-active: oklch(30% 0.12 260);
1552
- }
1553
-
1554
-
1555
- /* ========================================
1556
- SUBDUED MODE (Low Contrast)
1557
- Reduces contrast for gentler viewing.
1558
- Each preset gets its own tinted subdued variant
1559
- that maintains color character at lower contrast.
1560
- Apply via: <html data-contrast="subdued">
1561
- ======================================== */
1562
-
1563
- /* --- Base subdued: neutral fallback --- */
1564
- [data-contrast="subdued"] {
1565
- --color-text: oklch(35% 0.01 250);
1566
- --color-text-muted: oklch(48% 0.01 250);
1567
- --color-text-subtle: oklch(58% 0.01 250);
1568
- --color-background: oklch(96% 0.005 250);
1569
- --color-surface: oklch(93% 0.005 250);
1570
- --color-surface-raised: oklch(98% 0.005 250);
1571
- --color-border: oklch(82% 0.01 250);
1572
- --color-border-subtle: oklch(87% 0.01 250);
1573
- /* Mute accents globally */
1574
- --accent-subtle: color-mix(in oklch, var(--accent) 10%, var(--color-background));
1575
- }
1576
-
1577
- [data-theme="dark"][data-contrast="subdued"] {
1578
- --color-text: oklch(75% 0.01 230);
1579
- --color-text-muted: oklch(55% 0.01 230);
1580
- --color-text-subtle: oklch(45% 0.01 230);
1581
- --color-background: oklch(16% 0.015 230);
1582
- --color-surface: oklch(20% 0.015 230);
1583
- --color-surface-raised: oklch(24% 0.015 230);
1584
- --color-border: oklch(30% 0.02 230);
1585
- --color-border-subtle: oklch(25% 0.015 230);
1586
- }
1587
-
1588
-
1589
- /* --- STILL subdued: Cool gray, muted slate ---
1590
- Hue 250, very low chroma. Whisper-quiet. */
1591
- [data-motion="still"][data-contrast="subdued"],
1592
- [data-contrast="subdued"] [data-motion="still"],
1593
- [data-contrast="subdued"]:root[data-motion="still"] {
1594
- --color-text: oklch(35% 0.01 250);
1595
- --color-text-muted: oklch(48% 0.01 250);
1596
- --color-text-subtle: oklch(58% 0.008 250);
1597
- --color-background: oklch(96% 0.005 250);
1598
- --color-surface: oklch(93% 0.005 250);
1599
- --color-surface-raised: oklch(98% 0.005 250);
1600
- --color-border: oklch(82% 0.01 250);
1601
- --color-border-subtle: oklch(87% 0.008 250);
1602
- }
1603
-
1604
- [data-theme="dark"][data-motion="still"][data-contrast="subdued"],
1605
- [data-theme="dark"][data-contrast="subdued"] [data-motion="still"],
1606
- [data-theme="dark"][data-contrast="subdued"]:root[data-motion="still"] {
1607
- --color-text: oklch(75% 0.01 250);
1608
- --color-text-muted: oklch(55% 0.008 250);
1609
- --color-text-subtle: oklch(45% 0.008 250);
1610
- --color-background: oklch(15% 0.01 250);
1611
- --color-surface: oklch(19% 0.01 250);
1612
- --color-surface-raised: oklch(23% 0.01 250);
1613
- --color-border: oklch(28% 0.015 250);
1614
- --color-border-subtle: oklch(23% 0.01 250);
1615
- }
1616
-
1617
-
1618
- /* --- SERENE subdued: Warm taupe cream ---
1619
- Hue 60, low chroma. Like aged linen. */
1620
- [data-motion="serene"][data-contrast="subdued"],
1621
- [data-contrast="subdued"] [data-motion="serene"],
1622
- [data-contrast="subdued"]:root[data-motion="serene"] {
1623
- --color-text: oklch(35% 0.015 60);
1624
- --color-text-muted: oklch(48% 0.012 60);
1625
- --color-text-subtle: oklch(58% 0.01 60);
1626
- --color-background: oklch(96% 0.01 60);
1627
- --color-surface: oklch(93% 0.012 65);
1628
- --color-surface-raised: oklch(98% 0.008 60);
1629
- --color-border: oklch(82% 0.015 60);
1630
- --color-border-subtle: oklch(87% 0.01 60);
1631
- }
1632
-
1633
- [data-theme="dark"][data-motion="serene"][data-contrast="subdued"],
1634
- [data-theme="dark"][data-contrast="subdued"] [data-motion="serene"],
1635
- [data-theme="dark"][data-contrast="subdued"]:root[data-motion="serene"] {
1636
- --color-text: oklch(75% 0.01 60);
1637
- --color-text-muted: oklch(55% 0.01 60);
1638
- --color-text-subtle: oklch(45% 0.008 60);
1639
- --color-background: oklch(16% 0.012 60);
1640
- --color-surface: oklch(20% 0.015 60);
1641
- --color-surface-raised: oklch(24% 0.015 60);
1642
- --color-border: oklch(30% 0.015 60);
1643
- --color-border-subtle: oklch(25% 0.012 60);
1644
- }
1645
-
1646
-
1647
- /* --- TRICKLING subdued: Neutral gray-blue ---
1648
- Hue 190, minimal chroma. Understated, efficient. */
1649
- [data-motion="trickling"][data-contrast="subdued"],
1650
- [data-contrast="subdued"] [data-motion="trickling"],
1651
- [data-contrast="subdued"]:root[data-motion="trickling"] {
1652
- --color-text: oklch(35% 0.01 190);
1653
- --color-text-muted: oklch(48% 0.008 190);
1654
- --color-text-subtle: oklch(58% 0.006 190);
1655
- --color-background: oklch(96% 0.006 190);
1656
- --color-surface: oklch(93% 0.008 190);
1657
- --color-surface-raised: oklch(98% 0.004 190);
1658
- --color-border: oklch(82% 0.01 190);
1659
- --color-border-subtle: oklch(87% 0.008 190);
1660
- }
1661
-
1662
- [data-theme="dark"][data-motion="trickling"][data-contrast="subdued"],
1663
- [data-theme="dark"][data-contrast="subdued"] [data-motion="trickling"],
1664
- [data-theme="dark"][data-contrast="subdued"]:root[data-motion="trickling"] {
1665
- --color-text: oklch(75% 0.008 190);
1666
- --color-text-muted: oklch(55% 0.006 190);
1667
- --color-text-subtle: oklch(45% 0.006 190);
1668
- --color-background: oklch(15% 0.012 190);
1669
- --color-surface: oklch(19% 0.012 190);
1670
- --color-surface-raised: oklch(23% 0.012 190);
1671
- --color-border: oklch(28% 0.018 190);
1672
- --color-border-subtle: oklch(23% 0.012 190);
1673
- }
1674
-
1675
-
1676
- /* --- STREAM subdued: Cool teal mist ---
1677
- Hue 200, low chroma. Modern, soft tech. */
1678
- [data-motion="stream"][data-contrast="subdued"],
1679
- [data-contrast="subdued"] [data-motion="stream"],
1680
- [data-contrast="subdued"]:root[data-motion="stream"] {
1681
- --color-text: oklch(35% 0.012 200);
1682
- --color-text-muted: oklch(48% 0.01 200);
1683
- --color-text-subtle: oklch(58% 0.008 200);
1684
- --color-background: oklch(96% 0.008 200);
1685
- --color-surface: oklch(93% 0.01 200);
1686
- --color-surface-raised: oklch(98% 0.006 200);
1687
- --color-border: oklch(82% 0.012 200);
1688
- --color-border-subtle: oklch(87% 0.01 200);
1689
- }
1690
-
1691
- [data-theme="dark"][data-motion="stream"][data-contrast="subdued"],
1692
- [data-theme="dark"][data-contrast="subdued"] [data-motion="stream"],
1693
- [data-theme="dark"][data-contrast="subdued"]:root[data-motion="stream"] {
1694
- --color-text: oklch(75% 0.01 200);
1695
- --color-text-muted: oklch(55% 0.008 200);
1696
- --color-text-subtle: oklch(45% 0.008 200);
1697
- --color-background: oklch(15% 0.015 200);
1698
- --color-surface: oklch(19% 0.015 200);
1699
- --color-surface-raised: oklch(23% 0.015 200);
1700
- --color-border: oklch(28% 0.02 200);
1701
- --color-border-subtle: oklch(23% 0.015 200);
1702
- }
1703
-
1704
-
1705
- /* --- FLOWING subdued: Soft ocean blue (default) ---
1706
- Hue 230, low chroma. Calm water at rest. */
1707
- [data-motion="flowing"][data-contrast="subdued"],
1708
- [data-contrast="subdued"] [data-motion="flowing"],
1709
- [data-contrast="subdued"]:root[data-motion="flowing"],
1710
- [data-contrast="subdued"]:root:not([data-motion]) {
1711
- --color-text: oklch(35% 0.015 230);
1712
- --color-text-muted: oklch(48% 0.012 230);
1713
- --color-text-subtle: oklch(58% 0.01 230);
1714
- --color-background: oklch(96% 0.008 230);
1715
- --color-surface: oklch(93% 0.01 230);
1716
- --color-surface-raised: oklch(98% 0.006 230);
1717
- --color-border: oklch(82% 0.012 230);
1718
- --color-border-subtle: oklch(87% 0.01 230);
1719
- }
1720
-
1721
- [data-theme="dark"][data-motion="flowing"][data-contrast="subdued"],
1722
- [data-theme="dark"][data-contrast="subdued"] [data-motion="flowing"],
1723
- [data-theme="dark"][data-contrast="subdued"]:root[data-motion="flowing"],
1724
- [data-theme="dark"][data-contrast="subdued"]:root:not([data-motion]) {
1725
- --color-text: oklch(75% 0.01 220);
1726
- --color-text-muted: oklch(55% 0.008 220);
1727
- --color-text-subtle: oklch(45% 0.008 220);
1728
- --color-background: oklch(16% 0.02 230);
1729
- --color-surface: oklch(20% 0.02 230);
1730
- --color-surface-raised: oklch(24% 0.02 230);
1731
- --color-border: oklch(30% 0.025 230);
1732
- --color-border-subtle: oklch(25% 0.02 230);
1733
- }
1734
-
1735
-
1736
- /* --- CASCADE subdued: Soft lavender haze ---
1737
- Hue 280, low chroma. Gentle twilight. */
1738
- [data-motion="cascade"][data-contrast="subdued"],
1739
- [data-contrast="subdued"] [data-motion="cascade"],
1740
- [data-contrast="subdued"]:root[data-motion="cascade"] {
1741
- --color-text: oklch(35% 0.012 280);
1742
- --color-text-muted: oklch(48% 0.01 280);
1743
- --color-text-subtle: oklch(58% 0.008 280);
1744
- --color-background: oklch(96% 0.008 280);
1745
- --color-surface: oklch(93% 0.01 280);
1746
- --color-surface-raised: oklch(98% 0.006 280);
1747
- --color-border: oklch(82% 0.012 280);
1748
- --color-border-subtle: oklch(87% 0.01 280);
1749
- }
1750
-
1751
- [data-theme="dark"][data-motion="cascade"][data-contrast="subdued"],
1752
- [data-theme="dark"][data-contrast="subdued"] [data-motion="cascade"],
1753
- [data-theme="dark"][data-contrast="subdued"]:root[data-motion="cascade"] {
1754
- --color-text: oklch(75% 0.008 280);
1755
- --color-text-muted: oklch(55% 0.008 280);
1756
- --color-text-subtle: oklch(45% 0.006 280);
1757
- --color-background: oklch(17% 0.015 280);
1758
- --color-surface: oklch(21% 0.018 280);
1759
- --color-surface-raised: oklch(25% 0.018 280);
1760
- --color-border: oklch(30% 0.02 280);
1761
- --color-border-subtle: oklch(25% 0.015 280);
1762
- }
1763
-
1764
-
1765
- /* --- RAPIDS subdued: Warm muted ember ---
1766
- Hue 25, low chroma. Banked coals. */
1767
- [data-motion="rapids"][data-contrast="subdued"],
1768
- [data-contrast="subdued"] [data-motion="rapids"],
1769
- [data-contrast="subdued"]:root[data-motion="rapids"] {
1770
- --color-text: oklch(35% 0.015 25);
1771
- --color-text-muted: oklch(48% 0.012 25);
1772
- --color-text-subtle: oklch(58% 0.01 25);
1773
- --color-background: oklch(96% 0.01 25);
1774
- --color-surface: oklch(93% 0.012 30);
1775
- --color-surface-raised: oklch(98% 0.008 25);
1776
- --color-border: oklch(82% 0.015 25);
1777
- --color-border-subtle: oklch(87% 0.01 25);
1778
- }
1779
-
1780
- [data-theme="dark"][data-motion="rapids"][data-contrast="subdued"],
1781
- [data-theme="dark"][data-contrast="subdued"] [data-motion="rapids"],
1782
- [data-theme="dark"][data-contrast="subdued"]:root[data-motion="rapids"] {
1783
- --color-text: oklch(75% 0.01 25);
1784
- --color-text-muted: oklch(55% 0.008 25);
1785
- --color-text-subtle: oklch(45% 0.008 25);
1786
- --color-background: oklch(14% 0.02 25);
1787
- --color-surface: oklch(18% 0.02 25);
1788
- --color-surface-raised: oklch(22% 0.02 25);
1789
- --color-border: oklch(28% 0.03 25);
1790
- --color-border-subtle: oklch(23% 0.02 25);
1791
- }
1792
-
1793
-
1794
- /* --- TSUNAMI subdued: Deep muted navy ---
1795
- Hue 250, low chroma. Quiet ocean depth. */
1796
- [data-motion="tsunami"][data-contrast="subdued"],
1797
- [data-contrast="subdued"] [data-motion="tsunami"],
1798
- [data-contrast="subdued"]:root[data-motion="tsunami"] {
1799
- --color-text: oklch(35% 0.015 250);
1800
- --color-text-muted: oklch(48% 0.012 250);
1801
- --color-text-subtle: oklch(58% 0.01 250);
1802
- --color-background: oklch(96% 0.008 250);
1803
- --color-surface: oklch(93% 0.01 250);
1804
- --color-surface-raised: oklch(98% 0.006 250);
1805
- --color-border: oklch(82% 0.012 250);
1806
- --color-border-subtle: oklch(87% 0.01 250);
1807
- }
1808
-
1809
- [data-theme="dark"][data-motion="tsunami"][data-contrast="subdued"],
1810
- [data-theme="dark"][data-contrast="subdued"] [data-motion="tsunami"],
1811
- [data-theme="dark"][data-contrast="subdued"]:root[data-motion="tsunami"] {
1812
- --color-text: oklch(75% 0.012 200);
1813
- --color-text-muted: oklch(55% 0.01 200);
1814
- --color-text-subtle: oklch(45% 0.008 200);
1815
- --color-background: oklch(13% 0.03 250);
1816
- --color-surface: oklch(17% 0.03 250);
1817
- --color-surface-raised: oklch(21% 0.03 250);
1818
- --color-border: oklch(26% 0.04 240);
1819
- --color-border-subtle: oklch(21% 0.03 240);
1820
- }
1821
-
1822
-
1823
- /* --- BRUTALIST subdued: High-mid contrast ---
1824
- Pure achromatic. Less stark than default but still bold. */
1825
- [data-motion="brutalist"][data-contrast="subdued"],
1826
- [data-contrast="subdued"] [data-motion="brutalist"],
1827
- [data-contrast="subdued"]:root[data-motion="brutalist"] {
1828
- --color-text: oklch(20% 0 0);
1829
- --color-text-muted: oklch(40% 0 0);
1830
- --color-text-subtle: oklch(55% 0 0);
1831
- --color-background: oklch(95% 0 0);
1832
- --color-surface: oklch(92% 0 0);
1833
- --color-surface-raised: oklch(98% 0 0);
1834
- --color-border: oklch(60% 0 0);
1835
- --color-border-subtle: oklch(75% 0 0);
1836
- }
1837
-
1838
- [data-theme="dark"][data-motion="brutalist"][data-contrast="subdued"],
1839
- [data-theme="dark"][data-contrast="subdued"] [data-motion="brutalist"],
1840
- [data-theme="dark"][data-contrast="subdued"]:root[data-motion="brutalist"] {
1841
- --color-text: oklch(80% 0 0);
1842
- --color-text-muted: oklch(60% 0 0);
1843
- --color-text-subtle: oklch(45% 0 0);
1844
- --color-background: oklch(10% 0 0);
1845
- --color-surface: oklch(15% 0 0);
1846
- --color-surface-raised: oklch(20% 0 0);
1847
- --color-border: oklch(40% 0 0);
1848
- --color-border-subtle: oklch(30% 0 0);
1849
- }
1850
-
1851
-
1852
- /* --- EDITORIAL subdued: Warm paper muted ---
1853
- Hue 50-60, low chroma. Old library ambience. */
1854
- [data-motion="editorial"][data-contrast="subdued"],
1855
- [data-contrast="subdued"] [data-motion="editorial"],
1856
- [data-contrast="subdued"]:root[data-motion="editorial"] {
1857
- --color-text: oklch(35% 0.015 50);
1858
- --color-text-muted: oklch(50% 0.012 50);
1859
- --color-text-subtle: oklch(60% 0.01 50);
1860
- --color-background: oklch(96% 0.01 60);
1861
- --color-surface: oklch(93% 0.012 60);
1862
- --color-surface-raised: oklch(98% 0.008 60);
1863
- --color-border: oklch(82% 0.015 60);
1864
- --color-border-subtle: oklch(87% 0.01 60);
1865
- }
1866
-
1867
- [data-theme="dark"][data-motion="editorial"][data-contrast="subdued"],
1868
- [data-theme="dark"][data-contrast="subdued"] [data-motion="editorial"],
1869
- [data-theme="dark"][data-contrast="subdued"]:root[data-motion="editorial"] {
1870
- --color-text: oklch(75% 0.008 60);
1871
- --color-text-muted: oklch(55% 0.008 60);
1872
- --color-text-subtle: oklch(45% 0.006 60);
1873
- --color-background: oklch(15% 0.01 60);
1874
- --color-surface: oklch(19% 0.012 60);
1875
- --color-surface-raised: oklch(23% 0.012 60);
1876
- --color-border: oklch(28% 0.015 60);
1877
- --color-border-subtle: oklch(23% 0.01 60);
1878
- }
1879
-
1880
-
1881
- /* --- NEON subdued: Muted glow ---
1882
- Hue 280, reduced chroma. Neon behind frosted glass. */
1883
- [data-motion="neon"][data-contrast="subdued"],
1884
- [data-contrast="subdued"] [data-motion="neon"],
1885
- [data-contrast="subdued"]:root[data-motion="neon"] {
1886
- /* Neon defaults dark, so light subdued is unusual but supported */
1887
- --color-text: oklch(35% 0.015 280);
1888
- --color-text-muted: oklch(50% 0.012 280);
1889
- --color-text-subtle: oklch(60% 0.01 280);
1890
- --color-background: oklch(96% 0.008 280);
1891
- --color-surface: oklch(93% 0.01 280);
1892
- --color-surface-raised: oklch(98% 0.006 280);
1893
- --color-border: oklch(82% 0.012 280);
1894
- --color-border-subtle: oklch(87% 0.01 280);
1895
- /* Mute the glow effects */
1896
- --shadow-xs: none;
1897
- --shadow-sm: 0 0 8px color-mix(in oklch, var(--accent) 20%, transparent);
1898
- --shadow-md: 0 0 12px color-mix(in oklch, var(--accent) 25%, transparent);
1899
- --shadow-lg: 0 0 18px color-mix(in oklch, var(--accent) 25%, transparent);
1900
- --shadow-xl: 0 0 24px color-mix(in oklch, var(--accent) 20%, transparent);
1901
- }
1902
-
1903
- /* Neon dark subdued (the more common case) */
1904
- [data-motion="neon"][data-contrast="subdued"]:not([data-theme="light"]),
1905
- [data-contrast="subdued"]:root[data-motion="neon"]:not([data-theme="light"]) {
1906
- --color-text: oklch(80% 0.01 280);
1907
- --color-text-muted: oklch(60% 0.015 280);
1908
- --color-text-subtle: oklch(45% 0.01 280);
1909
- --color-background: oklch(10% 0.015 280);
1910
- --color-surface: oklch(14% 0.02 280);
1911
- --color-surface-raised: oklch(18% 0.02 280);
1912
- --color-border: oklch(22% 0.03 280);
1913
- --color-border-subtle: oklch(17% 0.02 280);
1914
- /* Muted glow */
1915
- --shadow-xs: none;
1916
- --shadow-sm: 0 0 6px color-mix(in oklch, var(--accent) 15%, transparent);
1917
- --shadow-md: 0 0 10px color-mix(in oklch, var(--accent) 20%, transparent);
1918
- --shadow-lg: 0 0 16px color-mix(in oklch, var(--accent) 20%, transparent);
1919
- --shadow-xl: 0 0 22px color-mix(in oklch, var(--accent) 15%, transparent);
1920
- }
1921
-
1922
-
1923
- /* ========================================
1924
- COLOR PALETTES (Moods)
1925
- Curated color families for accent selection
1926
- Apply via: <html data-palette="fluid">
1927
- ======================================== */
1928
-
1929
- /* --- FLUID PALETTE ---
1930
- Water-inspired colors that match motion styles.
1931
- When motion changes, accent auto-updates (if palette is fluid).
1932
- Each motion style has its natural water color. */
1933
-
1934
- /* Motion-to-color mapping for Fluid palette */
1935
- [data-palette="fluid"][data-motion="still"],
1936
- [data-palette="fluid"]:not([data-motion]) {
1937
- --palette-accent-1: oklch(55% 0.02 250); /* Slate gray - still water */
1938
- }
1939
-
1940
- [data-palette="fluid"][data-motion="serene"] {
1941
- --palette-accent-1: oklch(65% 0.12 195); /* Soft teal - calm lake */
1942
- }
1943
-
1944
- [data-palette="fluid"][data-motion="trickling"] {
1945
- --palette-accent-1: oklch(70% 0.14 210); /* Light cyan - gentle stream */
1946
- }
1947
-
1948
- [data-palette="fluid"][data-motion="flowing"] {
1949
- --palette-accent-1: oklch(60% 0.18 240); /* Medium blue - steady river */
1950
- }
1951
-
1952
- [data-palette="fluid"][data-motion="rapids"] {
1953
- --palette-accent-1: oklch(55% 0.15 200); /* Deep teal - whitewater */
1954
- }
1955
-
1956
- [data-palette="fluid"][data-motion="tsunami"] {
1957
- --palette-accent-1: oklch(40% 0.15 260); /* Deep navy - Hokusai wave */
1958
- }
1959
-
1960
- /* Fluid palette swatches (all 6 water colors) */
1961
- [data-palette="fluid"] {
1962
- --palette-1: oklch(55% 0.02 250); /* Still - Slate */
1963
- --palette-2: oklch(65% 0.12 195); /* Serene - Soft teal */
1964
- --palette-3: oklch(70% 0.14 210); /* Trickling - Light cyan */
1965
- --palette-4: oklch(60% 0.18 240); /* Flowing - Medium blue */
1966
- --palette-5: oklch(55% 0.15 200); /* Rapids - Deep teal */
1967
- --palette-6: oklch(40% 0.15 260); /* Tsunami - Deep navy */
1968
- }
1969
-
1970
-
1971
- /* --- MORANDI PALETTE ---
1972
- Muted, sophisticated colors inspired by Giorgio Morandi.
1973
- Desaturated, elegant, timeless. */
1974
- [data-palette="morandi"] {
1975
- --palette-1: oklch(65% 0.04 50); /* Dusty rose */
1976
- --palette-2: oklch(70% 0.03 90); /* Sage mist */
1977
- --palette-3: oklch(55% 0.05 230); /* Slate blue */
1978
- --palette-4: oklch(60% 0.04 60); /* Clay */
1979
- --palette-5: oklch(75% 0.02 100); /* Soft moss */
1980
- --palette-6: oklch(50% 0.04 280); /* Dusty lavender */
1981
- }
1982
-
1983
-
1984
- /* --- BOLD PALETTE ---
1985
- Vibrant, saturated colors that pop.
1986
- High energy, confident, attention-grabbing. */
1987
- [data-palette="bold"] {
1988
- --palette-1: oklch(60% 0.25 25); /* Electric coral */
1989
- --palette-2: oklch(55% 0.22 300); /* Vivid purple */
1990
- --palette-3: oklch(70% 0.20 145); /* Lime green */
1991
- --palette-4: oklch(65% 0.25 45); /* Hot orange */
1992
- --palette-5: oklch(50% 0.20 260); /* Royal blue */
1993
- --palette-6: oklch(55% 0.25 340); /* Magenta */
1994
- }
1995
-
1996
-
1997
- /* --- PASTEL PALETTE ---
1998
- Soft, light colors with gentle saturation.
1999
- Calming, approachable, friendly. */
2000
- [data-palette="pastel"] {
2001
- --palette-1: oklch(85% 0.08 350); /* Baby pink */
2002
- --palette-2: oklch(88% 0.06 200); /* Powder blue */
2003
- --palette-3: oklch(90% 0.07 100); /* Mint cream */
2004
- --palette-4: oklch(87% 0.08 80); /* Buttercream */
2005
- --palette-5: oklch(85% 0.07 280); /* Lavender mist */
2006
- --palette-6: oklch(88% 0.06 165); /* Seafoam */
2007
- }
2008
-
2009
-
2010
- /* --- EARTH PALETTE ---
2011
- Natural, warm tones from the earth.
2012
- Grounded, organic, trustworthy. */
2013
- [data-palette="earth"] {
2014
- --palette-1: oklch(50% 0.10 55); /* Terracotta */
2015
- --palette-2: oklch(45% 0.08 145); /* Forest green */
2016
- --palette-3: oklch(55% 0.12 70); /* Amber */
2017
- --palette-4: oklch(40% 0.06 40); /* Espresso */
2018
- --palette-5: oklch(60% 0.05 85); /* Sand */
2019
- --palette-6: oklch(50% 0.08 180); /* Teal stone */
2020
- }
2021
-
2022
-
2023
- /* --- MONO PALETTE ---
2024
- Single hue variations in blue-gray.
2025
- Clean, professional, focused. */
2026
- [data-palette="mono"] {
2027
- --palette-1: oklch(30% 0.03 250); /* Deep */
2028
- --palette-2: oklch(40% 0.03 250); /* Dark */
2029
- --palette-3: oklch(50% 0.03 250); /* Medium */
2030
- --palette-4: oklch(60% 0.03 250); /* Light */
2031
- --palette-5: oklch(70% 0.02 250); /* Lighter */
2032
- --palette-6: oklch(80% 0.02 250); /* Lightest */
2033
- }
2034
-
2035
-
2036
- /* --- 80s PALETTE ---
2037
- Retro 80s neon and synth-wave vibes.
2038
- Electric, nostalgic, bold. */
2039
- [data-palette="80s"] {
2040
- --palette-1: oklch(60% 0.28 330); /* Hot pink */
2041
- --palette-2: oklch(70% 0.22 190); /* Electric cyan */
2042
- --palette-3: oklch(55% 0.25 290); /* Purple neon */
2043
- --palette-4: oklch(75% 0.18 100); /* Lime */
2044
- --palette-5: oklch(65% 0.22 50); /* Sunset orange */
2045
- --palette-6: oklch(50% 0.20 260); /* Electric blue */
2046
- }
2047
-
2048
-
2049
- /* --- MATRIX PALETTE ---
2050
- Terminal/coding aesthetic.
2051
- Dark, digital, hacker vibes. */
2052
- [data-palette="matrix"] {
2053
- --palette-1: oklch(70% 0.25 145); /* Matrix green */
2054
- --palette-2: oklch(60% 0.20 145); /* Dark terminal */
2055
- --palette-3: oklch(55% 0.18 180); /* Cyan terminal */
2056
- --palette-4: oklch(45% 0.15 145); /* Deep code */
2057
- --palette-5: oklch(65% 0.12 100); /* Phosphor */
2058
- --palette-6: oklch(50% 0.08 200); /* Cold steel */
2059
- }
2060
-
2061
-
2062
- /* --- SUBTLE PALETTE ---
2063
- Trending soft feminine palettes (Etsy/IG aesthetic).
2064
- Brown, teal, gold - softened and subdued. */
2065
- [data-palette="subtle"] {
2066
- --palette-1: oklch(55% 0.08 50); /* Warm taupe */
2067
- --palette-2: oklch(50% 0.10 180); /* Muted teal */
2068
- --palette-3: oklch(65% 0.12 75); /* Soft gold */
2069
- --palette-4: oklch(60% 0.06 30); /* Blush brown */
2070
- --palette-5: oklch(70% 0.05 90); /* Cream sage */
2071
- --palette-6: oklch(45% 0.08 200); /* Deep sea */
2072
- }
2073
-
2074
-
2075
- /* --- TSUNAMI PALETTE ---
2076
- The dark, powerful side of a roaring wave.
2077
- Deep midnight blues, dark teals, ocean depths.
2078
- Dramatic, intense, commanding. */
2079
- [data-palette="tsunami"] {
2080
- --palette-1: oklch(25% 0.08 250); /* Midnight abyss */
2081
- --palette-2: oklch(30% 0.12 220); /* Deep ocean teal */
2082
- --palette-3: oklch(35% 0.10 200); /* Dark sea */
2083
- --palette-4: oklch(45% 0.15 210); /* Storm teal */
2084
- --palette-5: oklch(55% 0.12 195); /* Cresting wave */
2085
- --palette-6: oklch(20% 0.06 260); /* Ocean trench */
2086
- }
2087
-
2088
-
2089
- /* --- BRUTALIST PALETTE ---
2090
- High-contrast, stark, minimal color.
2091
- Black, white, and one brutal accent.
2092
- Raw, uncompromising, typographic. */
2093
- [data-palette="brutalist"] {
2094
- --palette-1: oklch(0% 0 0); /* Pure black */
2095
- --palette-2: oklch(100% 0 0); /* Pure white */
2096
- --palette-3: oklch(50% 0 0); /* True gray */
2097
- --palette-4: oklch(60% 0.25 25); /* Warning red */
2098
- --palette-5: oklch(25% 0 0); /* Near black */
2099
- --palette-6: oklch(75% 0 0); /* Light gray */
2100
- }
2101
-
2102
-
2103
- /* --- EDITORIAL PALETTE ---
2104
- Warm, muted, sophisticated colors.
2105
- Ink tones, paper textures, literary feel.
2106
- Timeless, elegant, understated. */
2107
- [data-palette="editorial"] {
2108
- --palette-1: oklch(40% 0.08 30); /* Dried ink */
2109
- --palette-2: oklch(55% 0.06 50); /* Warm sepia */
2110
- --palette-3: oklch(65% 0.04 70); /* Aged paper */
2111
- --palette-4: oklch(35% 0.05 200); /* Slate blue */
2112
- --palette-5: oklch(70% 0.03 90); /* Parchment */
2113
- --palette-6: oklch(45% 0.07 150); /* Verdigris */
2114
- }
2115
-
2116
-
2117
- /* --- NEON PALETTE ---
2118
- Electric, vibrant, cyberpunk colors.
2119
- Glowing accents on dark backgrounds.
2120
- Futuristic, bold, attention-grabbing. */
2121
- [data-palette="neon"] {
2122
- --palette-1: oklch(70% 0.25 320); /* Hot magenta */
2123
- --palette-2: oklch(75% 0.22 190); /* Electric cyan */
2124
- --palette-3: oklch(80% 0.20 100); /* Lime green */
2125
- --palette-4: oklch(65% 0.28 290); /* Violet */
2126
- --palette-5: oklch(75% 0.18 50); /* Orange glow */
2127
- --palette-6: oklch(60% 0.25 250); /* Blue electric */
2128
- }
2129
-
2130
-
2131
- /* ========================================
2132
- APPLY PALETTE ACCENT
2133
- When data-palette is set, use palette colors
2134
- ======================================== */
2135
-
2136
- /* Map swatch selection to accent */
2137
- [data-palette][data-accent="1"] { --accent: var(--palette-1); }
2138
- [data-palette][data-accent="2"] { --accent: var(--palette-2); }
2139
- [data-palette][data-accent="3"] { --accent: var(--palette-3); }
2140
- [data-palette][data-accent="4"] { --accent: var(--palette-4); }
2141
- [data-palette][data-accent="5"] { --accent: var(--palette-5); }
2142
- [data-palette][data-accent="6"] { --accent: var(--palette-6); }
2143
-
2144
- /* Auto-derive accent variants from selected palette color */
2145
- [data-palette] {
2146
- --accent-subtle: color-mix(in oklch, var(--accent) 15%, var(--color-background));
2147
- --accent-hover: color-mix(in oklch, var(--accent) 85%, var(--theme-dark));
2148
- --accent-active: color-mix(in oklch, var(--accent) 70%, var(--theme-dark));
2149
- }
2150
-
2151
- /* Fluid palette: auto-select accent based on motion */
2152
- [data-palette="fluid"]:not([data-accent]) {
2153
- --accent: var(--palette-accent-1);
2154
- }
2155
-
2156
- /* Default accent fallbacks for each palette (when no explicit accent selected) */
2157
- [data-palette="morandi"]:not([data-accent]) { --accent: var(--palette-2); } /* Sage mist */
2158
- [data-palette="bold"]:not([data-accent]) { --accent: var(--palette-1); } /* Electric coral */
2159
- [data-palette="pastel"]:not([data-accent]) { --accent: var(--palette-2); } /* Powder blue */
2160
- [data-palette="earth"]:not([data-accent]) { --accent: var(--palette-1); } /* Terracotta */
2161
- [data-palette="mono"]:not([data-accent]) { --accent: var(--palette-3); } /* Medium gray */
2162
- [data-palette="80s"]:not([data-accent]) { --accent: var(--palette-1); } /* Hot pink */
2163
- [data-palette="matrix"]:not([data-accent]) { --accent: var(--palette-1); } /* Matrix green */
2164
- [data-palette="subtle"]:not([data-accent]) { --accent: var(--palette-2); } /* Muted teal */
2165
- [data-palette="tsunami"]:not([data-accent]) { --accent: var(--palette-4); } /* Storm teal */
2166
- [data-palette="brutalist"]:not([data-accent]) { --accent: var(--palette-1); } /* Pure black */
2167
- [data-palette="editorial"]:not([data-accent]) { --accent: var(--palette-1); } /* Dried ink */
2168
- [data-palette="neon"]:not([data-accent]) { --accent: var(--palette-1); } /* Hot magenta */
2169
-
2170
-
2171
- /* ========================================
2172
- THEMED SURFACE & SECTION COLORS
2173
- Tinted backgrounds derived from palette
2174
- ======================================== */
2175
-
2176
- /* Base themed surfaces - derived from accent */
2177
- [data-palette] {
2178
- /* Subtle tint for alternating sections */
2179
- --surface-tinted: color-mix(in oklch, var(--accent) 8%, var(--color-background));
2180
- --surface-accent: color-mix(in oklch, var(--accent) 15%, var(--color-background));
2181
- --section-alt: color-mix(in oklch, var(--accent) 5%, var(--color-background));
2182
- }
2183
-
2184
- /* Mono: use gray tints instead of colored */
2185
- [data-palette="mono"] {
2186
- --surface-tinted: oklch(from var(--color-background) calc(l - 0.03) 0.01 250);
2187
- --surface-accent: oklch(from var(--color-background) calc(l - 0.06) 0.02 250);
2188
- --section-alt: oklch(from var(--color-background) calc(l - 0.02) 0.01 250);
2189
- }
2190
-
2191
- /* Pastel: lighter tints since colors are already soft */
2192
- [data-palette="pastel"] {
2193
- --surface-tinted: color-mix(in oklch, var(--accent) 12%, var(--color-background));
2194
- --surface-accent: color-mix(in oklch, var(--accent) 20%, var(--color-background));
2195
- }
2196
-
2197
- /* Bold/80s: stronger presence */
2198
- [data-palette="bold"],
2199
- [data-palette="80s"] {
2200
- --surface-tinted: color-mix(in oklch, var(--accent) 10%, var(--color-background));
2201
- --surface-accent: color-mix(in oklch, var(--accent) 18%, var(--color-background));
2202
- }
2203
-
2204
-
2205
- /* ========================================
2206
- DUOTONE IMAGE FILTERS
2207
- Theme-aware image color overlays
2208
- ======================================== */
2209
-
2210
- /* Duotone colors per palette */
2211
- [data-palette="fluid"] {
2212
- --duotone-dark: oklch(0.30 0.08 220);
2213
- --duotone-light: oklch(0.70 0.12 200);
2214
- }
2215
-
2216
- [data-palette="morandi"] {
2217
- --duotone-dark: oklch(0.35 0.04 60);
2218
- --duotone-light: oklch(0.75 0.04 80);
2219
- }
2220
-
2221
- [data-palette="bold"] {
2222
- --duotone-dark: oklch(0.30 0.15 25);
2223
- --duotone-light: oklch(0.65 0.20 30);
2224
- }
2225
-
2226
- [data-palette="pastel"] {
2227
- --duotone-dark: oklch(0.50 0.08 200);
2228
- --duotone-light: oklch(0.88 0.06 180);
2229
- }
2230
-
2231
- [data-palette="earth"] {
2232
- --duotone-dark: oklch(0.30 0.08 55);
2233
- --duotone-light: oklch(0.60 0.10 70);
2234
- }
2235
-
2236
- [data-palette="mono"] {
2237
- --duotone-dark: oklch(0.25 0.02 250);
2238
- --duotone-light: oklch(0.70 0.02 250);
2239
- }
2240
-
2241
- [data-palette="80s"] {
2242
- --duotone-dark: oklch(0.25 0.20 300);
2243
- --duotone-light: oklch(0.65 0.25 330);
2244
- }
2245
-
2246
- [data-palette="matrix"] {
2247
- --duotone-dark: oklch(0.15 0.10 145);
2248
- --duotone-light: oklch(0.60 0.22 145);
2249
- }
2250
-
2251
- [data-palette="subtle"] {
2252
- --duotone-dark: oklch(0.35 0.06 180);
2253
- --duotone-light: oklch(0.65 0.08 170);
2254
- }
2255
-
2256
- [data-palette="tsunami"] {
2257
- --duotone-dark: oklch(0.15 0.10 250);
2258
- --duotone-light: oklch(0.40 0.12 210);
2259
- }
2260
-
2261
- [data-palette="brutalist"] {
2262
- --duotone-dark: oklch(0 0 0);
2263
- --duotone-light: oklch(1 0 0);
2264
- }
2265
-
2266
- [data-palette="editorial"] {
2267
- --duotone-dark: oklch(0.30 0.04 50);
2268
- --duotone-light: oklch(0.80 0.03 60);
2269
- }
2270
-
2271
- [data-palette="neon"] {
2272
- --duotone-dark: oklch(0.10 0.15 280);
2273
- --duotone-light: oklch(0.70 0.25 320);
2274
- }
2275
-
2276
-
2277
- /* ========================================
2278
- PALETTE BACKGROUND TOKENS
2279
- Light/Dark/Neutral for visual hierarchy
2280
- ======================================== */
2281
-
2282
- /* Base backgrounds - subtle variations from palette */
2283
- [data-palette] {
2284
- --bg-light: color-mix(in oklch, var(--palette-5) 8%, var(--color-background));
2285
- --bg-neutral: color-mix(in oklch, var(--palette-3) 12%, var(--color-background));
2286
- --bg-dark: color-mix(in oklch, var(--palette-1) 15%, var(--color-background));
2287
- }
2288
-
2289
- /* Mono: gray scale backgrounds */
2290
- [data-palette="mono"] {
2291
- --bg-light: oklch(from var(--color-background) calc(l - 0.02) 0.01 250);
2292
- --bg-neutral: oklch(from var(--color-background) calc(l - 0.05) 0.02 250);
2293
- --bg-dark: oklch(from var(--color-background) calc(l - 0.10) 0.02 250);
2294
- }
2295
-
2296
- /* Bold: stronger colored backgrounds */
2297
- [data-palette="bold"] {
2298
- --bg-light: color-mix(in oklch, var(--palette-3) 10%, var(--color-background));
2299
- --bg-neutral: color-mix(in oklch, var(--palette-5) 15%, var(--color-background));
2300
- --bg-dark: color-mix(in oklch, var(--palette-1) 18%, var(--color-background));
2301
- }
2302
-
2303
- /* Matrix: dark terminal feel */
2304
- [data-palette="matrix"] {
2305
- --bg-light: color-mix(in oklch, var(--palette-1) 5%, var(--color-background));
2306
- --bg-neutral: color-mix(in oklch, var(--palette-4) 12%, var(--color-background));
2307
- --bg-dark: color-mix(in oklch, var(--palette-6) 20%, var(--color-background));
2308
- }
2309
-
2310
- /* Tsunami: deep ocean layers */
2311
- [data-palette="tsunami"] {
2312
- --bg-light: color-mix(in oklch, var(--palette-5) 15%, var(--color-background));
2313
- --bg-neutral: color-mix(in oklch, var(--palette-3) 25%, var(--color-background));
2314
- --bg-dark: color-mix(in oklch, var(--palette-1) 35%, var(--color-background));
2315
- }
2316
-
2317
-
2318
- /* ========================================
2319
- THEME-SPECIFIC SECTION COLORS
2320
- Alternating backgrounds per motion style
2321
- ======================================== */
2322
-
2323
- /* Still: Clean white/near-white with subtle gray alternation */
2324
- [data-motion="still"] {
2325
- --section-1: var(--color-background);
2326
- --section-2: oklch(from var(--color-background) calc(l - 0.02) 0.01 250);
2327
- --section-3: var(--color-background);
2328
- --section-accent: oklch(from var(--color-background) calc(l - 0.04) 0.02 250);
2329
- }
2330
-
2331
- /* Serene: Warm cream tones, editorial feel */
2332
- [data-motion="serene"] {
2333
- --section-1: oklch(from var(--color-background) l 0.01 60);
2334
- --section-2: oklch(from var(--color-background) calc(l - 0.02) 0.02 70);
2335
- --section-3: oklch(from var(--color-background) l 0.015 50);
2336
- --section-accent: color-mix(in oklch, var(--accent) 8%, var(--color-background));
2337
- }
2338
-
2339
- /* Stream: Cool, minimal alternation */
2340
- [data-motion="stream"] {
2341
- --section-1: var(--color-background);
2342
- --section-2: color-mix(in oklch, var(--accent) 4%, var(--color-background));
2343
- --section-3: var(--color-background);
2344
- --section-accent: color-mix(in oklch, var(--accent) 10%, var(--color-background));
2345
- }
2346
-
2347
- /* Flowing: Balanced, water-inspired blues */
2348
- [data-motion="flowing"] {
2349
- --section-1: var(--color-background);
2350
- --section-2: color-mix(in oklch, var(--accent) 6%, var(--color-background));
2351
- --section-3: oklch(from var(--color-background) calc(l - 0.01) 0.02 220);
2352
- --section-accent: color-mix(in oklch, var(--accent) 12%, var(--color-background));
2353
- }
2354
-
2355
- /* Cascade: Soft pastel alternation */
2356
- [data-motion="cascade"] {
2357
- --section-1: var(--color-background);
2358
- --section-2: color-mix(in oklch, var(--accent) 8%, var(--color-background));
2359
- --section-3: oklch(from var(--color-background) l 0.03 180);
2360
- --section-accent: color-mix(in oklch, var(--accent) 15%, var(--color-background));
2361
- }
2362
-
2363
- /* Rapids: Bold, energetic color bands using palette backgrounds */
2364
- [data-motion="rapids"] {
2365
- --section-1: var(--bg-light);
2366
- --section-2: var(--bg-neutral);
2367
- --section-3: var(--color-background);
2368
- --section-accent: var(--bg-dark);
2369
- }
2370
-
2371
- /* Tsunami: Dramatic deep ocean sections with strong hierarchy */
2372
- [data-motion="tsunami"] {
2373
- --section-1: var(--bg-neutral);
2374
- --section-2: var(--bg-dark);
2375
- --section-3: var(--bg-light);
2376
- --section-accent: color-mix(in oklch, var(--accent) 30%, var(--bg-dark));
2377
- }
2378
-
2379
- /* Brutalist: Stark black/white alternation, no softness */
2380
- [data-motion="brutalist"] {
2381
- --section-1: var(--color-background);
2382
- --section-2: var(--color-border);
2383
- --section-3: var(--color-background);
2384
- --section-accent: var(--color-text);
2385
- }
2386
-
2387
- /* Editorial: Warm, subtle cream variations */
2388
- [data-motion="editorial"] {
2389
- --section-1: oklch(98% 0.01 60);
2390
- --section-2: oklch(96% 0.015 65);
2391
- --section-3: oklch(97% 0.01 55);
2392
- --section-accent: oklch(94% 0.02 50);
2393
- }
2394
-
2395
- /* Neon: Dark sections with subtle purple variation */
2396
- [data-motion="neon"] {
2397
- --section-1: oklch(8% 0.02 280);
2398
- --section-2: oklch(10% 0.03 290);
2399
- --section-3: oklch(6% 0.02 270);
2400
- --section-accent: oklch(15% 0.08 320);
2401
- }
2402
-
2403
558
 
2404
- /* ========================================
2405
- FULL THEME OVERRIDES
2406
- Themes that customize entire color palette
2407
- Apply via: <html data-theme="ocean">
2408
- ======================================== */
2409
559
 
2410
- /*
2411
- [data-theme="ocean"] {
2412
- --theme-dark: oklch(15% 0.03 230);
2413
- --theme-light: oklch(98% 0.01 230);
2414
- --accent: oklch(50% 0.15 230);
560
+ /* PAGE TRANSITIONS are opt-in and not in this file. A site that wants
561
+ cross-document transitions adds, in its own stylesheet:
562
+ @media (prefers-reduced-motion: no-preference) {
563
+ @view-transition { navigation: auto; }
564
+ }
565
+ and gets the browser's own short cross-fade. */
2415
566
  }
2416
- */