@signal9/era-ui 4.14.0 → 4.15.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.
@@ -1028,6 +1028,7 @@ after the pane's own. Pass Compartments for reactive plugin sets. |
1028
1028
  | `onDragEnd?` | `(data: DragEventData) => void` | — | — |
1029
1029
  | `onClose?` | `() => void` | — | When set, a close button renders at the right of the handle bar. |
1030
1030
  | `header?` | `Snippet` | — | Extra handle-bar content, rendered after the title. |
1031
+ | `center?` | `Snippet` | — | Content centred on the bar itself — Pane.Handle's three-track grid. |
1031
1032
  | `children?` | `Snippet` | — | — |
1032
1033
 
1033
1034
  <!-- end: pane -->
@@ -194,7 +194,7 @@
194
194
  "slug": "pane",
195
195
  "title": "Pane",
196
196
  "summary": "A draggable floating window with a handle bar.",
197
- "tokenEstimate": 416,
197
+ "tokenEstimate": 441,
198
198
  "sections": [
199
199
  "Import",
200
200
  "Props"
@@ -29,4 +29,5 @@ after the pane's own. Pass Compartments for reactive plugin sets. |
29
29
  | `onDragEnd?` | `(data: DragEventData) => void` | — | — |
30
30
  | `onClose?` | `() => void` | — | When set, a close button renders at the right of the handle bar. |
31
31
  | `header?` | `Snippet` | — | Extra handle-bar content, rendered after the title. |
32
+ | `center?` | `Snippet` | — | Content centred on the bar itself — Pane.Handle's three-track grid. |
32
33
  | `children?` | `Snippet` | — | — |
@@ -150,6 +150,14 @@ export interface AppDefinition {
150
150
  * live AppPane too, so a host can swap it as the pane's content
151
151
  * changes. */
152
152
  actions?: Snippet;
153
+ /** Replaces the default icon+title cluster on the LEFT of the title bar.
154
+ * Assignable live, like `actions`. */
155
+ lead?: Snippet;
156
+ /** Content centred on the title bar itself (a document title, a breadcrumb) —
157
+ * rendered in Pane.Handle's three-track grid, so it stays pinned to the true
158
+ * centre of the bar however the sides grow. Assignable live. Interactive
159
+ * content in it must carry data-pane-control (the bar is the drag zone). */
160
+ center?: Snippet;
153
161
  }
154
162
  /** A live pane instance in the registry. */
155
163
  export interface AppPane {
@@ -173,6 +181,10 @@ export interface AppPane {
173
181
  minimizable: boolean;
174
182
  bodyClass?: string;
175
183
  actions?: Snippet;
184
+ /** See AppDefinition — both assignable on the live pane, which is how an
185
+ * app claims its own chrome: `pane.center = mySnippet` from the body. */
186
+ lead?: Snippet;
187
+ center?: Snippet;
176
188
  /** App-specific state, carried verbatim through a snapshot. */
177
189
  props?: JsonValue;
178
190
  }
@@ -183,6 +183,8 @@ export class PaneManager {
183
183
  minimizable: app.minimizable ?? true,
184
184
  bodyClass: app.bodyClass,
185
185
  actions: app.actions,
186
+ lead: app.lead,
187
+ center: app.center,
186
188
  props: app.props
187
189
  };
188
190
  this.panes.push(win);
@@ -364,6 +366,8 @@ export class PaneManager {
364
366
  minimizable: app.minimizable ?? true,
365
367
  bodyClass: app.bodyClass,
366
368
  actions: app.actions,
369
+ lead: app.lead,
370
+ center: app.center,
367
371
  props: s.props ?? app.props
368
372
  });
369
373
  }
@@ -92,14 +92,21 @@
92
92
  from the edge against 5px above and below it — read as crowded because it
93
93
  was. --era-inset-md is (h-md − text)/2, so the label now starts the same
94
94
  distance in from the edge as it sits from the top. -->
