tyrell-components 1.0.0-TC34 → 1.0.0-TC36

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.
@@ -130,8 +130,13 @@
130
130
  --ty-danger-hue: 25;
131
131
  --ty-danger-chroma: calc(var(--ty-brand-chroma) * 1.31);
132
132
 
133
- --ty-neutral-hue: var(--ty-brand-hue);
134
- --ty-neutral-chroma: calc(var(--ty-brand-chroma) * 0.04);
133
+ /* Neutral is ACHROMATIC by default — greys do not follow the brand.
134
+ * Most of an interface is monochrome; a rebrand should not drift it.
135
+ * To warm greys toward the brand (the pre-2026-08 behavior), opt in:
136
+ * --ty-neutral-hue: var(--ty-brand-hue);
137
+ * --ty-neutral-chroma: calc(var(--ty-brand-chroma) * 0.04); */
138
+ --ty-neutral-hue: 0;
139
+ --ty-neutral-chroma: 0;
135
140
 
136
141
  /* ----------------------------------------------------------------
137
142
  * Tier 3 — L-CURVE (light mode)
@@ -150,6 +155,33 @@
150
155
  --ty-l-bg-bold: 0.92;
151
156
  --ty-l-bg-soft: 0.98;
152
157
 
158
+ /* Text ladder (--ty-text-*) and surface borders — historically
159
+ * hardcoded hexes in tyrell.css that a rebrand never touched; now
160
+ * engine-wired at the SAME lightness the hexes had (measured via
161
+ * oklch(from #hex l c h)), tinted by the neutral seeds. At the
162
+ * default --ty-neutral-chroma (≈0.005) the tint is imperceptible. */
163
+ --ty-l-text-strong: 0; /* was #000 */
164
+ --ty-l-text-bold: 0; /* was #000 */
165
+ --ty-l-text-base: 0.21; /* was #111827 */
166
+ --ty-l-text-soft: 0.37; /* was #374151 */
167
+ --ty-l-text-faint: 0.71; /* was #9ca3af */
168
+
169
+ --ty-l-border-content: 0.93; /* was #e5e7eb */
170
+ --ty-l-border-elevated: 0.93;
171
+ --ty-l-border-floating: 0.93;
172
+
173
+ /* Generic border ladder — borders are their OWN family, not ink.
174
+ * Historically --ty-border-* aliased the neutral text ramp; that tied
175
+ * app chrome to the text emphasis curve (and to --ty-neutral-l-factor),
176
+ * so tuning text moved every border. Defaults preserve the exact L the
177
+ * aliases produced. Borders sit relative to the surfaces they separate;
178
+ * tune them here without touching text. */
179
+ --ty-l-border-strong: 0.46; /* was color-neutral-bold */
180
+ --ty-l-border-bold: 0.64;
181
+ --ty-l-border: 0.76; /* was color-neutral-soft */
182
+ --ty-l-border-soft: 0.88; /* was color-neutral-faint */
183
+ --ty-l-border-faint: 0.92;
184
+
153
185
  /* ----------------------------------------------------------------
154
186
  * Tier 4 — SATURATION CURVE
155
187
  * Per-shade chroma multipliers. Each shade's chroma =
@@ -235,6 +267,25 @@ html[data-theme="dark"] {
235
267
  --ty-l-bg-bold: 0.26;
236
268
  --ty-l-bg-soft: 0.19;
237
269
 
270
+ /* Text ladder + surface borders dark — L matched to the hexes
271
+ * tyrell.css used to hardcode (#fff/#f9fafb/#d6d6d6/#7f8590/#555a63,
272
+ * borders #030712/#414141/#494949). */
273
+ --ty-l-text-strong: 1;
274
+ --ty-l-text-bold: 0.985;
275
+ --ty-l-text-base: 0.88;
276
+ --ty-l-text-soft: 0.62;
277
+ --ty-l-text-faint: 0.47;
278
+
279
+ --ty-l-border-content: 0.13;
280
+ --ty-l-border-elevated: 0.375;
281
+ --ty-l-border-floating: 0.41;
282
+
283
+ --ty-l-border-strong: 0.66;
284
+ --ty-l-border-bold: 0.46;
285
+ --ty-l-border: 0.38;
286
+ --ty-l-border-soft: 0.3;
287
+ --ty-l-border-faint: 0.3;
288
+
238
289
  /* Tier 4 dark — dim shades hold more chroma so they don't grey out;
239
290
  * strong shades trim chroma so bright tones don't over-pop. */
240
291
  --ty-c-strong-mult: 0.77;
