turbo-themes 0.40.17 → 0.41.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 (99) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/assets/css/home.css +8 -3
  4. data/assets/css/noir.css +447 -0
  5. data/assets/css/site.css +31 -11
  6. data/assets/css/themes/ayu-dark.css +7 -2
  7. data/assets/css/themes/ayu-light.css +14 -12
  8. data/assets/css/themes/ayu-mirage.css +7 -2
  9. data/assets/css/themes/bulma-dark.css +9 -4
  10. data/assets/css/themes/bulma-light.css +7 -4
  11. data/assets/css/themes/catppuccin-frappe.css +6 -1
  12. data/assets/css/themes/catppuccin-latte.css +14 -12
  13. data/assets/css/themes/catppuccin-macchiato.css +6 -1
  14. data/assets/css/themes/catppuccin-mocha.css +6 -1
  15. data/assets/css/themes/dracula.css +8 -3
  16. data/assets/css/themes/everforest-dark-hard.css +6 -1
  17. data/assets/css/themes/everforest-dark-soft.css +11 -6
  18. data/assets/css/themes/everforest-dark.css +9 -4
  19. data/assets/css/themes/everforest-light-hard.css +12 -11
  20. data/assets/css/themes/everforest-light-soft.css +15 -14
  21. data/assets/css/themes/everforest-light.css +12 -11
  22. data/assets/css/themes/github-dark.css +7 -4
  23. data/assets/css/themes/github-light.css +5 -0
  24. data/assets/css/themes/gruvbox-dark-hard.css +6 -1
  25. data/assets/css/themes/gruvbox-dark-soft.css +8 -3
  26. data/assets/css/themes/gruvbox-dark.css +8 -3
  27. data/assets/css/themes/gruvbox-light-hard.css +7 -2
  28. data/assets/css/themes/gruvbox-light-soft.css +8 -4
  29. data/assets/css/themes/gruvbox-light.css +8 -3
  30. data/assets/css/themes/kanagawa-dragon.css +6 -1
  31. data/assets/css/themes/kanagawa-lotus.css +10 -6
  32. data/assets/css/themes/kanagawa-wave.css +6 -1
  33. data/assets/css/themes/nord.css +5 -0
  34. data/assets/css/themes/one-dark.css +10 -5
  35. data/assets/css/themes/one-light.css +12 -11
  36. data/assets/css/themes/radix-mauve-dark.css +5 -0
  37. data/assets/css/themes/radix-mauve-light.css +7 -4
  38. data/assets/css/themes/radix-slate-dark.css +5 -0
  39. data/assets/css/themes/radix-slate-light.css +7 -4
  40. data/assets/css/themes/rose-pine-dawn.css +12 -8
  41. data/assets/css/themes/rose-pine-moon.css +7 -2
  42. data/assets/css/themes/rose-pine.css +5 -0
  43. data/assets/css/themes/solarized-dark.css +16 -11
  44. data/assets/css/themes/solarized-light.css +17 -15
  45. data/assets/css/themes/terminal.css +5 -0
  46. data/assets/css/themes/tokyo-night-dark.css +5 -0
  47. data/assets/css/themes/tokyo-night-light.css +11 -6
  48. data/assets/css/themes/tokyo-night-storm.css +5 -0
  49. data/assets/css/themes/turbo/ayu-dark.css +6 -5
  50. data/assets/css/themes/turbo/ayu-light.css +13 -12
  51. data/assets/css/themes/turbo/ayu-mirage.css +6 -5
  52. data/assets/css/themes/turbo/bulma-dark.css +5 -4
  53. data/assets/css/themes/turbo/bulma-light.css +5 -4
  54. data/assets/css/themes/turbo/catppuccin-frappe.css +2 -1
  55. data/assets/css/themes/turbo/catppuccin-latte.css +15 -14
  56. data/assets/css/themes/turbo/catppuccin-macchiato.css +2 -1
  57. data/assets/css/themes/turbo/catppuccin-mocha.css +2 -1
  58. data/assets/css/themes/turbo/dracula.css +7 -6
  59. data/assets/css/themes/turbo/everforest-dark-hard.css +2 -1
  60. data/assets/css/themes/turbo/everforest-dark-soft.css +10 -9
  61. data/assets/css/themes/turbo/everforest-dark.css +8 -7
  62. data/assets/css/themes/turbo/everforest-light-hard.css +16 -15
  63. data/assets/css/themes/turbo/everforest-light-soft.css +22 -21
  64. data/assets/css/themes/turbo/everforest-light.css +16 -15
  65. data/assets/css/themes/turbo/github-dark.css +7 -6
  66. data/assets/css/themes/turbo/github-light.css +1 -0
  67. data/assets/css/themes/turbo/gruvbox-dark-hard.css +2 -1
  68. data/assets/css/themes/turbo/gruvbox-dark-soft.css +4 -3
  69. data/assets/css/themes/turbo/gruvbox-dark.css +4 -3
  70. data/assets/css/themes/turbo/gruvbox-light-hard.css +5 -4
  71. data/assets/css/themes/turbo/gruvbox-light-soft.css +6 -5
  72. data/assets/css/themes/turbo/gruvbox-light.css +6 -5
  73. data/assets/css/themes/turbo/kanagawa-dragon.css +5 -4
  74. data/assets/css/themes/turbo/kanagawa-lotus.css +9 -8
  75. data/assets/css/themes/turbo/kanagawa-wave.css +5 -4
  76. data/assets/css/themes/turbo/nord.css +1 -0
  77. data/assets/css/themes/turbo/one-dark.css +9 -8
  78. data/assets/css/themes/turbo/one-light.css +11 -10
  79. data/assets/css/themes/turbo/radix-mauve-dark.css +1 -0
  80. data/assets/css/themes/turbo/radix-mauve-light.css +3 -2
  81. data/assets/css/themes/turbo/radix-slate-dark.css +1 -0
  82. data/assets/css/themes/turbo/radix-slate-light.css +3 -2
  83. data/assets/css/themes/turbo/rose-pine-dawn.css +11 -10
  84. data/assets/css/themes/turbo/rose-pine-moon.css +6 -5
  85. data/assets/css/themes/turbo/rose-pine.css +1 -0
  86. data/assets/css/themes/turbo/solarized-dark.css +20 -19
  87. data/assets/css/themes/turbo/solarized-light.css +21 -20
  88. data/assets/css/themes/turbo/terminal.css +1 -0
  89. data/assets/css/themes/turbo/tokyo-night-dark.css +1 -0
  90. data/assets/css/themes/turbo/tokyo-night-light.css +10 -9
  91. data/assets/css/themes/turbo/tokyo-night-storm.css +1 -0
  92. data/assets/css/turbo-core.css +10 -1
  93. data/assets/js/homepage-showcase.js +3 -3
  94. data/assets/js/homepage-showcase.js.map +1 -1
  95. data/assets/js/homepage-showcase.min.js +2 -2
  96. data/assets/js/theme-selector.min.js +2 -2
  97. data/assets/scss/_tokens.scss +6 -1
  98. data/lib/turbo-themes/version.rb +1 -1
  99. metadata +2 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2264b02a4eb5f5af5af47d7601a68280eb8334fc3993cf397daf4fb74d3dc494
