@axiapps/axi-design 1.40.0 → 1.42.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/dist/axi.css +129 -3
- package/dist/themes/flat.css +2 -0
- package/dist/themes/glass.css +3 -0
- package/docs/RULES.md +22 -1
- package/package.json +1 -1
- package/src/forms.css +0 -2
- package/src/tokens.css +57 -0
- package/src/utilities.css +72 -1
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
|
|
@@ -1181,8 +1238,6 @@ button.axi-panel--tile:hover,
|
|
|
1181
1238
|
transform: rotate(45deg);
|
|
1182
1239
|
}
|
|
1183
1240
|
.axi-radio:checked::after { opacity: 1; }
|
|
1184
|
-
.axi-check:disabled,
|
|
1185
|
-
.axi-radio:disabled { cursor: not-allowed; opacity: .5; }
|
|
1186
1241
|
|
|
1187
1242
|
/* ---------- textarea ---------- */
|
|
1188
1243
|
/* Not a component: the same .axi-input a single-line field uses, with the two
|
|
@@ -3055,7 +3110,8 @@ textarea.axi-input {
|
|
|
3055
3110
|
|
|
3056
3111
|
/* --- utilities.css --- */
|
|
3057
3112
|
/* axi design language - utilities.
|
|
3058
|
-
Two families of single-purpose class, and the file is last in
|
|
3113
|
+
Two families of single-purpose class and one state, and the file is last in
|
|
3114
|
+
the cascade
|
|
3059
3115
|
because that is the whole point of it: a utility has to be able to overrule
|
|
3060
3116
|
the component it sits inside, and it is not allowed to buy that with
|
|
3061
3117
|
!important or with a padded selector.
|
|
@@ -3108,3 +3164,73 @@ textarea.axi-input {
|
|
|
3108
3164
|
.axi-edge-accent { border-color: var(--axi-accent); }
|
|
3109
3165
|
.axi-edge-rule { border-color: var(--axi-rule); }
|
|
3110
3166
|
.axi-edge-line { border-color: var(--axi-ink-line); }
|
|
3167
|
+
|
|
3168
|
+
/* ---------- the dead state ---------- */
|
|
3169
|
+
/* Twenty-four things in this language are interactive: twenty-three declare
|
|
3170
|
+
`cursor: pointer`, and the twenty-fourth is `.axi-input`, which both the
|
|
3171
|
+
single-line field and the textarea wear. Before this rule, two of the
|
|
3172
|
+
twenty-four said anything at all when they were disabled - the checkbox and
|
|
3173
|
+
the radio, which got `opacity: .5; cursor: not-allowed` in forms.css. The
|
|
3174
|
+
other twenty-three were, computed-style for computed-style, indistinguishable
|
|
3175
|
+
from their working selves: a disabled `.axi-btn` drew the same fill, the same
|
|
3176
|
+
border and the same ink as an enabled one, and still told the cursor it was a
|
|
3177
|
+
pointer. The control did not merely fail to look dead; it actively promised
|
|
3178
|
+
it was alive.
|
|
3179
|
+
|
|
3180
|
+
So every consumer invented the state. In the app this language was built
|
|
3181
|
+
for, twenty-four sites had written `disabled:opacity-50
|
|
3182
|
+
disabled:cursor-not-allowed` by hand - the same two declarations forms.css
|
|
3183
|
+
already contained, retyped once per control - and a replay stepper had
|
|
3184
|
+
invented a third answer, swapping the ink to a dimmer token instead.
|
|
3185
|
+
|
|
3186
|
+
Why opacity and the cursor, and deliberately nothing else. A disabled
|
|
3187
|
+
control has to stay recognisable as the control it is: whatever fill, border
|
|
3188
|
+
and ink said "primary button" or "danger action" a moment ago must still say
|
|
3189
|
+
it, only faded. Opacity does that to every layer at once without naming any
|
|
3190
|
+
of them, which is also the one form this can take that does not fight the
|
|
3191
|
+
ink layer below - the stepper's colour swap is exactly the defect the ink
|
|
3192
|
+
layer exists to prevent, because a disabled `.axi-action.axi-ink-danger`
|
|
3193
|
+
would lose its verdict rather than fade with it.
|
|
3194
|
+
|
|
3195
|
+
Why this file. A disabled state has to overrule the resting rule of the
|
|
3196
|
+
component it sits inside, and it is not allowed to buy that with
|
|
3197
|
+
!important - which is this file's whole reason for existing, and the reason
|
|
3198
|
+
the state lives here rather than beside each component. `:is()` carries its
|
|
3199
|
+
most specific argument's weight, so each of these selectors weighs one class
|
|
3200
|
+
and one pseudo-class: enough to beat any component's own resting rule, and
|
|
3201
|
+
nothing upstream sets `cursor` or `opacity` on a hover that could out-rank
|
|
3202
|
+
it.
|
|
3203
|
+
|
|
3204
|
+
Pointer events are left alone. `:disabled` already blocks activation
|
|
3205
|
+
natively, and `[aria-disabled="true"]` means "focusable, but ignore the
|
|
3206
|
+
action" - a control the reader can still reach and still read a title from.
|
|
3207
|
+
Removing pointer events would take away the tooltip that explains why the
|
|
3208
|
+
thing is dead, which is the one piece of help a dead control can offer. */
|
|
3209
|
+
.axi-accordion__head:is(:disabled, [aria-disabled="true"]),
|
|
3210
|
+
.axi-action:is(:disabled, [aria-disabled="true"]),
|
|
3211
|
+
.axi-btn:is(:disabled, [aria-disabled="true"]),
|
|
3212
|
+
.axi-card:is(:disabled, [aria-disabled="true"]),
|
|
3213
|
+
.axi-check:is(:disabled, [aria-disabled="true"]),
|
|
3214
|
+
.axi-chip--action:is(:disabled, [aria-disabled="true"]),
|
|
3215
|
+
.axi-drawer__close:is(:disabled, [aria-disabled="true"]),
|
|
3216
|
+
.axi-input:is(:disabled, [aria-disabled="true"]),
|
|
3217
|
+
.axi-link:is(:disabled, [aria-disabled="true"]),
|
|
3218
|
+
.axi-palette__row:is(:disabled, [aria-disabled="true"]),
|
|
3219
|
+
.axi-palette__trigger:is(:disabled, [aria-disabled="true"]),
|
|
3220
|
+
.axi-picker__btn:is(:disabled, [aria-disabled="true"]),
|
|
3221
|
+
.axi-picker__opt:is(:disabled, [aria-disabled="true"]),
|
|
3222
|
+
.axi-pill:is(:disabled, [aria-disabled="true"]),
|
|
3223
|
+
.axi-radio:is(:disabled, [aria-disabled="true"]),
|
|
3224
|
+
.axi-rail__item:is(:disabled, [aria-disabled="true"]),
|
|
3225
|
+
.axi-rail__subitem:is(:disabled, [aria-disabled="true"]),
|
|
3226
|
+
.axi-select:is(:disabled, [aria-disabled="true"]),
|
|
3227
|
+
.axi-switch:is(:disabled, [aria-disabled="true"]),
|
|
3228
|
+
.axi-table__sort:is(:disabled, [aria-disabled="true"]),
|
|
3229
|
+
.axi-menu__pop label:is(:disabled, [aria-disabled="true"]),
|
|
3230
|
+
.axi-prose a:is(:disabled, [aria-disabled="true"]),
|
|
3231
|
+
.axi-tabs a:is(:disabled, [aria-disabled="true"]),
|
|
3232
|
+
.axi-tabs button:is(:disabled, [aria-disabled="true"]),
|
|
3233
|
+
.axi-titlebar__btns button:is(:disabled, [aria-disabled="true"]) {
|
|
3234
|
+
opacity: .5;
|
|
3235
|
+
cursor: not-allowed;
|
|
3236
|
+
}
|
package/dist/themes/flat.css
CHANGED
|
@@ -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);
|
package/dist/themes/glass.css
CHANGED
|
@@ -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`,
|
|
@@ -631,7 +650,9 @@ first step back toward theme-only components.
|
|
|
631
650
|
|
|
632
651
|
Rules 1 and 2 name the relief a theme gets in the surface layer: a theme may
|
|
633
652
|
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.
|
|
653
|
+
may not mute an ink. Read those two rules for why. A theme taking the gradient
|
|
654
|
+
half of that relief owes the `-paint` companion of every surface it grades —
|
|
655
|
+
see rule 1.
|
|
635
656
|
|
|
636
657
|
The relief is not confined to that layer, though, and the first draft of this
|
|
637
658
|
section said it was. **A theme may also restate the block and the corner** —
|
package/package.json
CHANGED
package/src/forms.css
CHANGED
|
@@ -73,8 +73,6 @@
|
|
|
73
73
|
transform: rotate(45deg);
|
|
74
74
|
}
|
|
75
75
|
.axi-radio:checked::after { opacity: 1; }
|
|
76
|
-
.axi-check:disabled,
|
|
77
|
-
.axi-radio:disabled { cursor: not-allowed; opacity: .5; }
|
|
78
76
|
|
|
79
77
|
/* ---------- textarea ---------- */
|
|
80
78
|
/* Not a component: the same .axi-input a single-line field uses, with the two
|
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
|
package/src/utilities.css
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* axi design language - utilities.
|
|
2
|
-
Two families of single-purpose class, and the file is last in
|
|
2
|
+
Two families of single-purpose class and one state, and the file is last in
|
|
3
|
+
the cascade
|
|
3
4
|
because that is the whole point of it: a utility has to be able to overrule
|
|
4
5
|
the component it sits inside, and it is not allowed to buy that with
|
|
5
6
|
!important or with a padded selector.
|
|
@@ -52,3 +53,73 @@
|
|
|
52
53
|
.axi-edge-accent { border-color: var(--axi-accent); }
|
|
53
54
|
.axi-edge-rule { border-color: var(--axi-rule); }
|
|
54
55
|
.axi-edge-line { border-color: var(--axi-ink-line); }
|
|
56
|
+
|
|
57
|
+
/* ---------- the dead state ---------- */
|
|
58
|
+
/* Twenty-four things in this language are interactive: twenty-three declare
|
|
59
|
+
`cursor: pointer`, and the twenty-fourth is `.axi-input`, which both the
|
|
60
|
+
single-line field and the textarea wear. Before this rule, two of the
|
|
61
|
+
twenty-four said anything at all when they were disabled - the checkbox and
|
|
62
|
+
the radio, which got `opacity: .5; cursor: not-allowed` in forms.css. The
|
|
63
|
+
other twenty-three were, computed-style for computed-style, indistinguishable
|
|
64
|
+
from their working selves: a disabled `.axi-btn` drew the same fill, the same
|
|
65
|
+
border and the same ink as an enabled one, and still told the cursor it was a
|
|
66
|
+
pointer. The control did not merely fail to look dead; it actively promised
|
|
67
|
+
it was alive.
|
|
68
|
+
|
|
69
|
+
So every consumer invented the state. In the app this language was built
|
|
70
|
+
for, twenty-four sites had written `disabled:opacity-50
|
|
71
|
+
disabled:cursor-not-allowed` by hand - the same two declarations forms.css
|
|
72
|
+
already contained, retyped once per control - and a replay stepper had
|
|
73
|
+
invented a third answer, swapping the ink to a dimmer token instead.
|
|
74
|
+
|
|
75
|
+
Why opacity and the cursor, and deliberately nothing else. A disabled
|
|
76
|
+
control has to stay recognisable as the control it is: whatever fill, border
|
|
77
|
+
and ink said "primary button" or "danger action" a moment ago must still say
|
|
78
|
+
it, only faded. Opacity does that to every layer at once without naming any
|
|
79
|
+
of them, which is also the one form this can take that does not fight the
|
|
80
|
+
ink layer below - the stepper's colour swap is exactly the defect the ink
|
|
81
|
+
layer exists to prevent, because a disabled `.axi-action.axi-ink-danger`
|
|
82
|
+
would lose its verdict rather than fade with it.
|
|
83
|
+
|
|
84
|
+
Why this file. A disabled state has to overrule the resting rule of the
|
|
85
|
+
component it sits inside, and it is not allowed to buy that with
|
|
86
|
+
!important - which is this file's whole reason for existing, and the reason
|
|
87
|
+
the state lives here rather than beside each component. `:is()` carries its
|
|
88
|
+
most specific argument's weight, so each of these selectors weighs one class
|
|
89
|
+
and one pseudo-class: enough to beat any component's own resting rule, and
|
|
90
|
+
nothing upstream sets `cursor` or `opacity` on a hover that could out-rank
|
|
91
|
+
it.
|
|
92
|
+
|
|
93
|
+
Pointer events are left alone. `:disabled` already blocks activation
|
|
94
|
+
natively, and `[aria-disabled="true"]` means "focusable, but ignore the
|
|
95
|
+
action" - a control the reader can still reach and still read a title from.
|
|
96
|
+
Removing pointer events would take away the tooltip that explains why the
|
|
97
|
+
thing is dead, which is the one piece of help a dead control can offer. */
|
|
98
|
+
.axi-accordion__head:is(:disabled, [aria-disabled="true"]),
|
|
99
|
+
.axi-action:is(:disabled, [aria-disabled="true"]),
|
|
100
|
+
.axi-btn:is(:disabled, [aria-disabled="true"]),
|
|
101
|
+
.axi-card:is(:disabled, [aria-disabled="true"]),
|
|
102
|
+
.axi-check:is(:disabled, [aria-disabled="true"]),
|
|
103
|
+
.axi-chip--action:is(:disabled, [aria-disabled="true"]),
|
|
104
|
+
.axi-drawer__close:is(:disabled, [aria-disabled="true"]),
|
|
105
|
+
.axi-input:is(:disabled, [aria-disabled="true"]),
|
|
106
|
+
.axi-link:is(:disabled, [aria-disabled="true"]),
|
|
107
|
+
.axi-palette__row:is(:disabled, [aria-disabled="true"]),
|
|
108
|
+
.axi-palette__trigger:is(:disabled, [aria-disabled="true"]),
|
|
109
|
+
.axi-picker__btn:is(:disabled, [aria-disabled="true"]),
|
|
110
|
+
.axi-picker__opt:is(:disabled, [aria-disabled="true"]),
|
|
111
|
+
.axi-pill:is(:disabled, [aria-disabled="true"]),
|
|
112
|
+
.axi-radio:is(:disabled, [aria-disabled="true"]),
|
|
113
|
+
.axi-rail__item:is(:disabled, [aria-disabled="true"]),
|
|
114
|
+
.axi-rail__subitem:is(:disabled, [aria-disabled="true"]),
|
|
115
|
+
.axi-select:is(:disabled, [aria-disabled="true"]),
|
|
116
|
+
.axi-switch:is(:disabled, [aria-disabled="true"]),
|
|
117
|
+
.axi-table__sort:is(:disabled, [aria-disabled="true"]),
|
|
118
|
+
.axi-menu__pop label:is(:disabled, [aria-disabled="true"]),
|
|
119
|
+
.axi-prose a:is(:disabled, [aria-disabled="true"]),
|
|
120
|
+
.axi-tabs a:is(:disabled, [aria-disabled="true"]),
|
|
121
|
+
.axi-tabs button:is(:disabled, [aria-disabled="true"]),
|
|
122
|
+
.axi-titlebar__btns button:is(:disabled, [aria-disabled="true"]) {
|
|
123
|
+
opacity: .5;
|
|
124
|
+
cursor: not-allowed;
|
|
125
|
+
}
|