@@ -255,8 +306,8 @@ html[data-theme="dark"] {
255
306
  --ty-solid-strong-l: 0.06; /* tone+ */
256
307
  --ty-solid-soft-l: -0.1; /* tone- */
257
308
 
258
- --ty-solid-l: -0.25; /* dim against dark canvas */
259
- --ty-solid-c: 0.65; /* desaturate against dark canvas */
309
+ --ty-solid-l: -0.2; /* dim against dark canvas */
310
+ --ty-solid-c: 0.7; /* desaturate against dark canvas */
260
311
  --ty-solid-h: 0; /* hue offset */
261
312
  }
262
313
 
@@ -276,14 +327,18 @@ html[data-theme="dark"] {
276
327
  * var(--ty-{flavor}-hue) ← H
277
328
  * );
278
329
  *
279
- * Computed tokens are declared in BOTH `:root` and `html.dark` blocks
280
- * because tyrell.css ships hardcoded dark-mode hexes at html.dark
281
- * specificity (0,1,1); our brand-layer `:root` (0,1,0) would lose
282
- * source-order otherwise. Same math both blocks just different L/C
283
- * inputs from Tier 3/4.
330
+ * Formulas are declared ONCE, at `html:root` (specificity 0,1,1 — NOT
331
+ * plain `:root` (0,1,0)): they out-rank tyrell.css's hardcoded light
332
+ * tokens by specificity and its hardcoded `html.dark` hexes (0,1,1) by
333
+ * source order, since the brand layer loads after. Dark mode needs no
334
+ * formula duplicates — the dials they read (Tier 3/4, SECTION 1) are
335
+ * what flip per mode. The `html.dark` block at the END of this file
336
+ * holds only genuine per-mode DATA (surface ladder, hand-pinned
337
+ * absolutes) — never formulas. If you add a token here, do NOT mirror
338
+ * it there unless its VALUE is mode-specific.
284
339
  * ===================================================================== */
285
340
 