4
- data.tar.gz: 977b7c6535e9d38bbe79a714aeafcbf75089b9c4941fbca88acde8ded37a6ed1
3
+ metadata.gz: 3a8013dc373c73f6bf4158c858eef0d4618b7ea99545ed9b6c1a76aad5d3f2b2
4
+ data.tar.gz: 1f588862d17e07ec105370caf1666617fffa6d7a830bbc25f4f68d271fded137
5
5
  SHA512:
6
- metadata.gz: 0ce38afc1b6f282a69bb0086f2f6b139f845c121ef9409ec61bcbf0fe722920198a3994d02c0333fbf1c74dcaa61e05c10bc2f1ee1092807a2b8103a96942762
7
- data.tar.gz: 8461207ee842d51aa5da5dc18a04599254d995427168b7e2afa3ab088ff994a5a18b40c2b5e8757545b6b136d76060b124dfec4e8c2257304c3accaa31025e02
6
+ metadata.gz: d4adcd38fc59b9bb18a52c11c24079905a5fc424849738115e2aeadf776e974728ac8d3df17f6370ee2761f0b49f57dbdc612fe9985746308d00442f16756765
7
+ data.tar.gz: 25c5ae91fdfd421a0ee6c0738b055330966eabbf290472f5e65709f872d980e7193e71e40bc26245ab5debf50e3a06bafaf3d6696613feab9f9339b3e012095b
data/CHANGELOG.md CHANGED
@@ -10,6 +10,12 @@ The format is based on Keep a Changelog and this project adheres to SemVer.
10
10
 
11
11
  - TBD
12
12
 
