@axiapps/axi-design 1.16.0 → 1.18.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/README.md +5 -0
- package/dist/axi.css +312 -0
- package/dist/themes/glass.css +2 -0
- package/package.json +1 -1
- package/src/primitives.css +37 -0
- package/src/shells.css +239 -0
- package/src/tokens.css +36 -0
package/README.md
CHANGED
|
@@ -138,6 +138,9 @@ not "what does the system look like". Everything else is
|
|
|
138
138
|
| `--axi-check-size` | the size of an `.axi-check` or `.axi-radio` box | `22px` | `<input type="checkbox" class="axi-check" style="--axi-check-size: 16px">` |
|
|
139
139
|
| `--axi-check-fill` | the fill a checked `.axi-check` takes, and the colour of a checked `.axi-radio`'s diamond | `var(--axi-accent)` | `<input type="checkbox" class="axi-check" style="--axi-check-fill: var(--axi-danger)">` |
|
|
140
140
|
| `--axi-textarea-h` | the minimum height of a `<textarea class="axi-input">` | `90px` | `<textarea class="axi-input" style="--axi-textarea-h: 200px">` |
|
|
141
|
+
| `--axi-well-pad` | the padding inside an `.axi-well` | `10px` | `<div class="axi-well" style="--axi-well-pad: 18px">` |
|
|
142
|
+
| `--axi-rail-w` | an `.axi-rail`'s width, for labels longer than the default holds | `208px` | `<aside class="axi-rail" style="--axi-rail-w: 260px">` |
|
|
143
|
+
| `--axi-rail-pad` | the padding inside an `.axi-rail` | `10px` | `<aside class="axi-rail" style="--axi-rail-pad: 6px">` |
|
|
141
144
|
| `--axi-avatar-size` | the size of an `.axi-avatar` square | `40px` | `<span class="axi-avatar" style="--axi-avatar-size: 28px">MS</span>` |
|
|
142
145
|
| `--axi-modal-width` | the maximum width of an `.axi-modal`, before the viewport clamp | `560px` | `<dialog class="axi-modal" style="--axi-modal-width: 760px">` |
|
|
143
146
|
| `--axi-card-strip` | the colour of `.axi-card--strip`'s top strip | `var(--axi-accent)` | `<a class="axi-card axi-card--strip" style="--axi-card-strip: var(--axi-ok)">` |
|
|
@@ -146,6 +149,8 @@ not "what does the system look like". Everything else is
|
|
|
146
149
|
| `--axi-stack-gap` | gap between `.axi-stack` children | `12px` | `<div class="axi-stack" style="--axi-stack-gap: 20px">` |
|
|
147
150
|
| `--axi-panel-pad` | `.axi-panel`'s own padding | `26px` | `<div class="axi-panel" style="--axi-panel-pad: 14px">` |
|
|
148
151
|
| `--axi-page-pad` | `.axi-page`'s horizontal gutter | `var(--axi-gutter)` | `<div class="axi-page axi-page--narrow" style="--axi-page-pad: 0">` |
|
|
152
|
+
| `--axi-palette-w` | the widest an `.axi-palette__panel` gets (it fills the room below that) | `560px` | `<div class="axi-palette__panel" style="--axi-palette-w: 720px">` |
|
|
153
|
+
| `--axi-palette-top` | how far down the screen an `.axi-palette` opens | `12vh` | `<div class="axi-scrim axi-palette" style="--axi-palette-top: 6vh">` |
|
|
149
154
|
| `--axi-menu-width` | width of `.axi-menu__pop` | `310px` | `<div class="axi-menu__pop" style="--axi-menu-width: 380px">` |
|
|
150
155
|
| `--axi-drawer-width` | width of `.axi-drawer` (capped at `100vw`) | `560px` | `<aside class="axi-drawer" style="--axi-drawer-width: 720px">` |
|
|
151
156
|
| `--axi-series` | the ink a meter fill, bar, plot line or `.axi-diamond--series` is drawn in | `var(--axi-accent)` | `<span class="axi-meter__fill" style="--axi-series: var(--axi-ok)">` |
|
package/dist/axi.css
CHANGED
|
@@ -32,6 +32,20 @@
|
|
|
32
32
|
--axi-ground-image: none;
|
|
33
33
|
--axi-surface: #222731; /* a panel raised off the page */
|
|
34
34
|
--axi-surface-raised: #2b313d; /* a chip or popover raised off a panel */
|
|
35
|
+
/* The fill for a surface that FLOATS over content which scrolls behind it: a
|
|
36
|
+
command palette, a menu popover, a modal. Holds --axi-surface, because for
|
|
37
|
+
a theme whose panels are opaque a floating panel is just a panel and there
|
|
38
|
+
is nothing to decide.
|
|
39
|
+
|
|
40
|
+
It exists for the theme whose panels are not. A translucent pane is legible
|
|
41
|
+
standing still on the page and stops being legible the moment a table
|
|
42
|
+
scrolls under it - the text it is carrying now has text behind it. The blur
|
|
43
|
+
that was supposed to rescue that is not something a language can promise:
|
|
44
|
+
backdrop-filter is unavailable or switched off on whole platforms, and
|
|
45
|
+
where it is missing the alpha is the only thing left. So the float gets its
|
|
46
|
+
own token, and a theme that goes translucent restates it near-opaque
|
|
47
|
+
without giving up the tint on everything that merely sits still. */
|
|
48
|
+
--axi-surface-float: var(--axi-surface);
|
|
35
49
|
--axi-ink-line: #0c0e12; /* the outline every raised element is drawn with */
|
|
36
50
|
/* The tone a shape is made of when it is made of the line ink rather than
|
|
37
51
|
outlined in it - the tooltip, the titlebar strip, the switch's slug. Holds
|
|
@@ -43,6 +57,28 @@
|
|
|
43
57
|
--axi-ink-on-fill below, on the other side of the same token. */
|
|
44
58
|
--axi-ground-deep: var(--axi-ink-line);
|
|
45
59
|
--axi-rule: #3a4250; /* internal rules, inside an outlined panel */
|
|
60
|
+
/* --- the well --- */
|
|
61
|
+
/* A field sunk INTO a surface rather than raised off one: a table picker's
|
|
62
|
+
list, a bar's trough, a column of cards standing on the page. The language
|
|
63
|
+
had no name for it, and every consumer that needed one reached for a second
|
|
64
|
+
.axi-panel inside the first - two identical fills parted by a line, which
|
|
65
|
+
reads as one field with a stray rule through it.
|
|
66
|
+
|
|
67
|
+
Two tokens because a well is the exact inverse of a panel on both counts.
|
|
68
|
+
The fill is a SUBTRACTION from whatever is behind it, not a colour of its
|
|
69
|
+
own: spelled as --axi-ground it is right in this theme, where surfaces are
|
|
70
|
+
opaque and the page really is what shows through a hole in one, and wrong
|
|
71
|
+
in a glass theme, where an opaque page colour inside a translucent pane is
|
|
72
|
+
a black patch rather than a recess. A theme restates it as a darkening at
|
|
73
|
+
alpha and the panel's own tint carries through.
|
|
74
|
+
|
|
75
|
+
The line is --axi-rule and not --axi-ink-line, because ink only reads
|
|
76
|
+
against a fill lighter than itself. Every outline in this language is
|
|
77
|
+
near-black on the assumption of a ground the lines can be darker than; a
|
|
78
|
+
well IS at ground level, so an ink edge round one is a black line on a
|
|
79
|
+
black field. Raised takes ink, sunk takes the rule. */
|
|
80
|
+
--axi-well-fill: var(--axi-ground);
|
|
81
|
+
--axi-well-line: var(--axi-rule);
|
|
46
82
|
--axi-text: #f4f6f9;
|
|
47
83
|
--axi-text-dim: #a7b0be;
|
|
48
84
|
--axi-text-faint: #7c8695;
|
|
@@ -251,6 +287,22 @@ a { color: inherit; }
|
|
|
251
287
|
padding: var(--axi-panel-pad, 26px);
|
|
252
288
|
}
|
|
253
289
|
|
|
290
|
+
/* ---------- well ---------- */
|
|
291
|
+
/* The panel's inverse: a field sunk into the surface around it rather than
|
|
292
|
+
raised off it. No block, because a recess casts nothing, and the rule rather
|
|
293
|
+
than the ink for its edge - see --axi-well-fill in src/tokens.css for why
|
|
294
|
+
both of those follow from the same fact. Reach for this wherever a second
|
|
295
|
+
panel inside a panel was the instinct: a picker list beside a table, a column
|
|
296
|
+
of cards standing on the page, a trough a bar fills. */
|
|
297
|
+
.axi-well {
|
|
298
|
+
background: var(--axi-well-fill);
|
|
299
|
+
border: var(--axi-border-control) solid var(--axi-well-line);
|
|
300
|
+
border-radius: var(--axi-radius);
|
|
301
|
+
/* Tighter than a panel's 26px: a well is a container for objects that carry
|
|
302
|
+
their own padding, not a page margin. */
|
|
303
|
+
padding: var(--axi-well-pad, 10px);
|
|
304
|
+
}
|
|
305
|
+
|
|
254
306
|
/* ---------- button ---------- */
|
|
255
307
|
.axi-btn {
|
|
256
308
|
display: inline-flex; align-items: center; gap: 8px;
|
|
@@ -463,6 +515,27 @@ a { color: inherit; }
|
|
|
463
515
|
color: var(--axi-accent); font-weight: 900; pointer-events: none;
|
|
464
516
|
}
|
|
465
517
|
|
|
518
|
+
/* ---------- keyboard key ---------- */
|
|
519
|
+
/* A key on the keyboard, named in the interface: the Esc that closes a
|
|
520
|
+
palette, the Ctrl K that opens it. Drawn as a small control rather than as a
|
|
521
|
+
label, because what it depicts is a thing you press - so it takes the
|
|
522
|
+
control edge and the control radius, and every consumer that has a shortcut
|
|
523
|
+
to show stops having to invent a box for it.
|
|
524
|
+
|
|
525
|
+
It sits on --axi-surface, not --axi-ground: a key is raised out of whatever
|
|
526
|
+
it is printed on, and the fields and bars these appear in are already the
|
|
527
|
+
recessed thing. */
|
|
528
|
+
.axi-kbd {
|
|
529
|
+
display: inline-block;
|
|
530
|
+
padding: 2px 5px;
|
|
531
|
+
background: var(--axi-surface);
|
|
532
|
+
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
533
|
+
border-radius: var(--axi-radius-sm);
|
|
534
|
+
color: var(--axi-text-dim);
|
|
535
|
+
font: 700 10px/1 var(--axi-sans);
|
|
536
|
+
white-space: nowrap;
|
|
537
|
+
}
|
|
538
|
+
|
|
466
539
|
/* ---------- select ---------- */
|
|
467
540
|
/* The closed box is ours everywhere: strip the native control and draw the
|
|
468
541
|
caret, so a select sits alongside the other controls as just another
|
|
@@ -935,6 +1008,107 @@ textarea.axi-input {
|
|
|
935
1008
|
}
|
|
936
1009
|
.axi-tabs .axi-tabs__close:hover { color: var(--axi-text); border-color: transparent; }
|
|
937
1010
|
|
|
1011
|
+
/* ---------- rail ---------- */
|
|
1012
|
+
/* Vertical navigation, for a set too large for a tab strip to hold. Same
|
|
1013
|
+
grammar as .axi-tabs deliberately - the item you are on is filled with the
|
|
1014
|
+
accent and blocked, the rest are transparent until hovered - because "where
|
|
1015
|
+
am I" is one question and should have one answer however the app happens to
|
|
1016
|
+
be laying it out.
|
|
1017
|
+
|
|
1018
|
+
What a rail adds over a strip is a second level. A category is a place and
|
|
1019
|
+
takes the fill; a section under it is where the page is scrolled to, which is
|
|
1020
|
+
a smaller claim and is drawn as one: brightened text against the dim list
|
|
1021
|
+
around it, no fill, no edge. Two fills at two levels would leave the reader
|
|
1022
|
+
deciding which of them is the answer.
|
|
1023
|
+
|
|
1024
|
+
--axi-rail-w is declared nowhere and read with a fallback, i.e. it is the hook
|
|
1025
|
+
a consumer overrides to widen the rail for longer labels. */
|
|
1026
|
+
.axi-rail {
|
|
1027
|
+
display: flex;
|
|
1028
|
+
flex-direction: column;
|
|
1029
|
+
width: var(--axi-rail-w, 208px);
|
|
1030
|
+
flex: none;
|
|
1031
|
+
overflow-y: auto;
|
|
1032
|
+
padding: var(--axi-rail-pad, 10px);
|
|
1033
|
+
background: var(--axi-surface);
|
|
1034
|
+
backdrop-filter: var(--axi-surface-filter);
|
|
1035
|
+
border: var(--axi-border-panel) solid var(--axi-ink-line);
|
|
1036
|
+
border-radius: var(--axi-radius);
|
|
1037
|
+
}
|
|
1038
|
+
/* A rail that IS the edge of the page rather than a panel standing in the
|
|
1039
|
+
layout - pinned to the viewport, or a drawer's contents. It keeps the one
|
|
1040
|
+
border that faces the content and drops everything that says "this is an
|
|
1041
|
+
object with space around it": no radius on the flush side, and no block,
|
|
1042
|
+
because a block needs somewhere to fall.
|
|
1043
|
+
|
|
1044
|
+
The block is stated as what a free-standing rail has rather than as
|
|
1045
|
+
something the flush one turns off, so there is exactly one box-shadow here
|
|
1046
|
+
and it names the panel block. `box-shadow: none` would be a second spelling
|
|
1047
|
+
of the raise for a theme to miss. */
|
|
1048
|
+
.axi-rail:not(.axi-rail--flush) { box-shadow: var(--axi-shadow-panel); }
|
|
1049
|
+
.axi-rail--flush {
|
|
1050
|
+
border: 0;
|
|
1051
|
+
border-right: var(--axi-border-panel) solid var(--axi-ink-line);
|
|
1052
|
+
border-radius: 0;
|
|
1053
|
+
}
|
|
1054
|
+
.axi-rail__nav { display: flex; flex-direction: column; gap: 2px; }
|
|
1055
|
+
.axi-rail__item {
|
|
1056
|
+
display: flex;
|
|
1057
|
+
align-items: center;
|
|
1058
|
+
gap: 10px;
|
|
1059
|
+
width: 100%;
|
|
1060
|
+
padding: 8px 10px;
|
|
1061
|
+
font: 800 12.5px/1 var(--axi-sans);
|
|
1062
|
+
color: var(--axi-text-dim);
|
|
1063
|
+
text-align: left;
|
|
1064
|
+
background: transparent;
|
|
1065
|
+
border: var(--axi-border-control) solid transparent;
|
|
1066
|
+
border-radius: var(--axi-radius-sm);
|
|
1067
|
+
cursor: pointer;
|
|
1068
|
+
}
|
|
1069
|
+
.axi-rail__item:hover { color: var(--axi-text); background: var(--axi-surface-raised); }
|
|
1070
|
+
/* The same treatment a pressed pill and a current tab get. Only ever one of
|
|
1071
|
+
these on screen. Any aria-current value, because the right one depends on
|
|
1072
|
+
what the rail navigates - `page` for a view, `location` for a place within
|
|
1073
|
+
one - and the fill means the same thing either way. */
|
|
1074
|
+
.axi-rail__item[aria-current],
|
|
1075
|
+
.axi-rail__item[aria-current]:hover {
|
|
1076
|
+
background: var(--axi-accent);
|
|
1077
|
+
color: var(--axi-accent-ink);
|
|
1078
|
+
border-color: var(--axi-ink-line);
|
|
1079
|
+
box-shadow: var(--axi-shadow-control);
|
|
1080
|
+
}
|
|
1081
|
+
/* An icon beside a label is a label for it, not a second accent - so it takes
|
|
1082
|
+
the faint tone, and inside a filled item it follows the item's ink. */
|
|
1083
|
+
.axi-rail__item .axi-icon { color: var(--axi-text-faint); }
|
|
1084
|
+
.axi-rail__item[aria-current] .axi-icon { color: var(--axi-accent-ink); }
|
|
1085
|
+
/* The second level, indented under the item it belongs to. */
|
|
1086
|
+
.axi-rail__sub { display: flex; flex-direction: column; margin: 3px 0 5px 10px; }
|
|
1087
|
+
.axi-rail__subitem {
|
|
1088
|
+
padding: 5px 8px;
|
|
1089
|
+
font: 700 11.5px/1.3 var(--axi-sans);
|
|
1090
|
+
text-align: left;
|
|
1091
|
+
color: var(--axi-text-faint);
|
|
1092
|
+
background: transparent;
|
|
1093
|
+
border: 0;
|
|
1094
|
+
border-radius: var(--axi-radius-sm);
|
|
1095
|
+
cursor: pointer;
|
|
1096
|
+
}
|
|
1097
|
+
.axi-rail__subitem:hover { color: var(--axi-text-dim); background: var(--axi-surface-raised); }
|
|
1098
|
+
.axi-rail__subitem[aria-current] { color: var(--axi-text); }
|
|
1099
|
+
/* An annotation on an item, not a measurement of it, so rule 5 draws it
|
|
1100
|
+
outlined rather than filled. */
|
|
1101
|
+
.axi-rail__mark {
|
|
1102
|
+
margin-left: auto;
|
|
1103
|
+
padding: 2px 5px;
|
|
1104
|
+
font: var(--axi-t-micro);
|
|
1105
|
+
letter-spacing: .04em;
|
|
1106
|
+
text-transform: uppercase;
|
|
1107
|
+
color: var(--axi-meta);
|
|
1108
|
+
border: var(--axi-border-hairline) solid var(--axi-meta);
|
|
1109
|
+
border-radius: var(--axi-radius-sm);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
938
1112
|
/* ---------- notice ---------- */
|
|
939
1113
|
.axi-notice {
|
|
940
1114
|
display: flex; gap: 14px; padding: 15px 18px;
|
|
@@ -992,6 +1166,144 @@ textarea.axi-input {
|
|
|
992
1166
|
.axi-menu__pop label:hover { background: var(--axi-ground); color: var(--axi-text); }
|
|
993
1167
|
.axi-menu__pop input { margin: 3px 0 0; }
|
|
994
1168
|
|
|
1169
|
+
/* ---------- command palette ---------- */
|
|
1170
|
+
/* Everything in an app, reachable by typing its name. Not .axi-modal, which is
|
|
1171
|
+
a <dialog> with a head, a body and a foot and asks you a question: a palette
|
|
1172
|
+
has no question and no buttons, it is a field with a list under it, and it
|
|
1173
|
+
sits near the top of the screen because the list has to grow downward
|
|
1174
|
+
without moving the field you are typing into.
|
|
1175
|
+
|
|
1176
|
+
Two things travel with it. The panel is .axi-surface-float, not
|
|
1177
|
+
.axi-surface: whatever the app was showing is still behind it and still
|
|
1178
|
+
scrolling, and that is the case the float token exists for. And the host
|
|
1179
|
+
element wears .axi-scrim alongside .axi-palette - the scrim already knows
|
|
1180
|
+
what darkening the page looks like, and a second spelling of it here is one
|
|
1181
|
+
more place for a theme to be restated in only one of them.
|
|
1182
|
+
|
|
1183
|
+
The cursor is not the hover. A palette is driven from the keyboard, so the
|
|
1184
|
+
row Enter would take is marked with the accent fill outright, while hover is
|
|
1185
|
+
the quieter raised-surface wash - both visible at once, saying different
|
|
1186
|
+
things, with the filled one winning on sight. The consumer sets
|
|
1187
|
+
`data-active` on the cursor row and keeps it scrolled into view. */
|
|
1188
|
+
.axi-palette {
|
|
1189
|
+
display: flex; align-items: flex-start; justify-content: center;
|
|
1190
|
+
padding: var(--axi-palette-top, 12vh) 16px 16px;
|
|
1191
|
+
}
|
|
1192
|
+
.axi-palette__panel {
|
|
1193
|
+
display: flex; flex-direction: column;
|
|
1194
|
+
width: 100%; max-width: var(--axi-palette-w, 560px);
|
|
1195
|
+
max-height: 70vh; overflow: hidden;
|
|
1196
|
+
background: var(--axi-surface-float);
|
|
1197
|
+
backdrop-filter: var(--axi-surface-filter);
|
|
1198
|
+
border: var(--axi-border-panel) solid var(--axi-ink-line);
|
|
1199
|
+
border-radius: var(--axi-radius);
|
|
1200
|
+
box-shadow: var(--axi-shadow-panel);
|
|
1201
|
+
}
|
|
1202
|
+
/* The bar holds the field and the filters. It is the raised surface so the
|
|
1203
|
+
list below it reads as the panel's contents rather than as more bar. */
|
|
1204
|
+
.axi-palette__bar {
|
|
1205
|
+
flex: none; padding: 8px 10px;
|
|
1206
|
+
background: var(--axi-surface-raised);
|
|
1207
|
+
border-bottom: var(--axi-border-control) solid var(--axi-ink-line);
|
|
1208
|
+
}
|
|
1209
|
+
/* The field is an .axi-search wrapping an .axi-input, same as anywhere else;
|
|
1210
|
+
only the height comes down, because a palette's field is a row in a bar and
|
|
1211
|
+
not a form control standing on a page. No focus ring: the palette opens with
|
|
1212
|
+
the caret already here, so a permanent outline says nothing, and it would
|
|
1213
|
+
argue with the one mark that does - the accent on the cursor row. */
|
|
1214
|
+
.axi-palette__bar .axi-input { padding-top: 7px; padding-bottom: 7px; font-size: 13px; }
|
|
1215
|
+
.axi-palette__bar .axi-input:focus-visible { outline: none; }
|
|
1216
|
+
.axi-palette__filters { display: flex; align-items: center; gap: 6px; margin-top: 8px; }
|
|
1217
|
+
/* What the list is not showing. Meta, at the end of the filter row, because it
|
|
1218
|
+
is a reading about the results and not another control among them. */
|
|
1219
|
+
.axi-palette__count {
|
|
1220
|
+
margin-left: auto; flex: none;
|
|
1221
|
+
color: var(--axi-text-faint);
|
|
1222
|
+
font: var(--axi-t-label); font-size: 10px; letter-spacing: var(--axi-ls-label);
|
|
1223
|
+
text-transform: uppercase;
|
|
1224
|
+
}
|
|
1225
|
+
/* min-height: 0 because the panel is a flex column and a list that cannot
|
|
1226
|
+
shrink below its content pushes the panel past its own max-height. The
|
|
1227
|
+
scrollbar is hidden: at twelve rows behind a fixed cap the bar is a channel
|
|
1228
|
+
carved down the side of a small panel to report a fact the row count in the
|
|
1229
|
+
bar has already given. */
|
|
1230
|
+
.axi-palette__list {
|
|
1231
|
+
flex: 1; min-height: 0; overflow-y: auto; padding: 4px 0;
|
|
1232
|
+
scrollbar-width: none;
|
|
1233
|
+
}
|
|
1234
|
+
.axi-palette__list::-webkit-scrollbar { width: 0; height: 0; }
|
|
1235
|
+
/* One .axi-palette__section per group of rows, each opening with a
|
|
1236
|
+
.axi-palette__group heading. The section is a named part rather than left to
|
|
1237
|
+
the consumer's wrapper so the rule between groups can be stated as what it
|
|
1238
|
+
is - a parting between two sections - instead of guessed at from structure. */
|
|
1239
|
+
.axi-palette__section + .axi-palette__section {
|
|
1240
|
+
border-top: var(--axi-border-hairline) solid var(--axi-rule);
|
|
1241
|
+
margin-top: 4px;
|
|
1242
|
+
}
|
|
1243
|
+
.axi-palette__group {
|
|
1244
|
+
padding: 8px 10px 4px;
|
|
1245
|
+
color: var(--axi-text-faint);
|
|
1246
|
+
font: var(--axi-t-label); font-size: 10px; letter-spacing: var(--axi-ls-label);
|
|
1247
|
+
text-transform: uppercase;
|
|
1248
|
+
}
|
|
1249
|
+
.axi-palette__row {
|
|
1250
|
+
display: flex; align-items: center; gap: 8px;
|
|
1251
|
+
width: 100%; padding: 7px 10px;
|
|
1252
|
+
background: transparent; border: 0; border-radius: 0;
|
|
1253
|
+
color: var(--axi-text-dim);
|
|
1254
|
+
font: var(--axi-t-small); text-align: left; cursor: pointer;
|
|
1255
|
+
}
|
|
1256
|
+
.axi-palette__row:hover { background: var(--axi-surface-raised); color: var(--axi-text); }
|
|
1257
|
+
.axi-palette__row[data-active] { background: var(--axi-accent); color: var(--axi-accent-ink); }
|
|
1258
|
+
/* The row's own ink is set above, but a row carries a glyph and a second line
|
|
1259
|
+
of text that were coloured on their own; on the filled row they all become
|
|
1260
|
+
the ink the fill is written in. */
|
|
1261
|
+
.axi-palette__row[data-active] svg,
|
|
1262
|
+
.axi-palette__row[data-active] .axi-palette__meta { color: var(--axi-accent-ink); }
|
|
1263
|
+
/* Where the row is, as against what it is. */
|
|
1264
|
+
.axi-palette__meta { margin-left: auto; flex: none; color: var(--axi-text-faint); font-size: 11px; }
|
|
1265
|
+
.axi-palette__empty {
|
|
1266
|
+
padding: 24px 12px; text-align: center;
|
|
1267
|
+
color: var(--axi-text-faint); font: var(--axi-t-small);
|
|
1268
|
+
}
|
|
1269
|
+
/* The palette's field at rest, wherever the app keeps the way in. It is a
|
|
1270
|
+
button, and it is drawn as the field it opens - the same ground, the same
|
|
1271
|
+
edge, the same magnifier at the head - so opening the palette reads as this
|
|
1272
|
+
control growing rather than as a different object arriving.
|
|
1273
|
+
|
|
1274
|
+
It keeps the field's fill instead of a button's because what it stands for
|
|
1275
|
+
is somewhere to type. What it takes from a button is the answer to the
|
|
1276
|
+
cursor: the block and the lift, because it is still a thing you press. */
|
|
1277
|
+
.axi-palette__trigger {
|
|
1278
|
+
display: flex; align-items: center; width: 100%; overflow: hidden;
|
|
1279
|
+
padding: 0 10px 0 0;
|
|
1280
|
+
background: var(--axi-ground);
|
|
1281
|
+
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
1282
|
+
border-radius: var(--axi-radius-sm);
|
|
1283
|
+
color: var(--axi-text-dim);
|
|
1284
|
+
font: var(--axi-t-label); font-size: 12px; font-weight: 600;
|
|
1285
|
+
cursor: pointer;
|
|
1286
|
+
transition: transform .1s, box-shadow .1s, color .1s;
|
|
1287
|
+
}
|
|
1288
|
+
.axi-palette__trigger:hover {
|
|
1289
|
+
color: var(--axi-text);
|
|
1290
|
+
box-shadow: var(--axi-shadow-control);
|
|
1291
|
+
transform: translate(-2px, -2px);
|
|
1292
|
+
}
|
|
1293
|
+
/* The magnifier at the head of the well, carrying the accent on its own. Its
|
|
1294
|
+
own element and stretched to the trigger's height so the glyph is inside the
|
|
1295
|
+
outline rather than floating in the fill. It was a solid accent block once,
|
|
1296
|
+
which at a 30px row spends a lot of the loudest colour in the palette on a
|
|
1297
|
+
control nobody is meant to look at until they want it - the glyph says the
|
|
1298
|
+
same thing at a fraction of the volume. */
|
|
1299
|
+
.axi-palette__mark {
|
|
1300
|
+
display: flex; align-items: center; align-self: stretch; flex: none;
|
|
1301
|
+
padding: 0 8px 0 10px; margin-right: 10px;
|
|
1302
|
+
color: var(--axi-accent);
|
|
1303
|
+
}
|
|
1304
|
+
.axi-palette__label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1305
|
+
.axi-palette__trigger .axi-kbd { margin-left: auto; flex: none; }
|
|
1306
|
+
|
|
995
1307
|
/* ---------- tooltip ---------- */
|
|
996
1308
|
/* One line of ink on the darkest surface in the language, so it reads over
|
|
997
1309
|
anything it lands on. The class draws the box only; coordinates come from
|
package/dist/themes/glass.css
CHANGED
|
@@ -6,10 +6,12 @@
|
|
|
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
8
|
--axi-surface-raised: linear-gradient(145deg, rgba(86, 99, 130, .5), rgba(44, 52, 71, .38));
|
|
9
|
+
--axi-surface-float: linear-gradient(145deg, rgba(38, 45, 62, .97), rgba(22, 26, 36, .97));
|
|
9
10
|
--axi-surface-filter: blur(18px) saturate(140%);
|
|
10
11
|
--axi-ink-line: rgba(180, 200, 255, .16);
|
|
11
12
|
--axi-rule: rgba(180, 200, 255, .14);
|
|
12
13
|
--axi-ground-deep: #0c0e12;
|
|
14
|
+
--axi-well-fill: rgba(6, 8, 14, .32);
|
|
13
15
|
--axi-accent-ink: #0c0e12;
|
|
14
16
|
--axi-ink-on-fill: #0c0e12;
|
|
15
17
|
--axi-text: #f2f5fb;
|
package/package.json
CHANGED
package/src/primitives.css
CHANGED
|
@@ -29,6 +29,22 @@
|
|
|
29
29
|
padding: var(--axi-panel-pad, 26px);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/* ---------- well ---------- */
|
|
33
|
+
/* The panel's inverse: a field sunk into the surface around it rather than
|
|
34
|
+
raised off it. No block, because a recess casts nothing, and the rule rather
|
|
35
|
+
than the ink for its edge - see --axi-well-fill in src/tokens.css for why
|
|
36
|
+
both of those follow from the same fact. Reach for this wherever a second
|
|
37
|
+
panel inside a panel was the instinct: a picker list beside a table, a column
|
|
38
|
+
of cards standing on the page, a trough a bar fills. */
|
|
39
|
+
.axi-well {
|
|
40
|
+
background: var(--axi-well-fill);
|
|
41
|
+
border: var(--axi-border-control) solid var(--axi-well-line);
|
|
42
|
+
border-radius: var(--axi-radius);
|
|
43
|
+
/* Tighter than a panel's 26px: a well is a container for objects that carry
|
|
44
|
+
their own padding, not a page margin. */
|
|
45
|
+
padding: var(--axi-well-pad, 10px);
|
|
46
|
+
}
|
|
47
|
+
|
|
32
48
|
/* ---------- button ---------- */
|
|
33
49
|
.axi-btn {
|
|
34
50
|
display: inline-flex; align-items: center; gap: 8px;
|
|
@@ -241,6 +257,27 @@
|
|
|
241
257
|
color: var(--axi-accent); font-weight: 900; pointer-events: none;
|
|
242
258
|
}
|
|
243
259
|
|
|
260
|
+
/* ---------- keyboard key ---------- */
|
|
261
|
+
/* A key on the keyboard, named in the interface: the Esc that closes a
|
|
262
|
+
palette, the Ctrl K that opens it. Drawn as a small control rather than as a
|
|
263
|
+
label, because what it depicts is a thing you press - so it takes the
|
|
264
|
+
control edge and the control radius, and every consumer that has a shortcut
|
|
265
|
+
to show stops having to invent a box for it.
|
|
266
|
+
|
|
267
|
+
It sits on --axi-surface, not --axi-ground: a key is raised out of whatever
|
|
268
|
+
it is printed on, and the fields and bars these appear in are already the
|
|
269
|
+
recessed thing. */
|
|
270
|
+
.axi-kbd {
|
|
271
|
+
display: inline-block;
|
|
272
|
+
padding: 2px 5px;
|
|
273
|
+
background: var(--axi-surface);
|
|
274
|
+
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
275
|
+
border-radius: var(--axi-radius-sm);
|
|
276
|
+
color: var(--axi-text-dim);
|
|
277
|
+
font: 700 10px/1 var(--axi-sans);
|
|
278
|
+
white-space: nowrap;
|
|
279
|
+
}
|
|
280
|
+
|
|
244
281
|
/* ---------- select ---------- */
|
|
245
282
|
/* The closed box is ours everywhere: strip the native control and draw the
|
|
246
283
|
caret, so a select sits alongside the other controls as just another
|
package/src/shells.css
CHANGED
|
@@ -109,6 +109,107 @@
|
|
|
109
109
|
}
|
|
110
110
|
.axi-tabs .axi-tabs__close:hover { color: var(--axi-text); border-color: transparent; }
|
|
111
111
|
|
|
112
|
+
/* ---------- rail ---------- */
|
|
113
|
+
/* Vertical navigation, for a set too large for a tab strip to hold. Same
|
|
114
|
+
grammar as .axi-tabs deliberately - the item you are on is filled with the
|
|
115
|
+
accent and blocked, the rest are transparent until hovered - because "where
|
|
116
|
+
am I" is one question and should have one answer however the app happens to
|
|
117
|
+
be laying it out.
|
|
118
|
+
|
|
119
|
+
What a rail adds over a strip is a second level. A category is a place and
|
|
120
|
+
takes the fill; a section under it is where the page is scrolled to, which is
|
|
121
|
+
a smaller claim and is drawn as one: brightened text against the dim list
|
|
122
|
+
around it, no fill, no edge. Two fills at two levels would leave the reader
|
|
123
|
+
deciding which of them is the answer.
|
|
124
|
+
|
|
125
|
+
--axi-rail-w is declared nowhere and read with a fallback, i.e. it is the hook
|
|
126
|
+
a consumer overrides to widen the rail for longer labels. */
|
|
127
|
+
.axi-rail {
|
|
128
|
+
display: flex;
|
|
129
|
+
flex-direction: column;
|
|
130
|
+
width: var(--axi-rail-w, 208px);
|
|
131
|
+
flex: none;
|
|
132
|
+
overflow-y: auto;
|
|
133
|
+
padding: var(--axi-rail-pad, 10px);
|
|
134
|
+
background: var(--axi-surface);
|
|
135
|
+
backdrop-filter: var(--axi-surface-filter);
|
|
136
|
+
border: var(--axi-border-panel) solid var(--axi-ink-line);
|
|
137
|
+
border-radius: var(--axi-radius);
|
|
138
|
+
}
|
|
139
|
+
/* A rail that IS the edge of the page rather than a panel standing in the
|
|
140
|
+
layout - pinned to the viewport, or a drawer's contents. It keeps the one
|
|
141
|
+
border that faces the content and drops everything that says "this is an
|
|
142
|
+
object with space around it": no radius on the flush side, and no block,
|
|
143
|
+
because a block needs somewhere to fall.
|
|
144
|
+
|
|
145
|
+
The block is stated as what a free-standing rail has rather than as
|
|
146
|
+
something the flush one turns off, so there is exactly one box-shadow here
|
|
147
|
+
and it names the panel block. `box-shadow: none` would be a second spelling
|
|
148
|
+
of the raise for a theme to miss. */
|
|
149
|
+
.axi-rail:not(.axi-rail--flush) { box-shadow: var(--axi-shadow-panel); }
|
|
150
|
+
.axi-rail--flush {
|
|
151
|
+
border: 0;
|
|
152
|
+
border-right: var(--axi-border-panel) solid var(--axi-ink-line);
|
|
153
|
+
border-radius: 0;
|
|
154
|
+
}
|
|
155
|
+
.axi-rail__nav { display: flex; flex-direction: column; gap: 2px; }
|
|
156
|
+
.axi-rail__item {
|
|
157
|
+
display: flex;
|
|
158
|
+
align-items: center;
|
|
159
|
+
gap: 10px;
|
|
160
|
+
width: 100%;
|
|
161
|
+
padding: 8px 10px;
|
|
162
|
+
font: 800 12.5px/1 var(--axi-sans);
|
|
163
|
+
color: var(--axi-text-dim);
|
|
164
|
+
text-align: left;
|
|
165
|
+
background: transparent;
|
|
166
|
+
border: var(--axi-border-control) solid transparent;
|
|
167
|
+
border-radius: var(--axi-radius-sm);
|
|
168
|
+
cursor: pointer;
|
|
169
|
+
}
|
|
170
|
+
.axi-rail__item:hover { color: var(--axi-text); background: var(--axi-surface-raised); }
|
|
171
|
+
/* The same treatment a pressed pill and a current tab get. Only ever one of
|
|
172
|
+
these on screen. Any aria-current value, because the right one depends on
|
|
173
|
+
what the rail navigates - `page` for a view, `location` for a place within
|
|
174
|
+
one - and the fill means the same thing either way. */
|
|
175
|
+
.axi-rail__item[aria-current],
|
|
176
|
+
.axi-rail__item[aria-current]:hover {
|
|
177
|
+
background: var(--axi-accent);
|
|
178
|
+
color: var(--axi-accent-ink);
|
|
179
|
+
border-color: var(--axi-ink-line);
|
|
180
|
+
box-shadow: var(--axi-shadow-control);
|
|
181
|
+
}
|
|
182
|
+
/* An icon beside a label is a label for it, not a second accent - so it takes
|
|
183
|
+
the faint tone, and inside a filled item it follows the item's ink. */
|
|
184
|
+
.axi-rail__item .axi-icon { color: var(--axi-text-faint); }
|
|
185
|
+
.axi-rail__item[aria-current] .axi-icon { color: var(--axi-accent-ink); }
|
|
186
|
+
/* The second level, indented under the item it belongs to. */
|
|
187
|
+
.axi-rail__sub { display: flex; flex-direction: column; margin: 3px 0 5px 10px; }
|
|
188
|
+
.axi-rail__subitem {
|
|
189
|
+
padding: 5px 8px;
|
|
190
|
+
font: 700 11.5px/1.3 var(--axi-sans);
|
|
191
|
+
text-align: left;
|
|
192
|
+
color: var(--axi-text-faint);
|
|
193
|
+
background: transparent;
|
|
194
|
+
border: 0;
|
|
195
|
+
border-radius: var(--axi-radius-sm);
|
|
196
|
+
cursor: pointer;
|
|
197
|
+
}
|
|
198
|
+
.axi-rail__subitem:hover { color: var(--axi-text-dim); background: var(--axi-surface-raised); }
|
|
199
|
+
.axi-rail__subitem[aria-current] { color: var(--axi-text); }
|
|
200
|
+
/* An annotation on an item, not a measurement of it, so rule 5 draws it
|
|
201
|
+
outlined rather than filled. */
|
|
202
|
+
.axi-rail__mark {
|
|
203
|
+
margin-left: auto;
|
|
204
|
+
padding: 2px 5px;
|
|
205
|
+
font: var(--axi-t-micro);
|
|
206
|
+
letter-spacing: .04em;
|
|
207
|
+
text-transform: uppercase;
|
|
208
|
+
color: var(--axi-meta);
|
|
209
|
+
border: var(--axi-border-hairline) solid var(--axi-meta);
|
|
210
|
+
border-radius: var(--axi-radius-sm);
|
|
211
|
+
}
|
|
212
|
+
|
|
112
213
|
/* ---------- notice ---------- */
|
|
113
214
|
.axi-notice {
|
|
114
215
|
display: flex; gap: 14px; padding: 15px 18px;
|
|
@@ -166,6 +267,144 @@
|
|
|
166
267
|
.axi-menu__pop label:hover { background: var(--axi-ground); color: var(--axi-text); }
|
|
167
268
|
.axi-menu__pop input { margin: 3px 0 0; }
|
|
168
269
|
|
|
270
|
+
/* ---------- command palette ---------- */
|
|
271
|
+
/* Everything in an app, reachable by typing its name. Not .axi-modal, which is
|
|
272
|
+
a <dialog> with a head, a body and a foot and asks you a question: a palette
|
|
273
|
+
has no question and no buttons, it is a field with a list under it, and it
|
|
274
|
+
sits near the top of the screen because the list has to grow downward
|
|
275
|
+
without moving the field you are typing into.
|
|
276
|
+
|
|
277
|
+
Two things travel with it. The panel is .axi-surface-float, not
|
|
278
|
+
.axi-surface: whatever the app was showing is still behind it and still
|
|
279
|
+
scrolling, and that is the case the float token exists for. And the host
|
|
280
|
+
element wears .axi-scrim alongside .axi-palette - the scrim already knows
|
|
281
|
+
what darkening the page looks like, and a second spelling of it here is one
|
|
282
|
+
more place for a theme to be restated in only one of them.
|
|
283
|
+
|
|
284
|
+
The cursor is not the hover. A palette is driven from the keyboard, so the
|
|
285
|
+
row Enter would take is marked with the accent fill outright, while hover is
|
|
286
|
+
the quieter raised-surface wash - both visible at once, saying different
|
|
287
|
+
things, with the filled one winning on sight. The consumer sets
|
|
288
|
+
`data-active` on the cursor row and keeps it scrolled into view. */
|
|
289
|
+
.axi-palette {
|
|
290
|
+
display: flex; align-items: flex-start; justify-content: center;
|
|
291
|
+
padding: var(--axi-palette-top, 12vh) 16px 16px;
|
|
292
|
+
}
|
|
293
|
+
.axi-palette__panel {
|
|
294
|
+
display: flex; flex-direction: column;
|
|
295
|
+
width: 100%; max-width: var(--axi-palette-w, 560px);
|
|
296
|
+
max-height: 70vh; overflow: hidden;
|
|
297
|
+
background: var(--axi-surface-float);
|
|
298
|
+
backdrop-filter: var(--axi-surface-filter);
|
|
299
|
+
border: var(--axi-border-panel) solid var(--axi-ink-line);
|
|
300
|
+
border-radius: var(--axi-radius);
|
|
301
|
+
box-shadow: var(--axi-shadow-panel);
|
|
302
|
+
}
|
|
303
|
+
/* The bar holds the field and the filters. It is the raised surface so the
|
|
304
|
+
list below it reads as the panel's contents rather than as more bar. */
|
|
305
|
+
.axi-palette__bar {
|
|
306
|
+
flex: none; padding: 8px 10px;
|
|
307
|
+
background: var(--axi-surface-raised);
|
|
308
|
+
border-bottom: var(--axi-border-control) solid var(--axi-ink-line);
|
|
309
|
+
}
|
|
310
|
+
/* The field is an .axi-search wrapping an .axi-input, same as anywhere else;
|
|
311
|
+
only the height comes down, because a palette's field is a row in a bar and
|
|
312
|
+
not a form control standing on a page. No focus ring: the palette opens with
|
|
313
|
+
the caret already here, so a permanent outline says nothing, and it would
|
|
314
|
+
argue with the one mark that does - the accent on the cursor row. */
|
|
315
|
+
.axi-palette__bar .axi-input { padding-top: 7px; padding-bottom: 7px; font-size: 13px; }
|
|
316
|
+
.axi-palette__bar .axi-input:focus-visible { outline: none; }
|
|
317
|
+
.axi-palette__filters { display: flex; align-items: center; gap: 6px; margin-top: 8px; }
|
|
318
|
+
/* What the list is not showing. Meta, at the end of the filter row, because it
|
|
319
|
+
is a reading about the results and not another control among them. */
|
|
320
|
+
.axi-palette__count {
|
|
321
|
+
margin-left: auto; flex: none;
|
|
322
|
+
color: var(--axi-text-faint);
|
|
323
|
+
font: var(--axi-t-label); font-size: 10px; letter-spacing: var(--axi-ls-label);
|
|
324
|
+
text-transform: uppercase;
|
|
325
|
+
}
|
|
326
|
+
/* min-height: 0 because the panel is a flex column and a list that cannot
|
|
327
|
+
shrink below its content pushes the panel past its own max-height. The
|
|
328
|
+
scrollbar is hidden: at twelve rows behind a fixed cap the bar is a channel
|
|
329
|
+
carved down the side of a small panel to report a fact the row count in the
|
|
330
|
+
bar has already given. */
|
|
331
|
+
.axi-palette__list {
|
|
332
|
+
flex: 1; min-height: 0; overflow-y: auto; padding: 4px 0;
|
|
333
|
+
scrollbar-width: none;
|
|
334
|
+
}
|
|
335
|
+
.axi-palette__list::-webkit-scrollbar { width: 0; height: 0; }
|
|
336
|
+
/* One .axi-palette__section per group of rows, each opening with a
|
|
337
|
+
.axi-palette__group heading. The section is a named part rather than left to
|
|
338
|
+
the consumer's wrapper so the rule between groups can be stated as what it
|
|
339
|
+
is - a parting between two sections - instead of guessed at from structure. */
|
|
340
|
+
.axi-palette__section + .axi-palette__section {
|
|
341
|
+
border-top: var(--axi-border-hairline) solid var(--axi-rule);
|
|
342
|
+
margin-top: 4px;
|
|
343
|
+
}
|
|
344
|
+
.axi-palette__group {
|
|
345
|
+
padding: 8px 10px 4px;
|
|
346
|
+
color: var(--axi-text-faint);
|
|
347
|
+
font: var(--axi-t-label); font-size: 10px; letter-spacing: var(--axi-ls-label);
|
|
348
|
+
text-transform: uppercase;
|
|
349
|
+
}
|
|
350
|
+
.axi-palette__row {
|
|
351
|
+
display: flex; align-items: center; gap: 8px;
|
|
352
|
+
width: 100%; padding: 7px 10px;
|
|
353
|
+
background: transparent; border: 0; border-radius: 0;
|
|
354
|
+
color: var(--axi-text-dim);
|
|
355
|
+
font: var(--axi-t-small); text-align: left; cursor: pointer;
|
|
356
|
+
}
|
|
357
|
+
.axi-palette__row:hover { background: var(--axi-surface-raised); color: var(--axi-text); }
|
|
358
|
+
.axi-palette__row[data-active] { background: var(--axi-accent); color: var(--axi-accent-ink); }
|
|
359
|
+
/* The row's own ink is set above, but a row carries a glyph and a second line
|
|
360
|
+
of text that were coloured on their own; on the filled row they all become
|
|
361
|
+
the ink the fill is written in. */
|
|
362
|
+
.axi-palette__row[data-active] svg,
|
|
363
|
+
.axi-palette__row[data-active] .axi-palette__meta { color: var(--axi-accent-ink); }
|
|
364
|
+
/* Where the row is, as against what it is. */
|
|
365
|
+
.axi-palette__meta { margin-left: auto; flex: none; color: var(--axi-text-faint); font-size: 11px; }
|
|
366
|
+
.axi-palette__empty {
|
|
367
|
+
padding: 24px 12px; text-align: center;
|
|
368
|
+
color: var(--axi-text-faint); font: var(--axi-t-small);
|
|
369
|
+
}
|
|
370
|
+
/* The palette's field at rest, wherever the app keeps the way in. It is a
|
|
371
|
+
button, and it is drawn as the field it opens - the same ground, the same
|
|
372
|
+
edge, the same magnifier at the head - so opening the palette reads as this
|
|
373
|
+
control growing rather than as a different object arriving.
|
|
374
|
+
|
|
375
|
+
It keeps the field's fill instead of a button's because what it stands for
|
|
376
|
+
is somewhere to type. What it takes from a button is the answer to the
|
|
377
|
+
cursor: the block and the lift, because it is still a thing you press. */
|
|
378
|
+
.axi-palette__trigger {
|
|
379
|
+
display: flex; align-items: center; width: 100%; overflow: hidden;
|
|
380
|
+
padding: 0 10px 0 0;
|
|
381
|
+
background: var(--axi-ground);
|
|
382
|
+
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
383
|
+
border-radius: var(--axi-radius-sm);
|
|
384
|
+
color: var(--axi-text-dim);
|
|
385
|
+
font: var(--axi-t-label); font-size: 12px; font-weight: 600;
|
|
386
|
+
cursor: pointer;
|
|
387
|
+
transition: transform .1s, box-shadow .1s, color .1s;
|
|
388
|
+
}
|
|
389
|
+
.axi-palette__trigger:hover {
|
|
390
|
+
color: var(--axi-text);
|
|
391
|
+
box-shadow: var(--axi-shadow-control);
|
|
392
|
+
transform: translate(-2px, -2px);
|
|
393
|
+
}
|
|
394
|
+
/* The magnifier at the head of the well, carrying the accent on its own. Its
|
|
395
|
+
own element and stretched to the trigger's height so the glyph is inside the
|
|
396
|
+
outline rather than floating in the fill. It was a solid accent block once,
|
|
397
|
+
which at a 30px row spends a lot of the loudest colour in the palette on a
|
|
398
|
+
control nobody is meant to look at until they want it - the glyph says the
|
|
399
|
+
same thing at a fraction of the volume. */
|
|
400
|
+
.axi-palette__mark {
|
|
401
|
+
display: flex; align-items: center; align-self: stretch; flex: none;
|
|
402
|
+
padding: 0 8px 0 10px; margin-right: 10px;
|
|
403
|
+
color: var(--axi-accent);
|
|
404
|
+
}
|
|
405
|
+
.axi-palette__label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
406
|
+
.axi-palette__trigger .axi-kbd { margin-left: auto; flex: none; }
|
|
407
|
+
|
|
169
408
|
/* ---------- tooltip ---------- */
|
|
170
409
|
/* One line of ink on the darkest surface in the language, so it reads over
|
|
171
410
|
anything it lands on. The class draws the box only; coordinates come from
|
package/src/tokens.css
CHANGED
|
@@ -28,6 +28,20 @@
|
|
|
28
28
|
--axi-ground-image: none;
|
|
29
29
|
--axi-surface: #222731; /* a panel raised off the page */
|
|
30
30
|
--axi-surface-raised: #2b313d; /* a chip or popover raised off a panel */
|
|
31
|
+
/* The fill for a surface that FLOATS over content which scrolls behind it: a
|
|
32
|
+
command palette, a menu popover, a modal. Holds --axi-surface, because for
|
|
33
|
+
a theme whose panels are opaque a floating panel is just a panel and there
|
|
34
|
+
is nothing to decide.
|
|
35
|
+
|
|
36
|
+
It exists for the theme whose panels are not. A translucent pane is legible
|
|
37
|
+
standing still on the page and stops being legible the moment a table
|
|
38
|
+
scrolls under it - the text it is carrying now has text behind it. The blur
|
|
39
|
+
that was supposed to rescue that is not something a language can promise:
|
|
40
|
+
backdrop-filter is unavailable or switched off on whole platforms, and
|
|
41
|
+
where it is missing the alpha is the only thing left. So the float gets its
|
|
42
|
+
own token, and a theme that goes translucent restates it near-opaque
|
|
43
|
+
without giving up the tint on everything that merely sits still. */
|
|
44
|
+
--axi-surface-float: var(--axi-surface);
|
|
31
45
|
--axi-ink-line: #0c0e12; /* the outline every raised element is drawn with */
|
|
32
46
|
/* The tone a shape is made of when it is made of the line ink rather than
|
|
33
47
|
outlined in it - the tooltip, the titlebar strip, the switch's slug. Holds
|
|
@@ -39,6 +53,28 @@
|
|
|
39
53
|
--axi-ink-on-fill below, on the other side of the same token. */
|
|
40
54
|
--axi-ground-deep: var(--axi-ink-line);
|
|
41
55
|
--axi-rule: #3a4250; /* internal rules, inside an outlined panel */
|
|
56
|
+
/* --- the well --- */
|
|
57
|
+
/* A field sunk INTO a surface rather than raised off one: a table picker's
|
|
58
|
+
list, a bar's trough, a column of cards standing on the page. The language
|
|
59
|
+
had no name for it, and every consumer that needed one reached for a second
|
|
60
|
+
.axi-panel inside the first - two identical fills parted by a line, which
|
|
61
|
+
reads as one field with a stray rule through it.
|
|
62
|
+
|
|
63
|
+
Two tokens because a well is the exact inverse of a panel on both counts.
|
|
64
|
+
The fill is a SUBTRACTION from whatever is behind it, not a colour of its
|
|
65
|
+
own: spelled as --axi-ground it is right in this theme, where surfaces are
|
|
66
|
+
opaque and the page really is what shows through a hole in one, and wrong
|
|
67
|
+
in a glass theme, where an opaque page colour inside a translucent pane is
|
|
68
|
+
a black patch rather than a recess. A theme restates it as a darkening at
|
|
69
|
+
alpha and the panel's own tint carries through.
|
|
70
|
+
|
|
71
|
+
The line is --axi-rule and not --axi-ink-line, because ink only reads
|
|
72
|
+
against a fill lighter than itself. Every outline in this language is
|
|
73
|
+
near-black on the assumption of a ground the lines can be darker than; a
|
|
74
|
+
well IS at ground level, so an ink edge round one is a black line on a
|
|
75
|
+
black field. Raised takes ink, sunk takes the rule. */
|
|
76
|
+
--axi-well-fill: var(--axi-ground);
|
|
77
|
+
--axi-well-line: var(--axi-rule);
|
|
42
78
|
--axi-text: #f4f6f9;
|
|
43
79
|
--axi-text-dim: #a7b0be;
|
|
44
80
|
--axi-text-faint: #7c8695;
|