286
- :root {
341
+ html:root {
287
342
  /* ----------------------------------------------------------------
288
343
  * Flavor color ramps (fg + bg + border)
289
344
  * --------------------------------------------------------------*/
@@ -544,18 +599,66 @@ html[data-theme="dark"] {
544
599
  );
545
600
 
546
601
  /* ----------------------------------------------------------------
547
- * Global neutral borders — read off the neutral color ramp.
602
+ * Global neutral borders — own family, own L ladder (Tier 3).
603
+ * Tinted by the neutral seeds like everything grey, but NOT tied to
604
+ * the text emphasis curve or --ty-neutral-l-factor. Per-flavor
605
+ * accent borders (--ty-border-{flavor}) stay ink aliases — a colored
606
+ * border SHOULD track its flavor's ink.
548
607
  * --------------------------------------------------------------*/
549
- --ty-border-strong: var(--ty-color-neutral-bold);
550
- --ty-border: var(--ty-color-neutral-soft);
551
- --ty-border-bold: var(--ty-color-neutral-soft);
552
- --ty-border-soft: var(--ty-color-neutral-faint);
553
- --ty-border-faint: var(--ty-color-neutral-faint);
608
+ --ty-border-strong: oklch(
609
+ var(--ty-l-border-strong) var(--ty-neutral-chroma) var(--ty-neutral-hue)
610
+ );
611
+ --ty-border: oklch(
612
+ var(--ty-l-border) var(--ty-neutral-chroma) var(--ty-neutral-hue)
613
+ );
614
+ --ty-border-bold: oklch(
615
+ var(--ty-l-border-bold) var(--ty-neutral-chroma) var(--ty-neutral-hue)
616
+ );
617
+ --ty-border-soft: oklch(
618
+ var(--ty-l-border-soft) var(--ty-neutral-chroma) var(--ty-neutral-hue)
619
+ );
620
+ --ty-border-faint: oklch(
621
+ var(--ty-l-border-faint) var(--ty-neutral-chroma) var(--ty-neutral-hue)
622
+ );
623
+
624
+ /* ----------------------------------------------------------------
625
+ * Text ladder + surface borders — engine-wired versions of tokens
626
+ * tyrell.css hardcodes as hexes. Own L dials (Tier 3), neutral
627
+ * hue/chroma tint. These out-rank the base hexes the same way every
628
+ * other token here does; a rebrand now reaches body text too.
629
+ * --------------------------------------------------------------*/
630
+ --ty-text-strong: oklch(
631
+ var(--ty-l-text-strong) var(--ty-neutral-chroma) var(--ty-neutral-hue)
632
+ );
633
+ --ty-text-bold: oklch(
634
+ var(--ty-l-text-bold) var(--ty-neutral-chroma) var(--ty-neutral-hue)
635
+ );
636
+ --ty-text: oklch(
637
+ var(--ty-l-text-base) var(--ty-neutral-chroma) var(--ty-neutral-hue)
638
+ );
639
+ --ty-text-soft: oklch(
640
+ var(--ty-l-text-soft) var(--ty-neutral-chroma) var(--ty-neutral-hue)
641
+ );
642
+ --ty-text-faint: oklch(
643
+ var(--ty-l-text-faint) var(--ty-neutral-chroma) var(--ty-neutral-hue)
644
+ );
645
+
646
+ --ty-content-border: oklch(
647
+ var(--ty-l-border-content) var(--ty-neutral-chroma) var(--ty-neutral-hue)
648
+ );
649
+ --ty-elevated-border: oklch(
650
+ var(--ty-l-border-elevated) var(--ty-neutral-chroma) var(--ty-neutral-hue)
651
+ );
652
+ --ty-floating-border: oklch(
653
+ var(--ty-l-border-floating) var(--ty-neutral-chroma) var(--ty-neutral-hue)
654
+ );
554
655
 
555
656
  /* ----------------------------------------------------------------
556
657
  * Surfaces — monochrome by default; --ty-surface-chroma warms them.
557
658
  * --------------------------------------------------------------*/
558
- --ty-surface-canvas: oklch(0.985 var(--ty-surface-chroma) var(--ty-brand-hue));
659
+ --ty-surface-canvas: oklch(
660
+ 0.985 var(--ty-surface-chroma) var(--ty-brand-hue)
661
+ );
559
662
  --ty-surface-content: oklch(1 0 0);
560
663
  --ty-surface-elevated: oklch(1 0 0);
561
664
  --ty-surface-floating: oklch(1 0 0);
@@ -625,12 +728,107 @@ html:root {
625
728
  var(--ty-neutral-hue)
626
729
  );
627
730
 
628
- --ty-solid-primary-fg: white;
629
- --ty-solid-secondary-fg: white;
630
- --ty-solid-success-fg: white;
631
- --ty-solid-danger-fg: white;
632
- --ty-solid-warning-fg: white;
633
- --ty-solid-neutral-fg: white;
731
+ /* ----------------------------------------------------------------
732
+ * SOLID FOREGROUNDS — auto-contrast.
733
+ *
734
+ * Each fill gets a foreground picked from its OWN lightness, so a
735
+ * rebrand (--ty-brand-hue / --ty-brand-chroma / any L-curve override)
736
+ * can't strand white text on a pale fill. Branchless, no JS:
737
+ *
738
+ * clamp(0, (threshold - l) * 1000, 1) -> 1 (white) when the fill
739
+ * is darker than the threshold, 0 (black) when it's lighter.
740
+ *
741
+ * One token per FILL, not per flavor — base / -soft (tone-) / -strong
742
+ * (tone+) each carry a different L, so each needs its own decision.
743
+ * Declared once: the oklch(from ...) formulas re-resolve per mode,
744
+ * because html.dark redefines the fills these read.
745
+ *
746
+ * TO REVERT to the old fixed-white behaviour, override in your app:
747
+ * :root { --ty-solid-primary-fg: white; ... }
748
+ * or set --ty-solid-fg-threshold: 1 to force white everywhere.
749
+ * --------------------------------------------------------------*/
750
+
751
+ /* Crossover point. Fills lighter than this get black text. ~0.58-0.62
752
+ is the sRGB break-even; higher = prefers white, lower = prefers black. */
753
+ --ty-solid-fg-threshold: 0.62;
754
+
755
+ --ty-solid-primary-fg: oklch(
756
+ from var(--ty-solid-primary)
757
+ clamp(0, (var(--ty-solid-fg-threshold) - l) * 1000, 1) 0 0
758
+ );
759
+ --ty-solid-primary-soft-fg: oklch(
760
+ from var(--ty-solid-primary-soft)
761
+ clamp(0, (var(--ty-solid-fg-threshold) - l) * 1000, 1) 0 0
762
+ );
763
+ --ty-solid-primary-strong-fg: oklch(
764
+ from var(--ty-solid-primary-strong)
765
+ clamp(0, (var(--ty-solid-fg-threshold) - l) * 1000, 1) 0 0
766
+ );
767
+
768
+ --ty-solid-secondary-fg: oklch(
769
+ from var(--ty-solid-secondary)
770
+ clamp(0, (var(--ty-solid-fg-threshold) - l) * 1000, 1) 0 0
771
+ );
772
+ --ty-solid-secondary-soft-fg: oklch(
773
+ from var(--ty-solid-secondary-soft)
774
+ clamp(0, (var(--ty-solid-fg-threshold) - l) * 1000, 1) 0 0
775
+ );
776
+ --ty-solid-secondary-strong-fg: oklch(
777
+ from var(--ty-solid-secondary-strong)
778
+ clamp(0, (var(--ty-solid-fg-threshold) - l) * 1000, 1) 0 0
779
+ );
780
+
781
+ --ty-solid-success-fg: oklch(
782
+ from var(--ty-solid-success)
783
+ clamp(0, (var(--ty-solid-fg-threshold) - l) * 1000, 1) 0 0
784
+ );
785
+ --ty-solid-success-soft-fg: oklch(
786
+ from var(--ty-solid-success-soft)
787
+ clamp(0, (var(--ty-solid-fg-threshold) - l) * 1000, 1) 0 0
788
+ );
789
+ --ty-solid-success-strong-fg: oklch(
790
+ from var(--ty-solid-success-strong)
791
+ clamp(0, (var(--ty-solid-fg-threshold) - l) * 1000, 1) 0 0
792
+ );
793
+
794
+ --ty-solid-danger-fg: oklch(
795
+ from var(--ty-solid-danger)
796
+ clamp(0, (var(--ty-solid-fg-threshold) - l) * 1000, 1) 0 0
797
+ );
798
+ --ty-solid-danger-soft-fg: oklch(
799
+ from var(--ty-solid-danger-soft)
800
+ clamp(0, (var(--ty-solid-fg-threshold) - l) * 1000, 1) 0 0
801
+ );
802
+ --ty-solid-danger-strong-fg: oklch(
803
+ from var(--ty-solid-danger-strong)
804
+ clamp(0, (var(--ty-solid-fg-threshold) - l) * 1000, 1) 0 0
805
+ );
806
+
807
+ --ty-solid-warning-fg: oklch(
808
+ from var(--ty-solid-warning)
809
+ clamp(0, (var(--ty-solid-fg-threshold) - l) * 1000, 1) 0 0
810
+ );
811
+ --ty-solid-warning-soft-fg: oklch(
812
+ from var(--ty-solid-warning-soft)
813
+ clamp(0, (var(--ty-solid-fg-threshold) - l) * 1000, 1) 0 0
814
+ );
815
+ --ty-solid-warning-strong-fg: oklch(
816
+ from var(--ty-solid-warning-strong)
817
+ clamp(0, (var(--ty-solid-fg-threshold) - l) * 1000, 1) 0 0
818
+ );
819
+
820
+ --ty-solid-neutral-fg: oklch(
821
+ from var(--ty-solid-neutral)
822
+ clamp(0, (var(--ty-solid-fg-threshold) - l) * 1000, 1) 0 0
823
+ );
824
+ --ty-solid-neutral-soft-fg: oklch(
825
+ from var(--ty-solid-neutral-soft)
826
+ clamp(0, (var(--ty-solid-fg-threshold) - l) * 1000, 1) 0 0
827
+ );
828
+ --ty-solid-neutral-strong-fg: oklch(
829
+ from var(--ty-solid-neutral-strong)
830
+ clamp(0, (var(--ty-solid-fg-threshold) - l) * 1000, 1) 0 0
831
+ );
634
832
 
635
833
  /* derived states — axis 2 offsets on each base fill */
636
834
  --ty-solid-primary-hover: oklch(
@@ -713,262 +911,39 @@ html:root {
713
911
  }
714
912
 
715
913
  /* =====================================================================
716
- * SECTION 2 DARK — same computations re-declared so html.dark (0,1,1)
717
- * matches tyrell.css's hardcoded dark-mode hexes at source-order
718
- * specificity. The math is identical to the :root block above; only
719
- * the L/C inputs differ (from html.dark's Tier 3/4 overrides).
914
+ * SECTION 2 DARK — PER-MODE DATA ONLY
915
+ * =====================================================================
916
+ * No formulas here. Every computed token is declared once at html:root
917
+ * above and resolves per-mode through its dial inputs (SECTION 1 flips
918
+ * those at html.dark). This block holds only values that ARE the dark
919
+ * design data — hand-tuned absolutes with no light-mode derivation.
720
920
  * ===================================================================== */
721
921
 
722
922
  html.dark,
723
923
  html[data-theme="dark"] {
724
- /* Primary */
725
- --ty-color-primary-strong: oklch(
726
- calc(var(--ty-l-strong) * var(--ty-primary-l-factor))
727
- calc(var(--ty-brand-chroma) * var(--ty-c-strong-mult)) var(--ty-brand-hue)
728
- );
729
- --ty-color-primary-bold: oklch(
730
- calc(var(--ty-l-bold) * var(--ty-primary-l-factor))
731
- calc(var(--ty-brand-chroma) * var(--ty-c-bold-mult)) var(--ty-brand-hue)
732
- );
733
- --ty-color-primary: oklch(
734
- calc(var(--ty-l-base) * var(--ty-primary-l-factor))
735
- calc(var(--ty-brand-chroma) * var(--ty-c-base-mult)) var(--ty-brand-hue)
736
- );
737
- --ty-color-primary-soft: oklch(
738
- calc(var(--ty-l-soft) * var(--ty-primary-l-factor))
739
- calc(var(--ty-brand-chroma) * var(--ty-c-soft-mult)) var(--ty-brand-hue)
740
- );
741
- --ty-color-primary-faint: oklch(
742
- calc(var(--ty-l-faint) * var(--ty-primary-l-factor))
743
- calc(var(--ty-brand-chroma) * var(--ty-c-faint-mult)) var(--ty-brand-hue)
744
- );
745
- --ty-bg-primary: oklch(
746
- calc(var(--ty-l-bg-base) * var(--ty-primary-l-factor))
747
- calc(var(--ty-brand-chroma) * var(--ty-c-bg-base-mult))
748
- var(--ty-brand-hue)
749
- );
750
- --ty-bg-primary-bold: oklch(
751
- calc(var(--ty-l-bg-bold) * var(--ty-primary-l-factor))
752
- calc(var(--ty-brand-chroma) * var(--ty-c-bg-bold-mult))
753
- var(--ty-brand-hue)
754
- );
755
- --ty-bg-primary-soft: oklch(
756
- calc(var(--ty-l-bg-soft) * var(--ty-primary-l-factor))
757
- calc(var(--ty-brand-chroma) * var(--ty-c-bg-soft-mult))
758
- var(--ty-brand-hue)
759
- );
760
- --ty-border-primary: var(--ty-color-primary-soft);
761
-
762
- /* Secondary */
763
- --ty-color-secondary-strong: oklch(
764
- calc(var(--ty-l-strong) * var(--ty-secondary-l-factor))
765
- calc(var(--ty-secondary-chroma) * var(--ty-c-strong-mult))
766
- var(--ty-secondary-hue)
767
- );
768
- --ty-color-secondary-bold: oklch(
769
- calc(var(--ty-l-bold) * var(--ty-secondary-l-factor))
770
- calc(var(--ty-secondary-chroma) * var(--ty-c-bold-mult))
771
- var(--ty-secondary-hue)
772
- );
773
- --ty-color-secondary: oklch(
774
- calc(var(--ty-l-base) * var(--ty-secondary-l-factor))
775
- calc(var(--ty-secondary-chroma) * var(--ty-c-base-mult))
776
- var(--ty-secondary-hue)
777
- );
778
- --ty-color-secondary-soft: oklch(
779
- calc(var(--ty-l-soft) * var(--ty-secondary-l-factor))
780
- calc(var(--ty-secondary-chroma) * var(--ty-c-soft-mult))
781
- var(--ty-secondary-hue)
782
- );
783
- --ty-color-secondary-faint: oklch(
784
- calc(var(--ty-l-faint) * var(--ty-secondary-l-factor))
785
- calc(var(--ty-secondary-chroma) * var(--ty-c-faint-mult))
786
- var(--ty-secondary-hue)
787
- );
788
- --ty-bg-secondary: oklch(
789
- calc(var(--ty-l-bg-base) * var(--ty-secondary-l-factor))
790
- calc(var(--ty-secondary-chroma) * var(--ty-c-bg-base-mult))
791
- var(--ty-secondary-hue)
792
- );
793
- --ty-bg-secondary-bold: oklch(
794
- calc(var(--ty-l-bg-bold) * var(--ty-secondary-l-factor))
795
- calc(var(--ty-secondary-chroma) * var(--ty-c-bg-bold-mult))
796
- var(--ty-secondary-hue)
797
- );
798
- --ty-bg-secondary-soft: oklch(
799
- calc(var(--ty-l-bg-soft) * var(--ty-secondary-l-factor))
800
- calc(var(--ty-secondary-chroma) * var(--ty-c-bg-soft-mult))
801
- var(--ty-secondary-hue)
802
- );
803
- --ty-border-secondary: var(--ty-color-secondary-soft);
804
-
805
- /* Success */
806
- --ty-color-success-strong: oklch(
807
- calc(var(--ty-l-strong) * var(--ty-success-l-factor))
808
- calc(var(--ty-success-chroma) * var(--ty-c-strong-mult))
809
- var(--ty-success-hue)
810
- );
811
- --ty-color-success-bold: oklch(
812
- calc(var(--ty-l-bold) * var(--ty-success-l-factor))
813
- calc(var(--ty-success-chroma) * var(--ty-c-bold-mult))
814
- var(--ty-success-hue)
815
- );
816
- --ty-color-success: oklch(
817
- calc(var(--ty-l-base) * var(--ty-success-l-factor))
818
- calc(var(--ty-success-chroma) * var(--ty-c-base-mult))
819
- var(--ty-success-hue)
820
- );
821
- --ty-color-success-soft: oklch(
822
- calc(var(--ty-l-soft) * var(--ty-success-l-factor))
823
- calc(var(--ty-success-chroma) * var(--ty-c-soft-mult))
824
- var(--ty-success-hue)
825
- );
826
- --ty-color-success-faint: oklch(
827
- calc(var(--ty-l-faint) * var(--ty-success-l-factor))
828
- calc(var(--ty-success-chroma) * var(--ty-c-faint-mult))
829
- var(--ty-success-hue)
830
- );
831
- --ty-bg-success: oklch(
832
- calc(var(--ty-l-bg-base) * var(--ty-success-l-factor))
833
- calc(var(--ty-success-chroma) * var(--ty-c-bg-base-mult))
834
- var(--ty-success-hue)
835
- );
836
- --ty-bg-success-bold: oklch(
837
- calc(var(--ty-l-bg-bold) * var(--ty-success-l-factor))
838
- calc(var(--ty-success-chroma) * var(--ty-c-bg-bold-mult))
839
- var(--ty-success-hue)
840
- );
841
- --ty-bg-success-soft: oklch(
842
- calc(var(--ty-l-bg-soft) * var(--ty-success-l-factor))
843
- calc(var(--ty-success-chroma) * var(--ty-c-bg-soft-mult))
844
- var(--ty-success-hue)
845
- );
846
- --ty-border-success: var(--ty-color-success-soft);
847
-
848
- /* Danger */
849
- --ty-color-danger-strong: oklch(
850
- calc(var(--ty-l-strong) * var(--ty-danger-l-factor))
851
- calc(var(--ty-danger-chroma) * var(--ty-c-strong-mult))
852
- var(--ty-danger-hue)
853
- );
854
- --ty-color-danger-bold: oklch(
855
- calc(var(--ty-l-bold) * var(--ty-danger-l-factor))
856
- calc(var(--ty-danger-chroma) * var(--ty-c-bold-mult)) var(--ty-danger-hue)
857
- );
858
- --ty-color-danger: oklch(
859
- calc(var(--ty-l-base) * var(--ty-danger-l-factor))
860
- calc(var(--ty-danger-chroma) * var(--ty-c-base-mult)) var(--ty-danger-hue)
861
- );
862
- --ty-color-danger-soft: oklch(
863
- calc(var(--ty-l-soft) * var(--ty-danger-l-factor))
864
- calc(var(--ty-danger-chroma) * var(--ty-c-soft-mult)) var(--ty-danger-hue)
865
- );
866
- --ty-color-danger-faint: oklch(
867
- calc(var(--ty-l-faint) * var(--ty-danger-l-factor))
868
- calc(var(--ty-danger-chroma) * var(--ty-c-faint-mult))
869
- var(--ty-danger-hue)
870
- );
871
- --ty-bg-danger: oklch(
872
- calc(var(--ty-l-bg-base) * var(--ty-danger-l-factor))
873
- calc(var(--ty-danger-chroma) * var(--ty-c-bg-base-mult))
874
- var(--ty-danger-hue)
924
+ /* Surface ladder — hand-tuned; nothing to derive from light (which
925
+ * is 0.985 canvas + pure white everywhere else). */
926
+ --ty-surface-canvas: oklch(0.12 var(--ty-surface-chroma) var(--ty-brand-hue));
927
+ --ty-surface-content: oklch(
928
+ 0.16 var(--ty-surface-chroma) var(--ty-brand-hue)
875
929
  );
876
- --ty-bg-danger-bold: oklch(
877
- calc(var(--ty-l-bg-bold) * var(--ty-danger-l-factor))
878
- calc(var(--ty-danger-chroma) * var(--ty-c-bg-bold-mult))
879
- var(--ty-danger-hue)
930
+ --ty-surface-elevated: oklch(
931
+ 0.19 var(--ty-surface-chroma) var(--ty-brand-hue)
880
932
  );
881
- --ty-bg-danger-soft: oklch(
882
- calc(var(--ty-l-bg-soft) * var(--ty-danger-l-factor))
883
- calc(var(--ty-danger-chroma) * var(--ty-c-bg-soft-mult))
884
- var(--ty-danger-hue)
933
+ --ty-surface-floating: oklch(
934
+ 0.22 var(--ty-surface-chroma) var(--ty-brand-hue)
885
935
  );
886
- --ty-border-danger: var(--ty-color-danger-soft);
936
+ --ty-surface-input: oklch(0.18 var(--ty-surface-chroma) var(--ty-brand-hue));
887
937
 
888
- /* Warning */
889
- --ty-color-warning-strong: oklch(
890
- calc(var(--ty-l-strong) * var(--ty-warning-l-factor))
891
- calc(var(--ty-warning-chroma) * var(--ty-c-strong-mult))
892
- var(--ty-warning-hue)
893
- );
894
- --ty-color-warning-bold: oklch(
895
- calc(var(--ty-l-bold) * var(--ty-warning-l-factor))
896
- calc(var(--ty-warning-chroma) * var(--ty-c-bold-mult))
897
- var(--ty-warning-hue)
898
- );
899
- --ty-color-warning: oklch(
900
- calc(var(--ty-l-base) * var(--ty-warning-l-factor))
901
- calc(var(--ty-warning-chroma) * var(--ty-c-base-mult))
902
- var(--ty-warning-hue)
903
- );
904
- --ty-color-warning-soft: oklch(
905
- calc(var(--ty-l-soft) * var(--ty-warning-l-factor))
906
- calc(var(--ty-warning-chroma) * var(--ty-c-soft-mult))
907
- var(--ty-warning-hue)
908
- );
909
- --ty-color-warning-faint: oklch(
910
- calc(var(--ty-l-faint) * var(--ty-warning-l-factor))
911
- calc(var(--ty-warning-chroma) * var(--ty-c-faint-mult))
912
- var(--ty-warning-hue)
913
- );
914
- --ty-bg-warning: oklch(
915
- calc(var(--ty-l-bg-base) * var(--ty-warning-l-factor))
916
- calc(var(--ty-warning-chroma) * var(--ty-c-bg-base-mult))
917
- var(--ty-warning-hue)
918
- );
919
- --ty-bg-warning-bold: oklch(
920
- calc(var(--ty-l-bg-bold) * var(--ty-warning-l-factor))
921
- calc(var(--ty-warning-chroma) * var(--ty-c-bg-bold-mult))
922
- var(--ty-warning-hue)
923
- );
924
- --ty-bg-warning-soft: oklch(
925
- calc(var(--ty-l-bg-soft) * var(--ty-warning-l-factor))
926
- calc(var(--ty-warning-chroma) * var(--ty-c-bg-soft-mult))
927
- var(--ty-warning-hue)
938
+ /* Focus ring needs more alpha against dark surfaces (light: 5%). */
939
+ --ty-input-shadow-focus: color-mix(
940
+ in oklab,
941
+ var(--ty-color-primary) 20%,
942
+ transparent
928
943
  );
929
- --ty-border-warning: var(--ty-color-warning-soft);
930
944
 
931
- /* Neutral */
932
- --ty-color-neutral-strong: oklch(
933
- calc(var(--ty-l-strong) * var(--ty-neutral-l-factor))
934
- calc(var(--ty-neutral-chroma) * var(--ty-c-strong-mult))
935
- var(--ty-neutral-hue)
936
- );
937
- --ty-color-neutral-bold: oklch(
938
- calc(var(--ty-l-bold) * var(--ty-neutral-l-factor))
939
- calc(var(--ty-neutral-chroma) * var(--ty-c-bold-mult))
940
- var(--ty-neutral-hue)
941
- );
942
- --ty-color-neutral: oklch(
943
- calc(var(--ty-l-base) * var(--ty-neutral-l-factor))
944
- calc(var(--ty-neutral-chroma) * var(--ty-c-base-mult))
945
- var(--ty-neutral-hue)
946
- );
947
- --ty-color-neutral-soft: oklch(
948
- calc(var(--ty-l-soft) * var(--ty-neutral-l-factor))
949
- calc(var(--ty-neutral-chroma) * var(--ty-c-soft-mult))
950
- var(--ty-neutral-hue)
951
- );
952
- --ty-color-neutral-faint: oklch(
953
- calc(var(--ty-l-faint) * var(--ty-neutral-l-factor))
954
- calc(var(--ty-neutral-chroma) * var(--ty-c-faint-mult))
955
- var(--ty-neutral-hue)
956
- );
957
- --ty-bg-neutral: oklch(
958
- calc(var(--ty-l-bg-base) * var(--ty-neutral-l-factor))
959
- calc(var(--ty-neutral-chroma) * var(--ty-c-bg-base-mult))
960
- var(--ty-neutral-hue)
961
- );
962
- --ty-bg-neutral-bold: oklch(
963
- calc(var(--ty-l-bg-bold) * var(--ty-neutral-l-factor))
964
- calc(var(--ty-neutral-chroma) * var(--ty-c-bg-bold-mult))
965
- var(--ty-neutral-hue)
966
- );
967
- --ty-bg-neutral-soft: oklch(
968
- calc(var(--ty-l-bg-soft) * var(--ty-neutral-l-factor))
969
- calc(var(--ty-neutral-chroma) * var(--ty-c-bg-soft-mult))
970
- var(--ty-neutral-hue)
971
- );
945
+ /* Hand-pinned bg extremes — these OPPOSE the mode (strong bg is dark
946
+ * in light mode, light in dark mode) so they can't ride the bg ramp. */
972
947
  --ty-bg-neutral-strong: oklch(
973
948
  0.95 calc(var(--ty-neutral-chroma) * 2) var(--ty-neutral-hue)
974
949
  );
@@ -976,40 +951,12 @@ html[data-theme="dark"] {
976
951
  0.15 calc(var(--ty-neutral-chroma) * 2) var(--ty-neutral-hue)
977
952
  );
978
953
 
979
- /* Global neutral borders */
980
- --ty-border-strong: var(--ty-color-neutral-bold);
981
- --ty-border: var(--ty-color-neutral-soft);
982
- --ty-border-bold: var(--ty-color-neutral-soft);
983
- --ty-border-soft: var(--ty-color-neutral-faint);
984
- --ty-border-faint: var(--ty-color-neutral-faint);
985
-
986
- /* Surfaces — monochrome by default; --ty-surface-chroma warms them. */
987
- --ty-surface-canvas: oklch(0.12 var(--ty-surface-chroma) var(--ty-brand-hue));
988
- --ty-surface-content: oklch(0.16 var(--ty-surface-chroma) var(--ty-brand-hue));
989
- --ty-surface-elevated: oklch(0.19 var(--ty-surface-chroma) var(--ty-brand-hue));
990
- --ty-surface-floating: oklch(0.22 var(--ty-surface-chroma) var(--ty-brand-hue));
991
- --ty-surface-input: oklch(0.18 var(--ty-surface-chroma) var(--ty-brand-hue));
992
-
993
- /* Inputs — re-declared at html.dark specificity. */
994
- --ty-input-bg: var(--ty-surface-input);
995
- --ty-input-color: var(--ty-color-neutral-strong);
996
- --ty-input-border: var(--ty-color-neutral-faint);
997
- --ty-input-border-hover: var(--ty-color-neutral-soft);
998
- --ty-input-border-focus: var(--ty-color-primary);
999
- --ty-input-placeholder: var(--ty-color-neutral-soft);
1000
- --ty-input-disabled-bg: var(--ty-color-neutral-faint);
1001
- --ty-input-disabled-border: var(--ty-color-neutral-faint);
1002
- --ty-input-disabled-color: var(--ty-color-neutral-bold);
1003
- --ty-input-shadow-focus: color-mix(
1004
- in oklab,
1005
- var(--ty-color-primary) 20%,
1006
- transparent
1007
- );
1008
-
1009
- /* Tier 6 dark — solid dials live in SECTION 1's html.dark block.
1010
- * neutral is hand-pinned (it ignores Axis B); dim it per theme here. */
954
+ /* Neutral solid fill is an "ink" — dark grey in BOTH modes (0.4 light),
955
+ * dimmed a notch here. It ignores the emphasis curve and Axis B. */
1011
956
  --ty-solid-neutral: oklch(
1012
- 0.3 calc(var(--ty-neutral-chroma) * var(--ty-c-strong-mult))
957
+ 0.23 calc(var(--ty-neutral-chroma) * var(--ty-c-strong-mult))
1013
958
  var(--ty-neutral-hue)
1014
959
  );
960
+
961
+ --ty-neutral-l-factor: 0.8;
1015
962
  }