13
+ ## [0.41.0] - 2026-07-27
14
+
15
+ ### ✨ Added
16
+
17
+ - Strict WCAG AA contrast and widened axe coverage (#774)
18
+
13
19
  ## [0.40.17] - 2026-07-27
14
20
 
15
21
  ### 🐛 Fixed
data/assets/css/home.css CHANGED
@@ -149,8 +149,11 @@
149
149
  */
150
150
  .showcase-page .showcase-theme-more {
151
151
  font-size: 0.875rem;
152
- color: color-mix(in srgb, var(--turbo-brand-primary) 60%, var(--turbo-text-primary));
153
- text-decoration: none;
152
+ color: var(--turbo-accent-link, var(--turbo-text-primary));
153
+ text-decoration: underline;
154
+ text-underline-offset: 2px;
155
+ /* Beat chrome link color transitions during theme application. */
156
+ transition: none;
154
157
  }
155
158
 
156
159
  .showcase-page .showcase-theme-more:hover {
@@ -223,7 +226,8 @@
223
226
 
224
227
  .showcase-preview-tab {
225
228
  flex: 1;
226
- padding: 0.375rem 0.5rem;
229
+ padding: 0.5rem 0.625rem;
230
+ min-height: 44px;
227
231
  border: none;
228
232
  border-radius: 0.375rem;
229
233
  background: transparent;
@@ -777,6 +781,7 @@
777
781
  font-size: 0.8125rem;
778
782
  font-weight: 600;
779
783
  line-height: 1.25;
784
+ color: var(--turbo-text-primary);
780
785
  }
781
786
 
782
787
  .showcase-features {
@@ -0,0 +1,447 @@
1
+ /**
2
+ * Noir Editorial — design foundation tokens & shared patterns.
3
+ * Source: landing-designs.html Design #2 (.d2) via issue #512.
4
+ */
5
+
6
+ /* ==========================================================================
7
+ Design tokens
8
+ ========================================================================== */
9
+
10
+ /*
11
+ * Fallback faces with size-adjust so system fonts approximate webfont metrics.
12
+ * Paired with display=optional in BaseLayout (#729) to keep homepage CLS under
13
+ * the 0.1 CWV budget when Instrument Sans / Playfair swap (or never load).
14
+ */
15
+ @font-face {
16
+ font-family: 'Instrument Sans Fallback';
17
+ src: local('Arial'), local('Helvetica Neue'), local('Helvetica');
18
+ size-adjust: 102%;
19
+ ascent-override: 95%;
20
+ descent-override: 22%;
21
+ line-gap-override: 0%;
22
+ }
23
+
24
+ @font-face {
25
+ font-family: 'Playfair Display Fallback';
26
+ src: local('Times New Roman'), local('Times'), local('Georgia');
27
+ size-adjust: 108%;
28
+ ascent-override: 90%;
29
+ descent-override: 22%;
30
+ line-gap-override: 0%;
31
+ }
32
+
33
+ @font-face {
34
+ font-family: 'JetBrains Mono Fallback';
35
+ src: local('Menlo'), local('Consolas'), local('Monaco'), local('Courier New');
36
+ size-adjust: 100%;
37
+ ascent-override: 92%;
38
+ descent-override: 22%;
39
+ line-gap-override: 0%;
40
+ }
41
+
42
+ :root {
43
+ /* Fonts */
44
+ --font-display: 'Playfair Display', 'Playfair Display Fallback', Georgia, serif;
45
+ --font-body: 'Instrument Sans', 'Instrument Sans Fallback', system-ui, sans-serif;
46
+ --font-mono: 'JetBrains Mono', 'JetBrains Mono Fallback', ui-monospace, monospace;
47
+
48
+ /* Backgrounds */
49
+ --noir-bg-base: #0a0a0b;
50
+ --noir-bg-panel: #0e0e10;
51
+ --noir-bg-code: #111114;
52
+
53
+ /* Text */
54
+ --noir-text-primary: #e8e6e3;
55
+ --noir-text-secondary: #888;
56
+ --noir-text-muted: #7c7c7c;
57
+ /* AA normal text on --noir-bg-base (#0a0a0b): was #555 (~2.65:1) */
58
+ --noir-text-muted-deep: #8a8a8a;
59
+ --noir-text-eyebrow: #9a9a9a;
60
+ --noir-ghost: #222;
61
+ --noir-ghost-deep: #1a1a1c;
62
+
63
+ /* Accent */
64
+ --noir-accent: #cba6f7;
65
+ --noir-accent-glow: rgba(203, 166, 247, 0.2);
66
+
67
+ /* Borders */
68
+ --noir-border-section: #222;
69
+ --noir-border-row: #1a1a1c;
70
+ --noir-border-ghost: #333;
71
+
72
+ /* Motion */
73
+ --noir-ease: cubic-bezier(0.4, 0, 0.2, 1);
74
+ --noir-duration: 250ms;
75
+ --transition-fast: 200ms var(--noir-ease);
76
+ --transition-normal: 250ms var(--noir-ease);
77
+ --transition-slow: 300ms var(--noir-ease);
78
+
79
+ /*
80
+ * Theme gradients / radii / elevation come from site.css + turbo tokens.
81
+ * Do not flatten them here — that fought showcase/prod glam while Noir
82
+ * chrome remains temporarily (full Noir retirement is a separate task).
83
+ */
84
+ }
85
+
86
+ /* ==========================================================================
87
+ Global base (scoped to Noir design context)
88
+ ========================================================================== */
89
+
90
+ html[data-design='noir'] body {
91
+ margin: 0;
92
+ padding: 0;
93
+ font-family: var(--font-body);
94
+ font-size: 1rem;
95
+ line-height: 1.6;
96
+ color: var(--noir-text-primary);
97
+ background-color: var(--noir-bg-base);
98
+ -webkit-font-smoothing: antialiased;
99
+ -moz-osx-font-smoothing: grayscale;
100
+ /* No color/background transition: theme swaps would otherwise animate
101
+ through a mid-flight blend that fails WCAG AA under axe. */
102
+ }
103
+
104
+ html[data-design='noir'] h1,
105
+ html[data-design='noir'] h2,
106
+ html[data-design='noir'] h3,
107
+ html[data-design='noir'] .page-title {
108
+ font-family: var(--font-display);
109
+ font-weight: 900;
110
+ line-height: 1.1;
111
+ letter-spacing: -0.03em;
112
+ color: var(--noir-text-primary);
113
+ }
114
+
115
+ html:root h1,
116
+ html:root .page-title {
117
+ letter-spacing: -0.04em;
118
+ }
119
+
120
+ html:root h1 em,
121
+ html:root h2 em,
122
+ html:root h3 em {
123
+ font-style: italic;
124
+ font-weight: 400;
125
+ color: var(--turbo-brand-primary, var(--noir-accent));
126
+ }
127
+
128
+ html:root code,
129
+ html:root pre,
130
+ html:root kbd,
131
+ html:root samp {
132
+ font-family: var(--font-mono);
133
+ }
134
+
135
+ /*
136
+ * Content links follow the active theme accent (AA-normalized). Fixed
137
+ * `--noir-accent` is only ~2:1 on light theme bases. Header/footer keep
138
+ * their own noir ink via more-specific chrome selectors. Exclude `.btn`
139
+ * so component button colors win (#741).
140
+ */
141
+ html:root a:where(:not(.btn)) {
142
+ color: var(--turbo-accent-link, var(--noir-accent));
143
+ }
144
+
145
+ html:root a:where(:not(.btn)):hover {
146
+ color: var(--turbo-brand-primary, var(--noir-text-primary));
147
+ }
148
+
149
+ /* ==========================================================================
150
+ Header — editorial chrome
151
+ ========================================================================== */
152
+
153
+ html:root .header {
154
+ position: sticky;
155
+ top: 0;
156
+ z-index: 100;
157
+ background: var(--noir-bg-base);
158
+ backdrop-filter: none;
159
+ border-bottom: 1px solid var(--noir-border-section);
160
+ transition: border-color var(--transition-slow);
161
+ }
162
+
163
+ html:root .header-content {
164
+ display: flex;
165
+ align-items: center;
166
+ justify-content: space-between;
167
+ gap: 1rem;
168
+ padding: 0.875rem 1.5rem;
169
+ max-width: var(--container-wide, 1440px);
170
+ margin: 0 auto;
171
+ }
172
+
173
+ html:root .logo {
174
+ font-family: var(--font-display);
175
+ font-size: 1.125rem;
176
+ font-weight: 700;
177
+ letter-spacing: -0.02em;
178
+ color: var(--noir-text-primary);
179
+ text-decoration: none;
180
+ display: flex;
181
+ align-items: center;
182
+ gap: 0.5rem;
183
+ }
184
+
185
+ html:root .logo:hover {
186
+ color: var(--turbo-brand-primary, var(--noir-accent));
187
+ text-decoration: none;
188
+ }
189
+
190
+ html:root .logo-icon {
191
+ color: var(--turbo-brand-primary, var(--noir-accent));
192
+ font-size: 1rem;
193
+ line-height: 1;
194
+ }
195
+
196
+ html:root .logo-text {
197
+ text-transform: none;
198
+ }
199
+
200
+ /*
201
+ * Phone layout: let the brand wrap to two lines again. site.css pins
202
+ * `.logo` to `flex-shrink: 0` + `white-space: nowrap` (#742/#747) so the
203
+ * brand and nav cannot overlap on desktop; at 375px that keeps the brand
204
+ * on one line and pushes the theme selector off the right edge of the
205
+ * viewport. Noir chrome loads after site.css, so relaxing it here below
206
+ * the 768px breakpoint restores the wrapped phone brand while desktop
207
+ * keeps the no-shrink rule.
208
+ */
209
+ @media (max-width: 767px) {
210
+ html:root .logo {
211
+ flex-shrink: 1;
212
+ white-space: normal;
213
+ }
214
+ }
215
+
216
+ /*
217
+ * Do not set `display` here — site.css owns desktop flex + the mobile
218
+ * `display: none` / `.open` drawer. Noir loads after site.css; an
219
+ * unconditional `display: flex` kept the mobile menu permanently open
220
+ * and hid the homepage behind the drawer.
221
+ */
222
+ html:root .nav-links {
223
+ align-items: center;
224
+ gap: 0.25rem;
225
+ list-style: none;
226
+ margin: 0;
227
+ padding: 0;
228
+ }
229
+
230
+ /* Higher specificity than site.css theme-token nav colors so light themes
231
+ cannot paint dark secondary text onto the fixed noir header (#0a0a0b). */
232
+ html[data-design='noir'] .header .nav-link,
233
+ html[data-design='noir'] .header .nav-dropdown-trigger {
234
+ display: inline-flex;
235
+ align-items: center;
236
+ gap: 0.35rem;
237
+ padding: 0.5rem 0.75rem;
238
+ min-height: 44px;
239
+ font-family: var(--font-body);
240
+ font-size: 0.75rem;
241
+ font-weight: 600;
242
+ letter-spacing: 0.12em;
243
+ text-transform: uppercase;
244
+ color: var(--noir-text-muted);
245
+ text-decoration: none;
246
+ background: none;
247
+ border: none;
248
+ border-radius: var(--radius-md);
249
+ cursor: pointer;
250
+ /* No color/background transition — see the body rule above. */
251
+ }
252
+
253
+ html[data-design='noir'] .header .nav-link:hover,
254
+ html[data-design='noir'] .header .nav-dropdown-trigger:hover {
255
+ color: var(--noir-text-primary);
256
+ background: transparent;
257
+ text-decoration: none;
258
+ }
259
+
260
+ /*
261
+ * The Noir header is fixed dark chrome (#0a0a0b), so the active pill keeps
262
+ * Noir ink like the sibling rules above. Light themes' --turbo-brand-primary
263
+ * is tuned for a light page background and lands at ~3.4-3.7:1 here, which
264
+ * fails AA (axe flags a[data-testid="nav-home"] under catppuccin-latte,
265
+ * github-light/dark, everforest-light-soft, rose-pine-dawn, tokyo-night-light).
266
+ */
267
+ html[data-design='noir'] .header .nav-link.active {
268
+ color: var(--noir-accent);
269
+ background: color-mix(in srgb, var(--noir-accent) 14%, transparent);
270
+ }
271
+
272
+ html:root .menu-toggle {
273
+ color: var(--noir-text-primary);
274
+ }
275
+
276
+ html:root .menu-toggle:hover {
277
+ background: var(--noir-bg-panel);
278
+ }
279
+
280
+ html:root .nav-dropdown-menu {
281
+ background: var(--noir-bg-panel);
282
+ border: 1px solid var(--noir-border-section);
283
+ border-radius: var(--radius-lg);
284
+ box-shadow: var(--shadow-lg);
285
+ padding: 0.25rem 0;
286
+ }
287
+
288
+ html:root .nav-dropdown-item {
289
+ color: var(--noir-text-secondary);
290
+ border-radius: var(--radius-md);
291
+ font-size: 0.8125rem;
292
+ letter-spacing: 0.04em;
293
+ }
294
+
295
+ html:root .nav-dropdown-item:hover {
296
+ color: var(--noir-text-primary);
297
+ background: var(--noir-bg-code);
298
+ }
299
+
300
+ html:root .nav-controls {
301
+ display: flex;
302
+ align-items: center;
303
+ gap: 0.75rem;
304
+ flex: 1;
305
+ justify-content: flex-end;
306
+ max-width: 500px;
307
+ }
308
+
309
+ /* Theme / search chrome — muted panel, theme radii */
310
+ html[data-design='noir'] .header .theme-trigger,
311
+ html[data-design='noir'] .header .search-bar-inner {
312
+ border-color: var(--noir-border-ghost);
313
+ background: var(--noir-bg-panel);
314
+ color: var(--noir-text-secondary);
315
+ }
316
+
317
+ html:root .theme-menu,
318
+ html:root .search-menu {
319
+ border-color: var(--noir-border-section);
320
+ background: var(--noir-bg-panel);
321
+ box-shadow: var(--shadow-lg);
322
+ }
323
+
324
+ /* ==========================================================================
325
+ Footer — editorial chrome
326
+ ========================================================================== */
327
+
328
+ html[data-design='noir'] .footer {
329
+ padding: 0;
330
+ background: var(--noir-bg-base);
331
+ border-top: 1px solid var(--noir-border-section);
332
+ margin-top: var(--space-4xl, 6rem);
333
+ }
334
+
335
+ html:root .footer-content {
336
+ display: grid;
337
+ grid-template-columns: 1.4fr 1fr 1fr 1fr;
338
+ gap: 2.5rem;
339
+ align-items: start;
340
+ padding: 3.75rem 1.5rem;
341
+ max-width: var(--container-wide, 1440px);
342
+ margin: 0 auto;
343
+ }
344
+
345
+ html:root .footer-brand {
346
+ display: flex;
347
+ flex-direction: column;
348
+ gap: 0.75rem;
349
+ }
350
+
351
+ html:root .footer-brand-row {
352
+ display: flex;
353
+ align-items: center;
354
+ gap: 0.5rem;
355
+ }
356
+
357
+ html[data-design='noir'] .footer .footer-logo {
358
+ color: var(--turbo-brand-primary, var(--noir-accent));
359
+ font-size: 1rem;
360
+ line-height: 1;
361
+ }
362
+
363
+ html[data-design='noir'] .footer .footer-title {
364
+ font-family: var(--font-display);
365
+ font-size: 1.125rem;
366
+ font-weight: 700;
367
+ letter-spacing: -0.02em;
368
+ color: var(--noir-text-primary);
369
+ }
370
+
371
+ html[data-design='noir'] .footer .footer-tagline {
372
+ margin: 0;
373
+ max-width: 28ch;
374
+ font-size: 0.875rem;
375
+ line-height: 1.5;
376
+ color: var(--noir-text-secondary);
377
+ }
378
+
379
+ html:root .footer-col {
380
+ display: flex;
381
+ flex-direction: column;
382
+ gap: 0.75rem;
383
+ }
384
+
385
+ html[data-design='noir'] .footer .footer-col-label {
386
+ margin: 0 0 0.25rem;
387
+ font-family: var(--font-body);
388
+ font-size: 0.6875rem;
389
+ font-weight: 600;
390
+ letter-spacing: 0.15em;
391
+ text-transform: uppercase;
392
+ color: var(--noir-text-eyebrow);
393
+ }
394
+
395
+ html[data-design='noir'] .footer .footer-col a,
396
+ html[data-design='noir'] .footer .footer-links a {
397
+ color: var(--noir-text-secondary);
398
+ font-size: 0.875rem;
399
+ text-decoration: none;
400
+ }
401
+
402
+ html[data-design='noir'] .footer .footer-col a:hover,
403
+ html[data-design='noir'] .footer .footer-links a:hover {
404
+ color: var(--noir-text-primary);
405
+ text-decoration: none;
406
+ }
407
+
408
+ html[data-design='noir'] .footer .footer-bottom {
409
+ grid-column: 1 / -1;
410
+ display: flex;
411
+ flex-wrap: wrap;
412
+ align-items: center;
413
+ justify-content: space-between;
414
+ gap: 0.75rem;
415
+ padding-top: 1.5rem;
416
+ border-top: 1px solid var(--noir-border-row);
417
+ }
418
+
419
+ html[data-design='noir'] .footer .footer-copyright {
420
+ margin: 0;
421
+ font-size: 0.75rem;
422
+ letter-spacing: 0.06em;
423
+ text-transform: uppercase;
424
+ color: var(--noir-text-muted);
425
+ }
426
+
427
+ html[data-design='noir'] .footer .footer-separator {
428
+ color: var(--noir-text-muted-deep);
429
+ }
430
+
431
+ @media (max-width: 900px) {
432
+ html:root .footer-content {
433
+ grid-template-columns: 1fr 1fr;
434
+ gap: 2rem;
435
+ padding: 2.5rem 1.25rem;
436
+ }
437
+
438
+ html:root .footer-brand {
439
+ grid-column: 1 / -1;
440
+ }
441
+ }
442
+
443
+ @media (max-width: 600px) {
444
+ html:root .footer-content {
445
+ grid-template-columns: 1fr;
446
+ }
447
+ }
data/assets/css/site.css CHANGED
@@ -140,14 +140,17 @@ p {
140
140
  margin: 0 0 var(--space-md);
141
141
  }
142
142
 
143
- a {
144
- color: var(--turbo-brand-primary);
143
+ /* Exclude `.btn` anchors so component button colors win over chrome link ink
144
+ (same pattern as noir.css / #741). `a:hover` must not paint brand-primary
145
+ text onto primary CTAs. Do not transition `color` — theme swaps would
146
+ animate through mid-flight ink that fails WCAG AA / axe scans. */
147
+ a:where(:not(.btn)) {
148
+ color: var(--turbo-accent-link, var(--turbo-brand-primary));
145
149
  text-decoration: none;
146
- transition: color var(--transition-fast);
147
150
  }
148
151
 
149
- a:hover {
150
- color: var(--turbo-accent-link, var(--turbo-brand-primary));
152
+ a:where(:not(.btn)):hover {
153
+ color: var(--turbo-brand-primary, var(--turbo-accent-link));
151
154
  }
152
155
 
153
156
  /* Links in text need underlines for accessibility (WCAG link-in-text-block) */
@@ -367,6 +370,7 @@ html:root .nav-dropdown-trigger {
367
370
  align-items: center;
368
371
  gap: var(--space-xs);
369
372
  padding: var(--space-sm) var(--space-md);
373
+ min-height: 44px;
370
374
  font-size: 0.9375rem;
371
375
  font-weight: 500;
372
376
  color: var(--turbo-text-secondary);
@@ -501,6 +505,8 @@ html:root .theme-trigger {
501
505
  align-items: center;
502
506
  gap: var(--space-sm);
503
507
  padding: var(--space-sm) var(--space-md);
508
+ min-height: 44px;
509
+ min-width: 44px;
504
510
  font-size: 0.875rem;
505
511
  font-weight: 500;
506
512
  color: var(--turbo-text-primary);
@@ -544,16 +550,15 @@ html:root .theme-menu {
544
550
  border: 1px solid var(--turbo-border-default);
545
551
  border-radius: var(--radius-lg);
546
552
  box-shadow: var(--shadow-lg);
547
- opacity: 0;
548
- visibility: hidden;
553
+ /* display:none when closed so axe/AT skip offscreen menu ink (opacity:0 still scans) */
554
+ display: none;
549
555
  transform: translateY(-8px);
550
- transition: all var(--transition-fast);
556
+ transition: transform var(--transition-fast);
551
557
  z-index: 100;
552
558
  }
553
559
 
554
560
  html:root .theme-dropdown.open .theme-menu {
555
- opacity: 1;
556
- visibility: visible;
561
+ display: block;
557
562
  transform: translateY(0);
558
563
  }
559
564
 
@@ -579,6 +584,7 @@ html:root .theme-option {
579
584
  align-items: center;
580
585
  gap: var(--space-sm);
581
586
  width: 100%;
587
+ min-height: 44px;
582
588
  padding: var(--space-sm) var(--space-sm);
583
589
  font-size: 0.875rem;
584
590
  font-weight: 500;
@@ -3262,7 +3268,10 @@ html:root .footer-links a:hover {
3262
3268
  margin-bottom: 1.25rem;
3263
3269
  font-family: var(--turbo-font-mono);
3264
3270
  font-size: 1.0625rem;
3265
- color: color-mix(in srgb, var(--turbo-brand-primary) 60%, var(--turbo-text-primary));
3271
+ color: var(--turbo-text-primary);
3272
+ /* Reset global/inline-code chip backgrounds so primary ink stays AA */
3273
+ background: transparent;
3274
+ padding: 0;
3266
3275
  }
3267
3276
 
3268
3277
  @media (max-width: 900px) {
@@ -3303,6 +3312,17 @@ html:root .footer-links a:hover {
3303
3312
  opacity: 1;
3304
3313
  transform: none;
3305
3314
  }
3315
+
3316
+ /* Theme swaps change CSS variables; lingering transitions otherwise leave
3317
+ text/background mid-blend under axe contrast checks. */
3318
+ *,
3319
+ *::before,
3320
+ *::after {
3321
+ animation-duration: 0.01ms !important;
3322
+ animation-iteration-count: 1 !important;
3323
+ transition-duration: 0.01ms !important;
3324
+ scroll-behavior: auto !important;
3325
+ }
3306
3326
  }
3307
3327
 
3308
3328
  /* ==========================================================================
@@ -23,13 +23,18 @@
23
23
  --turbo-bg-surface: #141821; /* color */
24
24
  --turbo-bg-overlay: #1b1f29; /* color */
25
25
  --turbo-text-primary: #bfbdb6; /* color */
26
- --turbo-text-secondary: #5b6876; /* color */
26
+ --turbo-text-secondary: #7c8691; /* color */
27
27
  --turbo-text-inverse: #0d1017; /* color */
28
28
  --turbo-brand-primary: #e6b450; /* color */
29
+ --turbo-brand-primary-text: #0d1017; /* color */
29
30
  --turbo-state-info: #39bae6; /* color */
30
31
  --turbo-state-success: #aad94c; /* color */
31
32
  --turbo-state-warning: #ff8f40; /* color */
32
33
  --turbo-state-danger: #d95757; /* color */
34
+ --turbo-state-info-text: #0d1017; /* color */
35
+ --turbo-state-success-text: #0d1017; /* color */
36
+ --turbo-state-warning-text: #0d1017; /* color */
37
+ --turbo-state-danger-text: #0d1017; /* color */
33
38
  --turbo-border-default: #1b1f29; /* color */
34
39
  --turbo-accent-link: #ffb454; /* color */
35
40
  --turbo-font-sans: Inter, ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji"; /* fontFamily */
@@ -41,7 +46,7 @@
41
46
  --turbo-heading-h5: #d2a6ff; /* color */
42
47
  --turbo-heading-h6: #ff8f40; /* color */
43
48
  --turbo-body-primary: #bfbdb6; /* color */
44
- --turbo-body-secondary: #5b6876; /* color */
49
+ --turbo-body-secondary: #7c8691; /* color */
45
50
  --turbo-link-default: #ffb454; /* color */
46
51
  --turbo-selection-fg: #bfbdb6; /* color */
47
52
  --turbo-selection-bg: #1c212b; /* color */
@@ -23,29 +23,31 @@
23
23
  --turbo-bg-surface: #fafafa; /* color */
24
24
  --turbo-bg-overlay: #e7eaed; /* color */
25
25
  --turbo-text-primary: #5c6166; /* color */
26
- --turbo-text-secondary: #828e9f; /* color */
26
+ --turbo-text-secondary: #656f7c; /* color */
27
27
  --turbo-text-inverse: #f8f9fa; /* color */
28
28
  --turbo-brand-primary: #f29718; /* color */
29
+ --turbo-brand-primary-text: #343435; /* color */
29
30
  --turbo-state-info: #55b4d4; /* color */
30
31
  --turbo-state-success: #86b300; /* color */
31
32
  --turbo-state-warning: #ff7e33; /* color */
32
33
  --turbo-state-danger: #e65050; /* color */
33
- --turbo-state-info-text: #000000; /* color */
34
- --turbo-state-success-text: #000000; /* color */
35
- --turbo-state-warning-text: #000000; /* color */
34
+ --turbo-state-info-text: #39393a; /* color */
35
+ --turbo-state-success-text: #373737; /* color */
36
+ --turbo-state-warning-text: #343435; /* color */
37
+ --turbo-state-danger-text: #161617; /* color */
36
38
  --turbo-border-default: #e7eaed; /* color */
37
- --turbo-accent-link: #a37acc; /* color */
39
+ --turbo-accent-link: #8160a1; /* color */
38
40
  --turbo-font-sans: Inter, ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji"; /* fontFamily */
39
41
  --turbo-font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; /* fontFamily */
40
- --turbo-heading-h1: #f2a300; /* color */
41
- --turbo-heading-h2: #399ee6; /* color */
42
- --turbo-heading-h3: #55b4d4; /* color */
43
- --turbo-heading-h4: #86b300; /* color */
42
+ --turbo-heading-h1: #c28200; /* color */
43
+ --turbo-heading-h2: #3696db; /* color */
44
+ --turbo-heading-h3: #4899b4; /* color */
45
+ --turbo-heading-h4: #759c00; /* color */
44
46
  --turbo-heading-h5: #a37acc; /* color */
45
- --turbo-heading-h6: #ff7e33; /* color */
47
+ --turbo-heading-h6: #e3702d; /* color */
46
48
  --turbo-body-primary: #5c6166; /* color */
47
- --turbo-body-secondary: #828e9f; /* color */
48
- --turbo-link-default: #a37acc; /* color */
49
+ --turbo-body-secondary: #656f7c; /* color */
50
+ --turbo-link-default: #8160a1; /* color */
49
51
  --turbo-selection-fg: #5c6166; /* color */
50
52
  --turbo-selection-bg: #e5e9ed; /* color */
51
53
  --turbo-blockquote-border: #e7eaed; /* color */