@axiapps/axi-design 1.41.0 → 1.43.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.
package/accents.json CHANGED
@@ -9,5 +9,6 @@
9
9
  { "id": "crimson-red", "label": "Crimson Red", "hex": "#ef4444" },
10
10
  { "id": "slate-silver", "label": "Slate Silver", "hex": "#94a3b8" },
11
11
  { "id": "teal-ocean", "label": "Teal Ocean", "hex": "#14b8a6" },
12
- { "id": "gold-bronze", "label": "Gold Bronze", "hex": "#d4a017" }
12
+ { "id": "gold-bronze", "label": "Gold Bronze", "hex": "#d4a017" },
13
+ { "id": "electric-cyan", "label": "Electric Cyan", "hex": "#22d3ee" }
13
14
  ]
package/dist/accents.css CHANGED
@@ -12,3 +12,4 @@
12
12
  [data-axi-accent="slate-silver"] { --axi-accent: #94a3b8; }
13
13
  [data-axi-accent="teal-ocean"] { --axi-accent: #14b8a6; }
14
14
  [data-axi-accent="gold-bronze"] { --axi-accent: #d4a017; }
15
+ [data-axi-accent="electric-cyan"] { --axi-accent: #22d3ee; }
package/dist/axi.css CHANGED
@@ -54,6 +54,63 @@
54
54
  own token, and a theme that goes translucent restates it near-opaque
55
55
  without giving up the tint on everything that merely sits still. */
56
56
  --axi-surface-float: var(--axi-surface);
57
+ /* --- a surface as a single paint --- */
58
+ /* The three surface tokens above, each reduced to one flat <color>.
59
+
60
+ They exist because a surface token is allowed to hold a gradient - rule 1's
61
+ one relief, which is how a glass theme exists at all - and a token holding
62
+ a gradient can only ever BE a background. `fill` takes <paint> and not
63
+ <image>. `color-mix()` takes colours and nothing else. `background-color`
64
+ and `border-color` take a colour. So every consumer that wanted a
65
+ surface-coloured chart fill, or a surface mixed some way toward the accent,
66
+ had nothing in this language to ask for.
67
+
68
+ Nothing in this package noticed, because every component here paints a
69
+ surface through the `background` shorthand, where an image is welcome. The
70
+ cost landed downstream, and the way it lands is silent: an invalid `fill`
71
+ is dropped at computed-value time, and `fill` inherits, so the element
72
+ adopts its ancestor's paint rather than showing anything wrong. Measured in
73
+ axibridge under the flat theme - a chart's brush slide and its bar cursor
74
+ both computed rgb(0, 0, 0), and a heat ramp built on color-mix() lost its
75
+ fill altogether. The app had papered over the glass half by pinning two
76
+ flat values of its own by eye, and had no way to paper over flat.
77
+
78
+ Why a companion rather than splitting each surface into a colour and an
79
+ `-image` half, the way --axi-ground is split. That was tried first and
80
+ measured. It works for the ground because the ground is OPAQUE: the colour
81
+ is the page and the image is light added over it. It does not transfer to a
82
+ translucent surface, where the gradient is not light on a fill, it IS the
83
+ fill - alpha and all. Painting a glass panel as an absolute gradient over a
84
+ flat base composites the two and takes the panel from .55 to .77 opacity,
85
+ which is 15 RGB steps of visible change. Fitting a base and a modulation
86
+ that reproduce the original to within 2 steps is possible, and the fit
87
+ lands on rgba(28, 41, 65, .361) - a set of constants no one can maintain,
88
+ carrying an alpha .12 below the surface it is meant to be a reduction of,
89
+ which would make a chart fill read as a different material from the panel
90
+ beside it. A translucent surface genuinely is an image, and "that surface
91
+ as one flat paint" is genuinely a second thing derived from it.
92
+
93
+ What the value has to be is not a judgement each theme makes fresh: it is
94
+ the midpoint of that theme's gradient, the colour the gradient is a spread
95
+ around. Both shipped themes confirm it independently - flat's two midpoints
96
+ come out at exactly the #1a1f2e and #222838 its own notes call "axibridge's
97
+ flat values", and glass's two land within three per channel, at an
98
+ identical alpha, of the pair axibridge had already pinned by eye. The
99
+ colour was always there; it was never a token.
100
+
101
+ Aliased here because the main theme's surfaces are flat colours already, so
102
+ there is nothing to reduce. A theme that puts an image in a surface token
103
+ must restate that token's companion, or the alias hands the image straight
104
+ back - themes.test.mjs asserts exactly that, and it is the only way this
105
+ token can go wrong. Note the float's companion follows --axi-surface-paint
106
+ and NOT --axi-surface-float: the float's own default is var(--axi-surface),
107
+ so aliasing the float would walk the chain back into a theme's gradient in
108
+ any theme that restates the surface and leaves the float alone - which is
109
+ what flat does. */
110
+ --axi-surface-paint: var(--axi-surface);
111
+ --axi-surface-raised-paint: var(--axi-surface-raised);
112
+ --axi-surface-float-paint: var(--axi-surface-paint);
113
+
57
114
  --axi-ink-line: #0c0e12; /* the outline every raised element is drawn with */
58
115
  /* The tone a shape is made of when it is made of the line ink rather than
59
116
  outlined in it - the tooltip, the titlebar strip, the switch's slug. Holds
@@ -1959,7 +2016,44 @@ textarea.axi-input {
1959
2016
  .axi-card:hover .axi-card__go { color: var(--axi-text); }
1960
2017
 
1961
2018
  /* ---------- drawer ---------- */
2019
+ /* A scrim's layer is not a property of the scrim. It is "directly below the
2020
+ thing I dismiss", and this language has two things that get dismissed at two
2021
+ different rungs, so the scrim has two.
2022
+
2023
+ The default is the drawer-and-modal rung at 50, which is where
2024
+ --axi-scrim's own value was tuned (its token comment reads "behind a
2025
+ drawer"). `.axi-scrim--sheet` is the same scrim one rung below the sheet at
2026
+ 45.
2027
+
2028
+ Without that modifier a sheet cannot be scrimmed at all, and the failure is
2029
+ not cosmetic - it is a dead interface. Both elements are `position: fixed` in
2030
+ the same stacking context with no isolation between them, so at 50 over 45
2031
+ the scrim covers the sheet completely: measured with
2032
+ `document.elementFromPoint` at the centre of an open sheet, the element
2033
+ returned is the scrim. Every click lands on the dismiss handler instead of on
2034
+ the content, so the sheet opens and then does nothing.
2035
+
2036
+ The sheet still wants a scrim even though it is opaque and covers the view,
2037
+ for two reasons the sheet's own note explains. --axi-sheet-top pushes it down
2038
+ under an app's titlebar, so a strip of live page stays visible above it and
2039
+ wants darkening; and the scrim is the click-to-dismiss target for that strip,
2040
+ which is a function rather than an appearance.
2041
+
2042
+ A sheet that opens a modal or a drawer is unaffected: those land at 50 and
2043
+ 51, above both the sheet and its scrim, which is the ordering the sheet's own
2044
+ comment argues for. That is why this is a second rung and not a change to the
2045
+ first - moving the scrim below 45 outright would put it under the drawer it
2046
+ was measured for.
2047
+
2048
+ No motion here, deliberately. This language has no transition vocabulary -
2049
+ no duration or easing tokens - and rule 11 is a restriction on work
2050
+ indicators rather than a system for animating chrome. A consumer that fades
2051
+ a scrim in owns that animation, and owns keeping its keyframes off the fill:
2052
+ an enter/exit class that also sets a background is a colour literal standing
2053
+ on top of --axi-scrim, which is how the one consumer here had quietly
2054
+ replaced the token with rgba(10, 14, 18, .85). */
1962
2055
  .axi-scrim { position: fixed; inset: 0; z-index: 50; background: var(--axi-scrim); backdrop-filter: var(--axi-surface-filter); border: 0; }
2056
+ .axi-scrim--sheet { z-index: 44; }
1963
2057
  .axi-drawer {
1964
2058
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 51;
1965
2059
  width: min(var(--axi-drawer-width, 560px), 100vw);
@@ -4,7 +4,9 @@
4
4
  [data-axi-theme="flat"] {
5
5
  --axi-ground: #090b10;
6
6
  --axi-surface: linear-gradient(#1c2130, #181d2c);
7
+ --axi-surface-paint: #1a1f2e;
7
8
  --axi-surface-raised: linear-gradient(#242a3b, #202636);
9
+ --axi-surface-raised-paint: #222838;
8
10
  --axi-well-fill: #141822;
9
11
  --axi-ink-line: rgba(255, 255, 255, .10);
10
12
  --axi-rule: rgba(255, 255, 255, .07);
@@ -5,8 +5,11 @@
5
5
  --axi-ground: #0a0c10;
6
6
  --axi-ground-image: radial-gradient(900px 500px at 15% 0%, rgba(120, 90, 255, .30), transparent 60%), radial-gradient(700px 500px at 95% 30%, rgba(0, 190, 255, .22), transparent 62%), radial-gradient(600px 600px at 60% 100%, rgba(255, 90, 170, .20), transparent 60%);
7
7
  --axi-surface: linear-gradient(145deg, rgba(58, 68, 92, .55), rgba(28, 33, 46, .42));
8
+ --axi-surface-paint: rgba(43, 50, 69, .485);
8
9
  --axi-surface-raised: linear-gradient(145deg, rgba(86, 99, 130, .5), rgba(44, 52, 71, .38));
10
+ --axi-surface-raised-paint: rgba(65, 76, 100, .44);
9
11
  --axi-surface-float: linear-gradient(145deg, rgba(38, 45, 62, .97), rgba(22, 26, 36, .97));
12
+ --axi-surface-float-paint: rgba(30, 36, 49, .97);
10
13
  --axi-surface-filter: blur(18px) saturate(140%);
11
14
  --axi-ink-line: rgba(180, 200, 255, .16);
12
15
  --axi-rule: rgba(180, 200, 255, .14);
package/docs/RULES.md CHANGED
@@ -22,6 +22,23 @@ what lets a glass theme exist without one component ever learning the word
22
22
  "glass". A component cannot reach for that exception, because it cannot see it:
23
23
  what it reads is the same surface token it was already reading.
24
24
 
25
+ That relief has a consequence worth stating next to it, because it is not
26
+ visible from inside this package. A surface token that may hold a gradient can
27
+ only ever *be* a background: `fill` takes `<paint>` and not `<image>`,
28
+ `color-mix()` takes colours only, `background-color` and `border-color` take a
29
+ colour. Every component here paints a surface through the `background`
30
+ shorthand and so never meets this — but a consumer wanting a surface-coloured
31
+ chart fill, or a surface mixed some way toward the accent, has nothing to ask
32
+ for, and the failure is silent: an invalid `fill` drops at computed-value time
33
+ and `fill` inherits, so the element takes its ancestor's paint and renders
34
+ something plausible. So each surface has a **`-paint` companion** —
35
+ `--axi-surface-paint`, `--axi-surface-raised-paint`,
36
+ `--axi-surface-float-paint` — holding the same surface as one flat `<color>`.
37
+ Paint a surface with the surface token; reach for the companion wherever only a
38
+ colour is valid. A theme that grades a surface must restate that surface's
39
+ companion, and `themes.test.mjs` asserts it: the companions are aliased to the
40
+ surfaces, so a theme that forgets one hands its gradient straight back.
41
+
25
42
  ## 2. No colour at partial opacity over the ground
26
43
 
27
44
  If a colour is present it is at full strength. A muted gold over near-black is
@@ -490,6 +507,8 @@ Three layers, in `src/tokens.css` — the only file permitted to contain a colou
490
507
  literal.
491
508
 
492
509
  - **Surface & text** — `--axi-ground`, `--axi-surface`, `--axi-surface-raised`,
510
+ `--axi-surface-float`, their three `-paint` companions (the same surfaces as a
511
+ single `<color>`, for `fill`, `color-mix()` and the `*-color` properties),
493
512
  `--axi-ink-line`, `--axi-ground-deep`, `--axi-rule`, `--axi-text`,
494
513
  `--axi-text-dim`, `--axi-text-faint`, `--axi-scrim`
495
514
  - **Accent & status** — `--axi-accent`, `--axi-accent-ink`, `--axi-meta`,
@@ -529,6 +548,7 @@ instead of joining it.
529
548
  | Table corner | 3 | where the two cross |
530
549
  | Sticky chrome | 40 | `.axi-mast` |
531
550
  | Popovers | 41 | `.axi-menu__pop`, `.axi-picker__pop` |
551
+ | Sheet scrim | 44 | `.axi-scrim--sheet` |
532
552
  | Sheet | 45 | `.axi-sheet` |
533
553
  | Scrim | 50 | `.axi-scrim` |
534
554
  | Drawer | 51 | `.axi-drawer` |
@@ -553,6 +573,15 @@ A negative `z-index` inside a component's own `isolation` context — the sigil'
553
573
  backing shape — is not a layer and is not listed. It is invisible outside the
554
574
  component that owns it.
555
575
 
576
+ The scrim appears twice, and that is the table saying something rather than
577
+ repeating itself. A scrim's rung is not a property of the scrim; it is "directly
578
+ below the thing I dismiss", so a language with two dismissible surfaces at two
579
+ rungs has two scrims. Reading the single 50 as the scrim's own number is what
580
+ makes a sheet impossible to scrim: at 50 over the sheet's 45 the scrim covers
581
+ the sheet completely, every click lands on the dismiss handler, and the surface
582
+ opens dead. So when you add a dismissible surface, check whether it needs a
583
+ scrim rung directly beneath it, and add both rows together.
584
+
556
585
  ## Light mode
557
586
 
558
587
  Not shipped. The system is *structured* for it: no component contains a colour
@@ -631,7 +660,9 @@ first step back toward theme-only components.
631
660
 
632
661
  Rules 1 and 2 name the relief a theme gets in the surface layer: a theme may
633
662
  put a gradient on a surface and may hold a surface token at partial opacity. It
634
- may not mute an ink. Read those two rules for why.
663
+ may not mute an ink. Read those two rules for why. A theme taking the gradient
664
+ half of that relief owes the `-paint` companion of every surface it grades —
665
+ see rule 1.
635
666
 
636
667
  The relief is not confined to that layer, though, and the first draft of this
637
668
  section said it was. **A theme may also restate the block and the corner** —
@@ -663,6 +694,44 @@ The line to hold is the one-for-one rule above, not a list of layers. A theme
663
694
  that restates the block still paints every component; a theme that invents one
664
695
  does not.
665
696
 
697
+ ### A change to the language is not finished until every theme wears it
698
+
699
+ The one-for-one rule above is written as an obligation on a *theme* — here is
700
+ what a new theme owes the language. Read only that way it has a hole in it, and
701
+ the hole is every change that goes the other direction. A token added to
702
+ `tokens.css`, a component added to `src/`, a look retuned: each of those is a
703
+ change to the thing the themes are mirroring, and none of them is finished when
704
+ the main theme looks right. There are three themes — the language itself in
705
+ `src/tokens.css`, `flat`, and `glass` — and a change lands in all three or it
706
+ has not landed.
707
+
708
+ That is the symmetric half of the toll already stated above. **A new theme
709
+ capability costs a main-theme token first; a change to the main theme costs
710
+ every theme a look.** Neither direction is optional, and the second is the one
711
+ easy to skip, because the default theme is the one on screen while you work.
712
+
713
+ What "answered in every theme" means depends on the shape of the change:
714
+
715
+ - **A new token.** Every theme either restates it or can point at why it does
716
+ not need to. Two reasons count. The default is inert — `--axi-surface-filter`
717
+ and `--axi-ground-image` are `none`, so a theme that wants neither is already
718
+ correct. Or the token aliases one the theme did restate —
719
+ `--axi-surface-float: var(--axi-surface)`, so `flat` restating the surface
720
+ restates the float with it. A token holding a literal of its own is answered
721
+ by neither of those, and every theme has to say it. This is checked; see
722
+ below.
723
+ - **A new component.** It renders under all three, and you look at it under all
724
+ three. The gallery's theme switcher is there for exactly the reason the accent
725
+ switcher is: a component that hard-coded something looks fine until you
726
+ change the thing it hard-coded. A panel that reads as a panel on opaque slate
727
+ can vanish on a translucent one.
728
+ - **A retuned look.** The `-paint` companions are the case that made this a
729
+ section. Lifting them meant every surface a theme grades needs a flat
730
+ companion beside it, and both themes had to be edited in the same commit as
731
+ the tokens — edit one and the other hands a gradient straight to
732
+ `background-color`, which is not a subtle failure but it is an invisible one
733
+ from the theme you happened to be looking at.
734
+
666
735
  **What is mechanically enforced.** `tests/themes.test.mjs` reads every
667
736
  `dist/themes/*.css` and checks the mirror rather than trusting it: the file
668
737
  contains exactly one rule, its selector is `[data-axi-theme="<id>"]` for the
@@ -672,6 +741,16 @@ non-empty value, and every property it declares is already declared in
672
741
  takes a second selector. An invented token fails the last. The suite passes
673
742
  vacuously while no theme exists, and binds the moment the first file lands.
674
743
 
744
+ The section above is checked from the other side by the same file: a token that
745
+ *any* theme restates must be restated by *every* theme, unless that theme
746
+ inherits an answer already — the main-theme default is inert, or the token
747
+ aliases another the theme did restate, and the check follows the alias chain
748
+ rather than taking the two reasons on trust. So a token one theme has an opinion
749
+ about cannot be a token another theme forgot. What no test can check is the
750
+ third bullet, the look you did not look at: a component can paint under all
751
+ three themes and still be wrong under two of them, and the only instrument for
752
+ that is the theme switcher in the gallery.
753
+
675
754
  ## Adding a component
676
755
 
677
756
  1. Which rule justifies it? If none, write the rule first or stop.
@@ -681,7 +760,11 @@ vacuously while no theme exists, and binds the moment the first file lands.
681
760
  [Themes](#themes).
682
761
  4. Add it to the gallery, and check it with the accent switcher — if it does
683
762
  not follow the accent, it hard-coded something.
684
- 5. `npm run build` and commit `dist/axi.css` with your source change.
763
+ 5. Then check it with the theme switcher, under all three — the language,
764
+ `flat` and `glass`. Translucent surfaces and a 16px corner break different
765
+ things than opaque ones do, and a component is not done until it reads right
766
+ under each. See [Themes](#themes).
767
+ 6. `npm run build` and commit `dist/axi.css` with your source change.
685
768
 
686
769
  ### A style only reachable through a layer will be re-invented
687
770
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axiapps/axi-design",
3
- "version": "1.41.0",
3
+ "version": "1.43.0",
4
4
  "description": "The design language for the axi suite — flat and outlined, dark, drawn in saturated ink.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/shells.css CHANGED
@@ -708,7 +708,44 @@
708
708
  .axi-card:hover .axi-card__go { color: var(--axi-text); }
709
709
 
710
710
  /* ---------- drawer ---------- */
711
+ /* A scrim's layer is not a property of the scrim. It is "directly below the
712
+ thing I dismiss", and this language has two things that get dismissed at two
713
+ different rungs, so the scrim has two.
714
+
715
+ The default is the drawer-and-modal rung at 50, which is where
716
+ --axi-scrim's own value was tuned (its token comment reads "behind a
717
+ drawer"). `.axi-scrim--sheet` is the same scrim one rung below the sheet at
718
+ 45.
719
+
720
+ Without that modifier a sheet cannot be scrimmed at all, and the failure is
721
+ not cosmetic - it is a dead interface. Both elements are `position: fixed` in
722
+ the same stacking context with no isolation between them, so at 50 over 45
723
+ the scrim covers the sheet completely: measured with
724
+ `document.elementFromPoint` at the centre of an open sheet, the element
725
+ returned is the scrim. Every click lands on the dismiss handler instead of on
726
+ the content, so the sheet opens and then does nothing.
727
+
728
+ The sheet still wants a scrim even though it is opaque and covers the view,
729
+ for two reasons the sheet's own note explains. --axi-sheet-top pushes it down
730
+ under an app's titlebar, so a strip of live page stays visible above it and
731
+ wants darkening; and the scrim is the click-to-dismiss target for that strip,
732
+ which is a function rather than an appearance.
733
+
734
+ A sheet that opens a modal or a drawer is unaffected: those land at 50 and
735
+ 51, above both the sheet and its scrim, which is the ordering the sheet's own
736
+ comment argues for. That is why this is a second rung and not a change to the
737
+ first - moving the scrim below 45 outright would put it under the drawer it
738
+ was measured for.
739
+
740
+ No motion here, deliberately. This language has no transition vocabulary -
741
+ no duration or easing tokens - and rule 11 is a restriction on work
742
+ indicators rather than a system for animating chrome. A consumer that fades
743
+ a scrim in owns that animation, and owns keeping its keyframes off the fill:
744
+ an enter/exit class that also sets a background is a colour literal standing
745
+ on top of --axi-scrim, which is how the one consumer here had quietly
746
+ replaced the token with rgba(10, 14, 18, .85). */
711
747
  .axi-scrim { position: fixed; inset: 0; z-index: 50; background: var(--axi-scrim); backdrop-filter: var(--axi-surface-filter); border: 0; }
748
+ .axi-scrim--sheet { z-index: 44; }
712
749
  .axi-drawer {
713
750
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 51;
714
751
  width: min(var(--axi-drawer-width, 560px), 100vw);
package/src/tokens.css CHANGED
@@ -50,6 +50,63 @@
50
50
  own token, and a theme that goes translucent restates it near-opaque
51
51
  without giving up the tint on everything that merely sits still. */
52
52
  --axi-surface-float: var(--axi-surface);
53
+ /* --- a surface as a single paint --- */
54
+ /* The three surface tokens above, each reduced to one flat <color>.
55
+
56
+ They exist because a surface token is allowed to hold a gradient - rule 1's
57
+ one relief, which is how a glass theme exists at all - and a token holding
58
+ a gradient can only ever BE a background. `fill` takes <paint> and not
59
+ <image>. `color-mix()` takes colours and nothing else. `background-color`
60
+ and `border-color` take a colour. So every consumer that wanted a
61
+ surface-coloured chart fill, or a surface mixed some way toward the accent,
62
+ had nothing in this language to ask for.
63
+
64
+ Nothing in this package noticed, because every component here paints a
65
+ surface through the `background` shorthand, where an image is welcome. The
66
+ cost landed downstream, and the way it lands is silent: an invalid `fill`
67
+ is dropped at computed-value time, and `fill` inherits, so the element
68
+ adopts its ancestor's paint rather than showing anything wrong. Measured in
69
+ axibridge under the flat theme - a chart's brush slide and its bar cursor
70
+ both computed rgb(0, 0, 0), and a heat ramp built on color-mix() lost its
71
+ fill altogether. The app had papered over the glass half by pinning two
72
+ flat values of its own by eye, and had no way to paper over flat.
73
+
74
+ Why a companion rather than splitting each surface into a colour and an
75
+ `-image` half, the way --axi-ground is split. That was tried first and
76
+ measured. It works for the ground because the ground is OPAQUE: the colour
77
+ is the page and the image is light added over it. It does not transfer to a
78
+ translucent surface, where the gradient is not light on a fill, it IS the
79
+ fill - alpha and all. Painting a glass panel as an absolute gradient over a
80
+ flat base composites the two and takes the panel from .55 to .77 opacity,
81
+ which is 15 RGB steps of visible change. Fitting a base and a modulation
82
+ that reproduce the original to within 2 steps is possible, and the fit
83
+ lands on rgba(28, 41, 65, .361) - a set of constants no one can maintain,
84
+ carrying an alpha .12 below the surface it is meant to be a reduction of,
85
+ which would make a chart fill read as a different material from the panel
86
+ beside it. A translucent surface genuinely is an image, and "that surface
87
+ as one flat paint" is genuinely a second thing derived from it.
88
+
89
+ What the value has to be is not a judgement each theme makes fresh: it is
90
+ the midpoint of that theme's gradient, the colour the gradient is a spread
91
+ around. Both shipped themes confirm it independently - flat's two midpoints
92
+ come out at exactly the #1a1f2e and #222838 its own notes call "axibridge's
93
+ flat values", and glass's two land within three per channel, at an
94
+ identical alpha, of the pair axibridge had already pinned by eye. The
95
+ colour was always there; it was never a token.
96
+
97
+ Aliased here because the main theme's surfaces are flat colours already, so
98
+ there is nothing to reduce. A theme that puts an image in a surface token
99
+ must restate that token's companion, or the alias hands the image straight
100
+ back - themes.test.mjs asserts exactly that, and it is the only way this
101
+ token can go wrong. Note the float's companion follows --axi-surface-paint
102
+ and NOT --axi-surface-float: the float's own default is var(--axi-surface),
103
+ so aliasing the float would walk the chain back into a theme's gradient in
104
+ any theme that restates the surface and leaves the float alone - which is
105
+ what flat does. */
106
+ --axi-surface-paint: var(--axi-surface);
107
+ --axi-surface-raised-paint: var(--axi-surface-raised);
108
+ --axi-surface-float-paint: var(--axi-surface-paint);
109
+
53
110
  --axi-ink-line: #0c0e12; /* the outline every raised element is drawn with */
54
111
  /* The tone a shape is made of when it is made of the line ink rather than
55
112
  outlined in it - the tooltip, the titlebar strip, the switch's slug. Holds