95
- <Pane.Handle class="gap-(--era-gap) pl-(--era-inset-md)">
96
- {#if pane.icon}
97
- {@const Icon = pane.icon}
98
- <Icon class="size-(--era-h-xs) shrink-0 text-muted" />
95
+ <Pane.Handle class="gap-(--era-gap) pl-(--era-inset-md)" center={pane.center}>
96
+ {#if pane.lead}
97
+ <!-- The app replaced the whole icon+title cluster. Its content lives in
98
+ the drag zone like the default one; anything interactive in it
99
+ carries data-pane-control itself. -->
100
+ {@render pane.lead()}
101
+ {:else}
102
+ {#if pane.icon}
103
+ {@const Icon = pane.icon}
104
+ <Icon class="size-(--era-h-xs) shrink-0 text-muted" />
105
+ {/if}
106
+ <span class={cn('truncate text-body', focused ? 'text-bright' : 'text-muted')}
107
+ >{pane.title}</span
108
+ >
99
109
  {/if}
100
- <span class={cn('truncate text-body', focused ? 'text-bright' : 'text-muted')}
101
- >{pane.title}</span
102
- >
103
110
  <!-- Close / minimize / maximize. Deliberately NOT data-pane-control (same as
104
111
  the raw Pane.Close): a left press-and-HOLD drags the pane — neodrag only
105
112
  drags the primary button and suppresses the trailing click after a real
@@ -109,7 +116,7 @@
109
116
  rhythm as the rest of the handle. Like the close button they carry no
110
117
  data-pane-control, so a press-and-hold anywhere on the bar still drags
111
118
  the pane and only a clean click activates them. -->
112
- <div class="ml-auto flex shrink-0 items-center gap-(--era-gap)">
119
+ {#snippet trail()}
113
120
  {@render pane.actions?.()}
114
121
  <!-- The macOS traffic lights, one button: red = close at rest, and the
115
122
  held preview recolours to the action it will commit — yellow for
@@ -158,7 +165,7 @@
158
165
  <X />
159
166
  {/if}
160
167
  </Button>
161
- </div>
168
+ {/snippet}
162
169
  </Pane.Handle>
163
170
 
164
171
  <Pane.Content class={pane.bodyClass}>
@@ -31,38 +31,72 @@
31
31
  .dark {
32
32
  color-scheme: dark;
33
33
 
34
- --color-1: oklch(0.162 0 0);
35
- --color-2: oklch(0.195 0 0);
36
- --color-3: oklch(0.254 0 0);
37
- --color-4: oklch(0.302 0 0);
38
- --color-5: oklch(0.348 0 0);
39
- --color-6: oklch(0.396 0 0);
40
- --color-7: oklch(0.459 0 0);
41
- --color-8: oklch(0.549 0 0);
42
- --color-9: oklch(0.649 0 0);
43
- --color-10: oklch(0.72 0 0);
44
- --color-11: oklch(0.863 0 0);
45
- --color-12: oklch(0.933 0 0);
34
+ --color-1: oklch(0.162 0 none);
35
+ --color-2: oklch(0.195 0 none);
36
+ --color-3: oklch(0.254 0 none);
37
+ --color-4: oklch(0.302 0 none);
38
+ --color-5: oklch(0.348 0 none);
39
+ --color-6: oklch(0.396 0 none);
40
+ --color-7: oklch(0.459 0 none);
41
+ --color-8: oklch(0.549 0 none);
42
+ --color-9: oklch(0.649 0 none);
43
+ --color-10: oklch(0.72 0 none);
44
+ --color-11: oklch(0.863 0 none);
45
+ --color-12: oklch(0.933 0 none); /* hue `none`, not 0 — see below */
46
46
 
47
47
  /* Semantic tokens */
48
48
  --color-border: var(--color-5);
49
49
  --color-surface: var(--color-2);
50
- --color-hover: oklch(0.25 0 0);
50
+ --color-hover: oklch(0.25 0 none);
51
51
  --color-fg: var(--color-11);
52
- --color-bright: var(--color-12);
52
+ /* EMPHASIS INK IS TINTED BY THE THEME, not pinned to the top of the ramp.
53
+ *
54
+ * Step 12 is the ramp's extreme, and at the extreme a neutral ramp has run out
55
+ * of colour — so `bright` came out pure white on the dark themes and pure
56
+ * black on signalnine light. That is the one place a themed UI should NOT go
57
+ * achromatic: bright is the emphasis ink (headings, titles, the sentence you
58
+ * are meant to read first), so it is the text carrying the most identity and
59
+ * it was carrying none.
60
+ *
61
+ * It now takes 12% of --color-primary — the theme's own accent, which is also
62
+ * what the accent picker writes, so choosing an accent tints the emphasis text
63
+ * with it live. 12% is a cast, not a colour: it moves lightness by ~0.02 (the
64
+ * default dark page keeps 11.6:1) and lands chroma near 0.013, which reads as
65
+ * "warm white" or "blue-black" rather than as a coloured heading.
66
+ *
67
+ * A theme whose accent IS neutral (the default) is unchanged by construction —
68
+ * there is no hue to lend — which is the correct behaviour rather than an
69
+ * exception.
70
+ *
71
+ * The achromatic step 12s are written with hue `none` rather than hue 0, and
72
+ * that is load-bearing: hue 0 is a REAL hue (red), so oklch interpolation
73
+ * walks it round to the accent's the short way and a black-plus-blue mix came
74
+ * out magenta (signalnine light's #000 landed on hue 349). `none` marks the
75
+ * hue as powerless — which it is at chroma 0 — so the mix adopts the accent's
76
+ * hue instead of travelling to it. The colours themselves are unchanged
77
+ * everywhere else: at chroma 0 the hue paints nothing. */
78
+ --color-bright: color-mix(in oklch, var(--color-12) 88%, var(--color-primary));
53
79
  /* Muted is body copy, not a de-emphasis wash — the /text page sets whole
54
- * paragraphs in it — so it does NOT sit flat on step 10: it leans 30% of the
55
- * way to --color-fg. Step 10 alone read as "greyed out" at paragraph length
56
- * (7.80:1 on the default dark page); the lean lands it at 9.11:1, still a
57
- * clear step below fg for the mixed-tone case (muted prose beside a bright
58
- * literal) but no longer receding. The lean is toward fg rather than a fixed
59
- * lightness so it mirrors correctly in light mode (muted DARKENS toward fg)
60
- * and keeps each theme's own hue family — the tinted scales (adobe, sand)
61
- * interpolate between two steps of their own ramp, never toward neutral.
80
+ * paragraphs in it — so it does NOT sit flat on step 10: it leans 40% of the
81
+ * way to --color-bright. Step 10 alone read as "greyed out" at paragraph
82
+ * length (7.80:1 on the default dark page); the lean lands it at 10.43:1, and
83
+ * the ordering muted < fg < bright still holds by a clear margin (0.80 / 0.86
84
+ * / 0.93 in dark), so the mixed-tone case muted prose beside a bright
85
+ * literal keeps all three of its levels.
86
+ *
87
+ * It aims at BRIGHT rather than fg on purpose: fg is the ink of controls and
88
+ * labels, bright is the ink of the sentence you are meant to read, and body
89
+ * copy belongs with the second one. It also means muted inherits the theme's
90
+ * tint through bright, so prose is never a neutral grey on a themed page.
91
+ *
92
+ * The lean is toward a TOKEN rather than a fixed lightness so it mirrors
93
+ * correctly in light mode (muted DARKENS toward bright) and keeps each theme's
94
+ * own hue family — the tinted scales interpolate between two steps of their
95
+ * own ramp, never toward neutral.
62
96
  *
63
97
  * signalnine sets its muted by hand from measured source colours and is
64
98
  * deliberately exempt. */
65
- --color-muted: color-mix(in oklch, var(--color-10) 70%, var(--color-fg));
99
+ --color-muted: color-mix(in oklch, var(--color-10) 60%, var(--color-bright));
66
100
  /* Inline link ink. Declared once here and inherited by every theme —
67
101
  * var(--color-bright) resolves in the element's own context, so a theme only
68
102
  * names this if it wants links to differ from emphasis text (signalnine
@@ -72,7 +106,7 @@
72
106
  /* Accent tokens — override these to theme the library */
73
107
  --color-primary: var(--color-fg);
74
108
  --color-primary-fg: var(--color-1);
75
- --color-primary-hover: color-mix(in oklch, var(--color-primary) 85%, black);
109
+ --color-primary-hover: color-mix(in oklch, var(--color-primary) 85%, oklch(0 0 none));
76
110
  /* Destructive carries LIGHT text — a white glyph on the red fill.
77
111
  *
78
112
  * The reference terminal app's danger red, matched exactly: era had landed on
@@ -89,7 +123,7 @@
89
123
  * needs but not the 4.5:1 body copy would. Pushing the red LIGHTER widens
90
124
  * that gap; pushing it darker closes it. */
91
125
  --color-destructive: oklch(0.65 0.2 25); /* #f14d4c */
92
- --color-destructive-fg: oklch(0.96 0 0);
126
+ --color-destructive-fg: oklch(0.96 0 none);
93
127
  /* The semantic trio is one family: same lightness and chroma as destructive,
94
128
  * varying only in hue, so a success fill and a destructive fill carry the same
95
129
  * weight — which is what makes the pane's traffic lights read as three lights
@@ -103,9 +137,9 @@
103
137
  * the gap: measured against the page, the three now sit at 5.9 / 7.0 / 9.3
104
138
  * where they sat at 5.9 / 7.0 / 11.1. */
105
139
  --color-success: oklch(0.65 0.2 145);
106
- --color-success-fg: oklch(0.96 0 0);
140
+ --color-success-fg: oklch(0.96 0 none);
107
141
  --color-warning: oklch(0.75 0.15 85);
108
- --color-warning-fg: oklch(0.162 0 0);
142
+ --color-warning-fg: oklch(0.162 0 none);
109
143
  }
110
144
 
111
145
  /* -------------------------------------------------- */
@@ -115,75 +149,75 @@
115
149
  :root:not(.dark) {
116
150
  color-scheme: light;
117
151
 
118
- --color-1: oklch(0.933 0 0);
119
- --color-2: oklch(0.863 0 0);
120
- --color-3: oklch(0.72 0 0);
121
- --color-4: oklch(0.649 0 0);
122
- --color-5: oklch(0.549 0 0);
123
- --color-6: oklch(0.459 0 0);
124
- --color-7: oklch(0.396 0 0);
125
- --color-8: oklch(0.348 0 0);
126
- --color-9: oklch(0.302 0 0);
127
- --color-10: oklch(0.254 0 0);
128
- --color-11: oklch(0.195 0 0);
129
- --color-12: oklch(0.162 0 0);
152
+ --color-1: oklch(0.933 0 none);
153
+ --color-2: oklch(0.863 0 none);
154
+ --color-3: oklch(0.72 0 none);
155
+ --color-4: oklch(0.649 0 none);
156
+ --color-5: oklch(0.549 0 none);
157
+ --color-6: oklch(0.459 0 none);
158
+ --color-7: oklch(0.396 0 none);
159
+ --color-8: oklch(0.348 0 none);
160
+ --color-9: oklch(0.302 0 none);
161
+ --color-10: oklch(0.254 0 none);
162
+ --color-11: oklch(0.195 0 none);
163
+ --color-12: oklch(0.162 0 none); /* hue `none`, not 0 — see below */
130
164
 
131
165
  --color-border: var(--color-5);
132
166
  --color-surface: var(--color-2);
133
- --color-hover: oklch(0.8 0 0);
167
+ --color-hover: oklch(0.8 0 none);
134
168
  --color-fg: var(--color-11);
135
- --color-bright: var(--color-12);
136
- --color-muted: color-mix(in oklch, var(--color-10) 70%, var(--color-fg));
169
+ --color-bright: color-mix(in oklch, var(--color-12) 88%, var(--color-primary));
170
+ --color-muted: color-mix(in oklch, var(--color-10) 60%, var(--color-bright));
137
171
 
138
172
  /* Accent tokens */
139
173
  --color-primary: var(--color-fg);
140
174
  --color-primary-fg: var(--color-1);
141
- --color-primary-hover: color-mix(in oklch, var(--color-primary) 85%, black);
175
+ --color-primary-hover: color-mix(in oklch, var(--color-primary) 85%, oklch(0 0 none));
142
176
  --color-destructive: oklch(0.65 0.2 25); /* #f14d4c */
143
- --color-destructive-fg: oklch(0.98 0 0);
177
+ --color-destructive-fg: oklch(0.98 0 none);
144
178
  /* Chroma matched to destructive like the dark theme; the LIGHTNESSES stay
145
179
  * where they are, because here they carry white text — green at the red's
146
180
  * 0.65 drops to 2.99:1 against white, under the 3:1 a filled glyph needs. */
147
181
  --color-success: oklch(0.5 0.2 145);
148
- --color-success-fg: oklch(0.98 0 0);
182
+ --color-success-fg: oklch(0.98 0 none);
149
183
  --color-warning: oklch(0.72 0.15 85);
150
- --color-warning-fg: oklch(0.162 0 0);
184
+ --color-warning-fg: oklch(0.162 0 none);
151
185
  }
152
186
  }
153
187
 
154
188
  .light {
155
189
  color-scheme: light;
156
190
 
157
- --color-1: oklch(0.933 0 0);
158
- --color-2: oklch(0.863 0 0);
159
- --color-3: oklch(0.72 0 0);
160
- --color-4: oklch(0.649 0 0);
161
- --color-5: oklch(0.549 0 0);
162
- --color-6: oklch(0.459 0 0);
163
- --color-7: oklch(0.396 0 0);
164
- --color-8: oklch(0.348 0 0);
165
- --color-9: oklch(0.302 0 0);
166
- --color-10: oklch(0.254 0 0);
167
- --color-11: oklch(0.195 0 0);
168
- --color-12: oklch(0.162 0 0);
191
+ --color-1: oklch(0.933 0 none);
192
+ --color-2: oklch(0.863 0 none);
193
+ --color-3: oklch(0.72 0 none);
194
+ --color-4: oklch(0.649 0 none);
195
+ --color-5: oklch(0.549 0 none);
196
+ --color-6: oklch(0.459 0 none);
197
+ --color-7: oklch(0.396 0 none);
198
+ --color-8: oklch(0.348 0 none);
199
+ --color-9: oklch(0.302 0 none);
200
+ --color-10: oklch(0.254 0 none);
201
+ --color-11: oklch(0.195 0 none);
202
+ --color-12: oklch(0.162 0 none); /* hue `none`, not 0 — see below */
169
203
 
170
204
  --color-border: var(--color-5);
171
205
  --color-surface: var(--color-2);
172
- --color-hover: oklch(0.8 0 0);
206
+ --color-hover: oklch(0.8 0 none);
173
207
  --color-fg: var(--color-11);
174
- --color-bright: var(--color-12);
175
- --color-muted: color-mix(in oklch, var(--color-10) 70%, var(--color-fg));
208
+ --color-bright: color-mix(in oklch, var(--color-12) 88%, var(--color-primary));
209
+ --color-muted: color-mix(in oklch, var(--color-10) 60%, var(--color-bright));
176
210
 
177
211
  /* Accent tokens */
178
212
  --color-primary: var(--color-fg);
179
213
  --color-primary-fg: var(--color-1);
180
- --color-primary-hover: color-mix(in oklch, var(--color-primary) 85%, black);
214
+ --color-primary-hover: color-mix(in oklch, var(--color-primary) 85%, oklch(0 0 none));
181
215
  --color-destructive: oklch(0.65 0.2 25); /* #f14d4c */
182
- --color-destructive-fg: oklch(0.98 0 0);
216
+ --color-destructive-fg: oklch(0.98 0 none);
183
217
  --color-success: oklch(0.5 0.17 145);
184
- --color-success-fg: oklch(0.98 0 0);
218
+ --color-success-fg: oklch(0.98 0 none);
185
219
  --color-warning: oklch(0.72 0.15 85);
186
- --color-warning-fg: oklch(0.162 0 0);
220
+ --color-warning-fg: oklch(0.162 0 none);
187
221
  }
188
222
 
189
223
  /* -------------------------------------------------- */
@@ -215,8 +249,8 @@
215
249
  --color-surface: var(--color-2);
216
250
  --color-hover: oklch(0.252 0.024 56);
217
251
  --color-fg: var(--color-11);
218
- --color-bright: var(--color-12);
219
- --color-muted: color-mix(in oklch, var(--color-10) 70%, var(--color-fg));
252
+ --color-bright: color-mix(in oklch, var(--color-12) 88%, var(--color-primary));
253
+ --color-muted: color-mix(in oklch, var(--color-10) 60%, var(--color-bright));
220
254
 
221
255
  /* Warm sandstone primary + unexpected turquoise support. */
222
256
  --color-primary: oklch(0.74 0.108 68);
@@ -259,12 +293,12 @@
259
293
  --color-surface: var(--color-2);
260
294
  --color-hover: oklch(0.81 0.022 73);
261
295
  --color-fg: var(--color-11);
262
- --color-bright: var(--color-12);
263
- --color-muted: color-mix(in oklch, var(--color-10) 70%, var(--color-fg));
296
+ --color-bright: color-mix(in oklch, var(--color-12) 88%, var(--color-primary));
297
+ --color-muted: color-mix(in oklch, var(--color-10) 60%, var(--color-bright));
264
298
 
265
299
  --color-primary: oklch(0.515 0.16 38);
266
300
  --color-primary-fg: oklch(0.96 0.014 78);
267
- --color-primary-hover: color-mix(in oklch, var(--color-primary) 85%, black);
301
+ --color-primary-hover: color-mix(in oklch, var(--color-primary) 85%, oklch(0 0 none));
268
302
  --color-destructive: oklch(0.575 0.22 28);
269
303
  --color-destructive-fg: oklch(0.98 0.012 78);
270
304
  --color-success: oklch(0.5 0.13 158);
@@ -294,12 +328,12 @@
294
328
  --color-surface: var(--color-2);
295
329
  --color-hover: oklch(0.81 0.022 73);
296
330
  --color-fg: var(--color-11);
297
- --color-bright: var(--color-12);
298
- --color-muted: color-mix(in oklch, var(--color-10) 70%, var(--color-fg));
331
+ --color-bright: color-mix(in oklch, var(--color-12) 88%, var(--color-primary));
332
+ --color-muted: color-mix(in oklch, var(--color-10) 60%, var(--color-bright));
299
333
 
300
334
  --color-primary: oklch(0.515 0.16 38);
301
335
  --color-primary-fg: oklch(0.96 0.014 78);
302
- --color-primary-hover: color-mix(in oklch, var(--color-primary) 85%, black);
336
+ --color-primary-hover: color-mix(in oklch, var(--color-primary) 85%, oklch(0 0 none));
303
337
  --color-destructive: oklch(0.575 0.22 28);
304
338
  --color-destructive-fg: oklch(0.98 0.012 78);
305
339
  --color-success: oklch(0.5 0.13 158);
@@ -332,8 +366,8 @@
332
366
  --color-surface: var(--color-2);
333
367
  --color-hover: color-mix(in oklch, var(--color-3) 80%, var(--color-2));
334
368
  --color-fg: var(--color-11);
335
- --color-bright: var(--color-12);
336
- --color-muted: color-mix(in oklch, var(--color-10) 70%, var(--color-fg));
369
+ --color-bright: color-mix(in oklch, var(--color-12) 88%, var(--color-primary));
370
+ --color-muted: color-mix(in oklch, var(--color-10) 60%, var(--color-bright));
337
371
 
338
372
  --color-primary: oklch(0.8 0.034 54);
339
373
  --color-primary-fg: var(--color-1);
@@ -370,12 +404,12 @@
370
404
  --color-surface: var(--color-2);
371
405
  --color-hover: oklch(0.88 0.016 72);
372
406
  --color-fg: var(--color-11);
373
- --color-bright: var(--color-12);
374
- --color-muted: color-mix(in oklch, var(--color-10) 70%, var(--color-fg));
407
+ --color-bright: color-mix(in oklch, var(--color-12) 88%, var(--color-primary));
408
+ --color-muted: color-mix(in oklch, var(--color-10) 60%, var(--color-bright));
375
409
 
376
410
  --color-primary: oklch(0.55 0.12 58);
377
411
  --color-primary-fg: oklch(0.97 0.008 80);
378
- --color-primary-hover: color-mix(in oklch, var(--color-primary) 85%, black);
412
+ --color-primary-hover: color-mix(in oklch, var(--color-primary) 85%, oklch(0 0 none));
379
413
  --color-destructive: oklch(0.59 0.18 32);
380
414
  --color-destructive-fg: oklch(0.98 0.008 80);
381
415
  --color-success: oklch(0.54 0.11 164);
@@ -405,12 +439,12 @@
405
439
  --color-surface: var(--color-2);
406
440
  --color-hover: oklch(0.88 0.016 72);
407
441
  --color-fg: var(--color-11);
408
- --color-bright: var(--color-12);
409
- --color-muted: color-mix(in oklch, var(--color-10) 70%, var(--color-fg));
442
+ --color-bright: color-mix(in oklch, var(--color-12) 88%, var(--color-primary));
443
+ --color-muted: color-mix(in oklch, var(--color-10) 60%, var(--color-bright));
410
444
 
411
445
  --color-primary: oklch(0.55 0.12 58);
412
446
  --color-primary-fg: oklch(0.97 0.008 80);
413
- --color-primary-hover: color-mix(in oklch, var(--color-primary) 85%, black);
447
+ --color-primary-hover: color-mix(in oklch, var(--color-primary) 85%, oklch(0 0 none));
414
448
  --color-destructive: oklch(0.59 0.18 32);
415
449
  --color-destructive-fg: oklch(0.98 0.008 80);
416
450
  --color-success: oklch(0.54 0.11 164);
@@ -420,12 +454,16 @@
420
454
  }
421
455
 
422
456
  /* -------------------------------------------------- */
423
- /* Signalnine — dark (Tomorrow, measured) */
457
+ /* Signalnine — dark (Tomorrow bones, amber ink) */
424
458
  /* */
425
- /* The classic imageboard dark theme. The accents and */
426
- /* the upper scale are Tomorrow Night's own values, */
427
- /* converted to OKLCH comment and foreground land */
428
- /* exactly at steps 8 and 11. */
459
+ /* The classic imageboard dark theme. The scale keeps */
460
+ /* Tomorrow Night's lightnesses — comment and */
461
+ /* foreground still land at steps 8 and 11 — but the */
462
+ /* BLUE IS GONE: the ramp's cast and the accent are */
463
+ /* both a cool amber (hue 84, the yellow side of */
464
+ /* amber), so chrome, links and emphasis share one */
465
+ /* temperature instead of a blue ramp under yellow */
466
+ /* accents. */
429
467
  /* */
430
468
  /* The DARK END is deliberately deeper than Tomorrow's. */
431
469
  /* Its #1d1f21 page read light next to era's other dark */
@@ -440,11 +478,11 @@
440
478
  /* the link blue had been placed IN the ramp, which */
441
479
  /* forced the steps under it to swerve to reach it. */
442
480
  /* Tomorrow does not work that way and neither does */
443
- /* this now — the ramp is one cool gray from end to */
444
- /* end, and the blue, red, green and yellow are ACCENT */
445
- /* tokens sitting on top of it. That is what an */
446
- /* imageboard actually looks like: gray chrome, colour */
447
- /* carried by the text. */
481
+ /* this now — the ramp is one gray from end to end */
482
+ /* (cast faintly amber, never placed IN the ramp), and */
483
+ /* the amber, red and green are ACCENT tokens sitting */
484
+ /* on top of it. That is what an imageboard actually */
485
+ /* looks like: gray chrome, colour carried by the text. */
448
486
  /* -------------------------------------------------- */
449
487
  :root[data-theme='signalnine'],
450
488
  [data-theme='signalnine'].dark {
@@ -454,18 +492,18 @@
454
492
  * own #1d1f21 — that read too light beside the other dark themes. Everything
455
493
  * from step 2 up re-spaces evenly from the new floor to the comment anchor,
456
494
  * so lowering the page did not leave a cliff between it and the panels. */
457
- --color-1: oklch(0.168 0.005 248); /* #0d0f11 */
458
- --color-2: oklch(0.225 0.007 258); /* #1a1c1f */
459
- --color-3: oklch(0.29 0.01 258); /* #282c30 */
460
- --color-4: oklch(0.355 0.011 258); /* #383c42 */
461
- --color-5: oklch(0.435 0.011 257);
462
- --color-6: oklch(0.515 0.01 256);
463
- --color-7: oklch(0.596 0.009 255);
464
- --color-8: oklch(0.677 0.006 200); /* #939899 Tomorrow comment */
465
- --color-9: oklch(0.73 0.005 190);
466
- --color-10: oklch(0.78 0.005 180);
467
- --color-11: oklch(0.83 0.004 157); /* #c5c8c6 Tomorrow foreground */
468
- --color-12: oklch(0.9 0.004 157);
495
+ --color-1: oklch(0.168 0.004 84); /* graphite floor, amber-cast */
496
+ --color-2: oklch(0.225 0.005 84);
497
+ --color-3: oklch(0.29 0.007 84);
498
+ --color-4: oklch(0.355 0.008 84);
499
+ --color-5: oklch(0.435 0.008 84);
500
+ --color-6: oklch(0.515 0.008 84);
501
+ --color-7: oklch(0.596 0.007 84);
502
+ --color-8: oklch(0.677 0.006 84); /* comment grey, warmed */
503
+ --color-9: oklch(0.73 0.005 84);
504
+ --color-10: oklch(0.78 0.005 84);
505
+ --color-11: oklch(0.83 0.005 84); /* foreground, warmed */
506
+ --color-12: oklch(0.9 0.005 84);
469
507
 
470
508
  /* Border sits a step LOWER than the usual 5. Tomorrow's own rules are
471
509
  * quiet — its border colour is the current-line grey — and at step 5 the
@@ -473,25 +511,26 @@
473
511
  * page, inside the 1.7–3.2 the other dark themes hold. */
474
512
  --color-border: var(--color-4);
475
513
  --color-surface: var(--color-2);
476
- --color-hover: oklch(0.255 0.009 258);
514
+ --color-hover: oklch(0.255 0.006 84);
477
515
  --color-fg: var(--color-11);
478
- --color-bright: var(--color-12);
516
+ --color-bright: color-mix(in oklch, var(--color-12) 88%, var(--color-primary));
479
517
  /* Tomorrow's comment grey, which is what it uses for de-emphasised text. */
480
518
  --color-muted: var(--color-8);
481
- /* The link blue every imageboard reader knows. */
482
- --color-link: oklch(0.6975 0.0551 244.3); /* #81a2be */
483
-
484
- /* Tomorrow's accents, unmodified. They are pastels, so each takes DARK text
485
- * the page colour rather than white. */
486
- --color-primary: oklch(0.6975 0.0551 244.3); /* #81a2be blue */
487
- --color-primary-fg: oklch(0.168 0.005 248);
488
- --color-primary-hover: color-mix(in oklch, var(--color-primary) 85%, black);
519
+ /* The link follows the accent one ink for "this is interactive". */
520
+ --color-link: var(--color-primary);
521
+
522
+ /* A COOL amber: hue 84 — the yellow side of amber, not orange with the
523
+ * pastel restraint of the Tomorrow accents it replaces, so it still takes
524
+ * DARK text (the page colour) rather than white. */
525
+ --color-primary: oklch(0.76 0.115 84); /* cool amber */
526
+ --color-primary-fg: oklch(0.168 0.004 84);
527
+ --color-primary-hover: color-mix(in oklch, var(--color-primary) 85%, oklch(0 0 none));
489
528
  --color-destructive: oklch(0.6308 0.1298 21.4); /* #cc6666 red */
490
- --color-destructive-fg: oklch(0.168 0.005 248);
529
+ --color-destructive-fg: oklch(0.168 0.004 84);
491
530
  --color-success: oklch(0.7733 0.1095 113.4); /* #b5bd68 green */
492
- --color-success-fg: oklch(0.168 0.005 248);
531
+ --color-success-fg: oklch(0.168 0.004 84);
493
532
  --color-warning: oklch(0.8462 0.1115 82.9); /* #f0c674 yellow */
494
- --color-warning-fg: oklch(0.168 0.005 248);
533
+ --color-warning-fg: oklch(0.168 0.004 84);
495
534
  }
496
535
 
497
536
  /* Signalnine — light (yahoo.co.jp, measured) */
@@ -547,7 +586,7 @@
547
586
  :root[data-theme='signalnine']:not(.dark) {
548
587
  color-scheme: light;
549
588
 
550
- --color-1: oklch(1 0 0); /* #ffffff page */
589
+ --color-1: oklch(1 0 none); /* #ffffff page */
551
590
  --color-2: oklch(0.9439 0.0251 271.14); /* #e6ecfe card fill */
552
591
  --color-3: oklch(0.887 0.0368 265.13); /* #cedaf3 border */
553
592
  --color-4: oklch(0.844 0.0447 265.6); /* #bcccea */
@@ -555,10 +594,10 @@
555
594
  --color-6: oklch(0.676 0.078 263);
556
595
  --color-7: oklch(0.593 0.0975 260.33); /* #5c7eb8 muted text, darkened */
557
596
  --color-8: oklch(0.545 0.126 262);
558
- --color-9: oklch(0.51 0 0); /* #666666 */
597
+ --color-9: oklch(0.51 0 none); /* #666666 */
559
598
  --color-10: oklch(0.358 0.1534 266.07); /* #17318b link text, darkened */
560
599
  --color-11: oklch(0.16 0.008 266);
561
- --color-12: oklch(0 0 0); /* #000000 body text */
600
+ --color-12: oklch(0 0 none); /* #000000 body text (hue `none` — see --color-bright) */
562
601
 
563
602
  /* Repointed off the conventional steps on purpose — these name the roles
564
603
  * yahoo.co.jp actually paints, so each points at the step holding its
@@ -569,19 +608,19 @@
569
608
  --color-surface: var(--color-2);
570
609
  --color-hover: oklch(0.97 0.014 268.5); /* #f1f5ff */
571
610
  --color-fg: var(--color-11);
572
- --color-bright: var(--color-12);
611
+ --color-bright: color-mix(in oklch, var(--color-12) 88%, var(--color-primary));
573
612
  --color-muted: var(--color-7);
574
613
  --color-link: var(--color-10);
575
614
  /* Cards read --era-surface-bg, which the surface contract defines as
576
615
  * --color-2 — so the card fill follows from the scale, no override. */
577
616
 
578
617
  --color-primary: oklch(0.566 0.222 265.8); /* #3766f6 the CTA blue, darkened */
579
- --color-primary-fg: oklch(1 0 0);
580
- --color-primary-hover: color-mix(in oklch, var(--color-primary) 85%, black);
618
+ --color-primary-fg: oklch(1 0 none);
619
+ --color-primary-hover: color-mix(in oklch, var(--color-primary) 85%, oklch(0 0 none));
581
620
  --color-destructive: oklch(0.59 0.242 27.7); /* #eb0013 */
582
- --color-destructive-fg: oklch(1 0 0);
621
+ --color-destructive-fg: oklch(1 0 none);
583
622
  --color-success: oklch(0.58 0.15 152);
584
- --color-success-fg: oklch(1 0 0);
623
+ --color-success-fg: oklch(1 0 none);
585
624
  --color-warning: oklch(0.76 0.145 80);
586
625
  --color-warning-fg: oklch(0.16 0.008 266);
587
626
  }
@@ -590,7 +629,7 @@
590
629
  [data-theme='signalnine'].light {
591
630
  color-scheme: light;
592
631
 
593
- --color-1: oklch(1 0 0); /* #ffffff page */
632
+ --color-1: oklch(1 0 none); /* #ffffff page */
594
633
  --color-2: oklch(0.9439 0.0251 271.14); /* #e6ecfe card fill */
595
634
  --color-3: oklch(0.887 0.0368 265.13); /* #cedaf3 border */
596
635
  --color-4: oklch(0.844 0.0447 265.6); /* #bcccea */
@@ -598,10 +637,10 @@
598
637
  --color-6: oklch(0.676 0.078 263);
599
638
  --color-7: oklch(0.593 0.0975 260.33); /* #5c7eb8 muted text, darkened */
600
639
  --color-8: oklch(0.545 0.126 262);
601
- --color-9: oklch(0.51 0 0); /* #666666 */
640
+ --color-9: oklch(0.51 0 none); /* #666666 */
602
641
  --color-10: oklch(0.358 0.1534 266.07); /* #17318b link text, darkened */
603
642
  --color-11: oklch(0.16 0.008 266);
604
- --color-12: oklch(0 0 0); /* #000000 body text */
643
+ --color-12: oklch(0 0 none); /* #000000 body text (hue `none` — see --color-bright) */
605
644
 
606
645
  /* Repointed off the conventional steps on purpose — these name the roles
607
646
  * yahoo.co.jp actually paints, so each points at the step holding its
@@ -612,19 +651,19 @@
612
651
  --color-surface: var(--color-2);
613
652
  --color-hover: oklch(0.97 0.014 268.5); /* #f1f5ff */
614
653
  --color-fg: var(--color-11);
615
- --color-bright: var(--color-12);
654
+ --color-bright: color-mix(in oklch, var(--color-12) 88%, var(--color-primary));
616
655
  --color-muted: var(--color-7);
617
656
  --color-link: var(--color-10);
618
657
  /* Cards read --era-surface-bg, which the surface contract defines as
619
658
  * --color-2 — so the card fill follows from the scale, no override. */
620
659
 
621
660
  --color-primary: oklch(0.566 0.222 265.8); /* #3766f6 the CTA blue, darkened */
622
- --color-primary-fg: oklch(1 0 0);
623
- --color-primary-hover: color-mix(in oklch, var(--color-primary) 85%, black);
661
+ --color-primary-fg: oklch(1 0 none);
662
+ --color-primary-hover: color-mix(in oklch, var(--color-primary) 85%, oklch(0 0 none));
624
663
  --color-destructive: oklch(0.59 0.242 27.7); /* #eb0013 */
625
- --color-destructive-fg: oklch(1 0 0);
664
+ --color-destructive-fg: oklch(1 0 none);
626
665
  --color-success: oklch(0.58 0.15 152);
627
- --color-success-fg: oklch(1 0 0);
666
+ --color-success-fg: oklch(1 0 none);
628
667
  --color-warning: oklch(0.76 0.145 80);
629
668
  --color-warning-fg: oklch(0.16 0.008 266);
630
669
  }