@axiapps/axi-design 1.15.0 → 1.17.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 +3 -0
- package/dist/axi.css +188 -5
- package/dist/themes/glass.css +1 -0
- package/package.json +1 -1
- package/src/primitives.css +16 -0
- package/src/shells.css +150 -5
- package/src/tokens.css +22 -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)">` |
|
package/dist/axi.css
CHANGED
|
@@ -43,6 +43,28 @@
|
|
|
43
43
|
--axi-ink-on-fill below, on the other side of the same token. */
|
|
44
44
|
--axi-ground-deep: var(--axi-ink-line);
|
|
45
45
|
--axi-rule: #3a4250; /* internal rules, inside an outlined panel */
|
|
46
|
+
/* --- the well --- */
|
|
47
|
+
/* A field sunk INTO a surface rather than raised off one: a table picker's
|
|
48
|
+
list, a bar's trough, a column of cards standing on the page. The language
|
|
49
|
+
had no name for it, and every consumer that needed one reached for a second
|
|
50
|
+
.axi-panel inside the first - two identical fills parted by a line, which
|
|
51
|
+
reads as one field with a stray rule through it.
|
|
52
|
+
|
|
53
|
+
Two tokens because a well is the exact inverse of a panel on both counts.
|
|
54
|
+
The fill is a SUBTRACTION from whatever is behind it, not a colour of its
|
|
55
|
+
own: spelled as --axi-ground it is right in this theme, where surfaces are
|
|
56
|
+
opaque and the page really is what shows through a hole in one, and wrong
|
|
57
|
+
in a glass theme, where an opaque page colour inside a translucent pane is
|
|
58
|
+
a black patch rather than a recess. A theme restates it as a darkening at
|
|
59
|
+
alpha and the panel's own tint carries through.
|
|
60
|
+
|
|
61
|
+
The line is --axi-rule and not --axi-ink-line, because ink only reads
|
|
62
|
+
against a fill lighter than itself. Every outline in this language is
|
|
63
|
+
near-black on the assumption of a ground the lines can be darker than; a
|
|
64
|
+
well IS at ground level, so an ink edge round one is a black line on a
|
|
65
|
+
black field. Raised takes ink, sunk takes the rule. */
|
|
66
|
+
--axi-well-fill: var(--axi-ground);
|
|
67
|
+
--axi-well-line: var(--axi-rule);
|
|
46
68
|
--axi-text: #f4f6f9;
|
|
47
69
|
--axi-text-dim: #a7b0be;
|
|
48
70
|
--axi-text-faint: #7c8695;
|
|
@@ -251,6 +273,22 @@ a { color: inherit; }
|
|
|
251
273
|
padding: var(--axi-panel-pad, 26px);
|
|
252
274
|
}
|
|
253
275
|
|
|
276
|
+
/* ---------- well ---------- */
|
|
277
|
+
/* The panel's inverse: a field sunk into the surface around it rather than
|
|
278
|
+
raised off it. No block, because a recess casts nothing, and the rule rather
|
|
279
|
+
than the ink for its edge - see --axi-well-fill in src/tokens.css for why
|
|
280
|
+
both of those follow from the same fact. Reach for this wherever a second
|
|
281
|
+
panel inside a panel was the instinct: a picker list beside a table, a column
|
|
282
|
+
of cards standing on the page, a trough a bar fills. */
|
|
283
|
+
.axi-well {
|
|
284
|
+
background: var(--axi-well-fill);
|
|
285
|
+
border: var(--axi-border-control) solid var(--axi-well-line);
|
|
286
|
+
border-radius: var(--axi-radius);
|
|
287
|
+
/* Tighter than a panel's 26px: a well is a container for objects that carry
|
|
288
|
+
their own padding, not a page margin. */
|
|
289
|
+
padding: var(--axi-well-pad, 10px);
|
|
290
|
+
}
|
|
291
|
+
|
|
254
292
|
/* ---------- button ---------- */
|
|
255
293
|
.axi-btn {
|
|
256
294
|
display: inline-flex; align-items: center; gap: 8px;
|
|
@@ -874,23 +912,167 @@ textarea.axi-input {
|
|
|
874
912
|
color: var(--axi-text-faint);
|
|
875
913
|
letter-spacing: .09em; text-transform: uppercase;
|
|
876
914
|
}
|
|
877
|
-
.
|
|
878
|
-
|
|
915
|
+
/* Anchors and buttons both. A strip that moves between URLs is made of links;
|
|
916
|
+
a strip inside an application that swaps a view in place is made of buttons,
|
|
917
|
+
and they look the same because they answer the same question. A button also
|
|
918
|
+
arrives carrying the UA's own font, fill and cursor, which an anchor does
|
|
919
|
+
not, so those three are cleared here rather than left to the consumer. */
|
|
920
|
+
.axi-tabs { display: flex; gap: 8px; }
|
|
921
|
+
/* The auto margin belongs to the masthead, not to the strip: it is a statement
|
|
922
|
+
about where the tabs sit in that row, and a strip standing on its own -
|
|
923
|
+
spanning a view instead of ending a header - has nothing to push away from. */
|
|
924
|
+
.axi-mast .axi-tabs { margin-left: auto; }
|
|
925
|
+
.axi-tabs a,
|
|
926
|
+
.axi-tabs button {
|
|
879
927
|
padding: 8px 16px;
|
|
880
928
|
font: 800 13.5px/1 var(--axi-sans);
|
|
881
929
|
color: var(--axi-text-dim); text-decoration: none;
|
|
930
|
+
background: none;
|
|
931
|
+
cursor: pointer;
|
|
882
932
|
border: var(--axi-border-control) solid transparent;
|
|
883
933
|
border-radius: var(--axi-radius-sm);
|
|
884
934
|
}
|
|
885
|
-
.axi-tabs a:hover
|
|
935
|
+
.axi-tabs a:hover,
|
|
936
|
+
.axi-tabs button:hover { color: var(--axi-text); border-color: var(--axi-rule); }
|
|
886
937
|
/* The current tab is filled and blocked - the same treatment a pressed pill
|
|
887
938
|
gets, because it is the same idea: this one is on. */
|
|
888
|
-
.axi-tabs a[aria-current="page"]
|
|
939
|
+
.axi-tabs a[aria-current="page"],
|
|
940
|
+
.axi-tabs button[aria-current="page"] {
|
|
941
|
+
background: var(--axi-accent);
|
|
942
|
+
color: var(--axi-accent-ink);
|
|
943
|
+
border-color: var(--axi-ink-line);
|
|
944
|
+
box-shadow: var(--axi-shadow-control);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
/* A closable tab - a report or a document, an open thing rather than a place.
|
|
948
|
+
The label and its close control travel together, so the pair is a box and
|
|
949
|
+
the strip's gap falls between pairs instead of between a tab and its own X.
|
|
950
|
+
|
|
951
|
+
The close control is a tab's affordance and not a tab, so it opts out of the
|
|
952
|
+
tab treatment: no tab padding, no tab weight, and faint until the cursor is
|
|
953
|
+
on it. Two classes to outrank `.axi-tabs button`, which is one class and one
|
|
954
|
+
element and would otherwise win on source order alone. */
|
|
955
|
+
/* A document title is arbitrarily long, so the label truncates rather than
|
|
956
|
+
pushing the close control off the strip. Both boxes need min-width: 0 - a
|
|
957
|
+
flex item's default min-width is its content, so without it the label simply
|
|
958
|
+
refuses to shrink and the ellipsis never appears. The consumer sets the width
|
|
959
|
+
it wants to hold on .axi-tabs__tab. */
|
|
960
|
+
.axi-tabs__tab { display: inline-flex; align-items: center; min-width: 0; }
|
|
961
|
+
.axi-tabs__tab > a:first-child,
|
|
962
|
+
.axi-tabs__tab > button:first-child {
|
|
963
|
+
min-width: 0;
|
|
964
|
+
overflow: hidden;
|
|
965
|
+
text-overflow: ellipsis;
|
|
966
|
+
white-space: nowrap;
|
|
967
|
+
}
|
|
968
|
+
.axi-tabs .axi-tabs__close {
|
|
969
|
+
padding: 0 6px;
|
|
970
|
+
font: var(--axi-t-micro);
|
|
971
|
+
color: var(--axi-text-faint);
|
|
972
|
+
border-color: transparent;
|
|
973
|
+
}
|
|
974
|
+
.axi-tabs .axi-tabs__close:hover { color: var(--axi-text); border-color: transparent; }
|
|
975
|
+
|
|
976
|
+
/* ---------- rail ---------- */
|
|
977
|
+
/* Vertical navigation, for a set too large for a tab strip to hold. Same
|
|
978
|
+
grammar as .axi-tabs deliberately - the item you are on is filled with the
|
|
979
|
+
accent and blocked, the rest are transparent until hovered - because "where
|
|
980
|
+
am I" is one question and should have one answer however the app happens to
|
|
981
|
+
be laying it out.
|
|
982
|
+
|
|
983
|
+
What a rail adds over a strip is a second level. A category is a place and
|
|
984
|
+
takes the fill; a section under it is where the page is scrolled to, which is
|
|
985
|
+
a smaller claim and is drawn as one: brightened text against the dim list
|
|
986
|
+
around it, no fill, no edge. Two fills at two levels would leave the reader
|
|
987
|
+
deciding which of them is the answer.
|
|
988
|
+
|
|
989
|
+
--axi-rail-w is declared nowhere and read with a fallback, i.e. it is the hook
|
|
990
|
+
a consumer overrides to widen the rail for longer labels. */
|
|
991
|
+
.axi-rail {
|
|
992
|
+
display: flex;
|
|
993
|
+
flex-direction: column;
|
|
994
|
+
width: var(--axi-rail-w, 208px);
|
|
995
|
+
flex: none;
|
|
996
|
+
overflow-y: auto;
|
|
997
|
+
padding: var(--axi-rail-pad, 10px);
|
|
998
|
+
background: var(--axi-surface);
|
|
999
|
+
backdrop-filter: var(--axi-surface-filter);
|
|
1000
|
+
border: var(--axi-border-panel) solid var(--axi-ink-line);
|
|
1001
|
+
border-radius: var(--axi-radius);
|
|
1002
|
+
}
|
|
1003
|
+
/* A rail that IS the edge of the page rather than a panel standing in the
|
|
1004
|
+
layout - pinned to the viewport, or a drawer's contents. It keeps the one
|
|
1005
|
+
border that faces the content and drops everything that says "this is an
|
|
1006
|
+
object with space around it": no radius on the flush side, and no block,
|
|
1007
|
+
because a block needs somewhere to fall.
|
|
1008
|
+
|
|
1009
|
+
The block is stated as what a free-standing rail has rather than as
|
|
1010
|
+
something the flush one turns off, so there is exactly one box-shadow here
|
|
1011
|
+
and it names the panel block. `box-shadow: none` would be a second spelling
|
|
1012
|
+
of the raise for a theme to miss. */
|
|
1013
|
+
.axi-rail:not(.axi-rail--flush) { box-shadow: var(--axi-shadow-panel); }
|
|
1014
|
+
.axi-rail--flush {
|
|
1015
|
+
border: 0;
|
|
1016
|
+
border-right: var(--axi-border-panel) solid var(--axi-ink-line);
|
|
1017
|
+
border-radius: 0;
|
|
1018
|
+
}
|
|
1019
|
+
.axi-rail__nav { display: flex; flex-direction: column; gap: 2px; }
|
|
1020
|
+
.axi-rail__item {
|
|
1021
|
+
display: flex;
|
|
1022
|
+
align-items: center;
|
|
1023
|
+
gap: 10px;
|
|
1024
|
+
width: 100%;
|
|
1025
|
+
padding: 8px 10px;
|
|
1026
|
+
font: 800 12.5px/1 var(--axi-sans);
|
|
1027
|
+
color: var(--axi-text-dim);
|
|
1028
|
+
text-align: left;
|
|
1029
|
+
background: transparent;
|
|
1030
|
+
border: var(--axi-border-control) solid transparent;
|
|
1031
|
+
border-radius: var(--axi-radius-sm);
|
|
1032
|
+
cursor: pointer;
|
|
1033
|
+
}
|
|
1034
|
+
.axi-rail__item:hover { color: var(--axi-text); background: var(--axi-surface-raised); }
|
|
1035
|
+
/* The same treatment a pressed pill and a current tab get. Only ever one of
|
|
1036
|
+
these on screen. Any aria-current value, because the right one depends on
|
|
1037
|
+
what the rail navigates - `page` for a view, `location` for a place within
|
|
1038
|
+
one - and the fill means the same thing either way. */
|
|
1039
|
+
.axi-rail__item[aria-current],
|
|
1040
|
+
.axi-rail__item[aria-current]:hover {
|
|
889
1041
|
background: var(--axi-accent);
|
|
890
1042
|
color: var(--axi-accent-ink);
|
|
891
1043
|
border-color: var(--axi-ink-line);
|
|
892
1044
|
box-shadow: var(--axi-shadow-control);
|
|
893
1045
|
}
|
|
1046
|
+
/* An icon beside a label is a label for it, not a second accent - so it takes
|
|
1047
|
+
the faint tone, and inside a filled item it follows the item's ink. */
|
|
1048
|
+
.axi-rail__item .axi-icon { color: var(--axi-text-faint); }
|
|
1049
|
+
.axi-rail__item[aria-current] .axi-icon { color: var(--axi-accent-ink); }
|
|
1050
|
+
/* The second level, indented under the item it belongs to. */
|
|
1051
|
+
.axi-rail__sub { display: flex; flex-direction: column; margin: 3px 0 5px 10px; }
|
|
1052
|
+
.axi-rail__subitem {
|
|
1053
|
+
padding: 5px 8px;
|
|
1054
|
+
font: 700 11.5px/1.3 var(--axi-sans);
|
|
1055
|
+
text-align: left;
|
|
1056
|
+
color: var(--axi-text-faint);
|
|
1057
|
+
background: transparent;
|
|
1058
|
+
border: 0;
|
|
1059
|
+
border-radius: var(--axi-radius-sm);
|
|
1060
|
+
cursor: pointer;
|
|
1061
|
+
}
|
|
1062
|
+
.axi-rail__subitem:hover { color: var(--axi-text-dim); background: var(--axi-surface-raised); }
|
|
1063
|
+
.axi-rail__subitem[aria-current] { color: var(--axi-text); }
|
|
1064
|
+
/* An annotation on an item, not a measurement of it, so rule 5 draws it
|
|
1065
|
+
outlined rather than filled. */
|
|
1066
|
+
.axi-rail__mark {
|
|
1067
|
+
margin-left: auto;
|
|
1068
|
+
padding: 2px 5px;
|
|
1069
|
+
font: var(--axi-t-micro);
|
|
1070
|
+
letter-spacing: .04em;
|
|
1071
|
+
text-transform: uppercase;
|
|
1072
|
+
color: var(--axi-meta);
|
|
1073
|
+
border: var(--axi-border-hairline) solid var(--axi-meta);
|
|
1074
|
+
border-radius: var(--axi-radius-sm);
|
|
1075
|
+
}
|
|
894
1076
|
|
|
895
1077
|
/* ---------- notice ---------- */
|
|
896
1078
|
.axi-notice {
|
|
@@ -1205,7 +1387,8 @@ textarea.axi-input {
|
|
|
1205
1387
|
|
|
1206
1388
|
@media (max-width: 640px) {
|
|
1207
1389
|
.axi-mast__in { min-height: 0; padding: 10px 16px; gap: 12px; }
|
|
1208
|
-
.axi-tabs { margin-left: 0;
|
|
1390
|
+
.axi-mast .axi-tabs { margin-left: 0; }
|
|
1391
|
+
.axi-tabs { width: 100%; overflow-x: auto; }
|
|
1209
1392
|
/* The offset blocks eat horizontal room a phone does not have, so the panel
|
|
1210
1393
|
step drops to the control step here rather than to an invented number.
|
|
1211
1394
|
The card's hover has to come down with it - left alone it kept deepening
|
package/dist/themes/glass.css
CHANGED
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;
|
package/src/shells.css
CHANGED
|
@@ -48,23 +48,167 @@
|
|
|
48
48
|
color: var(--axi-text-faint);
|
|
49
49
|
letter-spacing: .09em; text-transform: uppercase;
|
|
50
50
|
}
|
|
51
|
-
.
|
|
52
|
-
|
|
51
|
+
/* Anchors and buttons both. A strip that moves between URLs is made of links;
|
|
52
|
+
a strip inside an application that swaps a view in place is made of buttons,
|
|
53
|
+
and they look the same because they answer the same question. A button also
|
|
54
|
+
arrives carrying the UA's own font, fill and cursor, which an anchor does
|
|
55
|
+
not, so those three are cleared here rather than left to the consumer. */
|
|
56
|
+
.axi-tabs { display: flex; gap: 8px; }
|
|
57
|
+
/* The auto margin belongs to the masthead, not to the strip: it is a statement
|
|
58
|
+
about where the tabs sit in that row, and a strip standing on its own -
|
|
59
|
+
spanning a view instead of ending a header - has nothing to push away from. */
|
|
60
|
+
.axi-mast .axi-tabs { margin-left: auto; }
|
|
61
|
+
.axi-tabs a,
|
|
62
|
+
.axi-tabs button {
|
|
53
63
|
padding: 8px 16px;
|
|
54
64
|
font: 800 13.5px/1 var(--axi-sans);
|
|
55
65
|
color: var(--axi-text-dim); text-decoration: none;
|
|
66
|
+
background: none;
|
|
67
|
+
cursor: pointer;
|
|
56
68
|
border: var(--axi-border-control) solid transparent;
|
|
57
69
|
border-radius: var(--axi-radius-sm);
|
|
58
70
|
}
|
|
59
|
-
.axi-tabs a:hover
|
|
71
|
+
.axi-tabs a:hover,
|
|
72
|
+
.axi-tabs button:hover { color: var(--axi-text); border-color: var(--axi-rule); }
|
|
60
73
|
/* The current tab is filled and blocked - the same treatment a pressed pill
|
|
61
74
|
gets, because it is the same idea: this one is on. */
|
|
62
|
-
.axi-tabs a[aria-current="page"]
|
|
75
|
+
.axi-tabs a[aria-current="page"],
|
|
76
|
+
.axi-tabs button[aria-current="page"] {
|
|
77
|
+
background: var(--axi-accent);
|
|
78
|
+
color: var(--axi-accent-ink);
|
|
79
|
+
border-color: var(--axi-ink-line);
|
|
80
|
+
box-shadow: var(--axi-shadow-control);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* A closable tab - a report or a document, an open thing rather than a place.
|
|
84
|
+
The label and its close control travel together, so the pair is a box and
|
|
85
|
+
the strip's gap falls between pairs instead of between a tab and its own X.
|
|
86
|
+
|
|
87
|
+
The close control is a tab's affordance and not a tab, so it opts out of the
|
|
88
|
+
tab treatment: no tab padding, no tab weight, and faint until the cursor is
|
|
89
|
+
on it. Two classes to outrank `.axi-tabs button`, which is one class and one
|
|
90
|
+
element and would otherwise win on source order alone. */
|
|
91
|
+
/* A document title is arbitrarily long, so the label truncates rather than
|
|
92
|
+
pushing the close control off the strip. Both boxes need min-width: 0 - a
|
|
93
|
+
flex item's default min-width is its content, so without it the label simply
|
|
94
|
+
refuses to shrink and the ellipsis never appears. The consumer sets the width
|
|
95
|
+
it wants to hold on .axi-tabs__tab. */
|
|
96
|
+
.axi-tabs__tab { display: inline-flex; align-items: center; min-width: 0; }
|
|
97
|
+
.axi-tabs__tab > a:first-child,
|
|
98
|
+
.axi-tabs__tab > button:first-child {
|
|
99
|
+
min-width: 0;
|
|
100
|
+
overflow: hidden;
|
|
101
|
+
text-overflow: ellipsis;
|
|
102
|
+
white-space: nowrap;
|
|
103
|
+
}
|
|
104
|
+
.axi-tabs .axi-tabs__close {
|
|
105
|
+
padding: 0 6px;
|
|
106
|
+
font: var(--axi-t-micro);
|
|
107
|
+
color: var(--axi-text-faint);
|
|
108
|
+
border-color: transparent;
|
|
109
|
+
}
|
|
110
|
+
.axi-tabs .axi-tabs__close:hover { color: var(--axi-text); border-color: transparent; }
|
|
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 {
|
|
63
177
|
background: var(--axi-accent);
|
|
64
178
|
color: var(--axi-accent-ink);
|
|
65
179
|
border-color: var(--axi-ink-line);
|
|
66
180
|
box-shadow: var(--axi-shadow-control);
|
|
67
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
|
+
}
|
|
68
212
|
|
|
69
213
|
/* ---------- notice ---------- */
|
|
70
214
|
.axi-notice {
|
|
@@ -379,7 +523,8 @@
|
|
|
379
523
|
|
|
380
524
|
@media (max-width: 640px) {
|
|
381
525
|
.axi-mast__in { min-height: 0; padding: 10px 16px; gap: 12px; }
|
|
382
|
-
.axi-tabs { margin-left: 0;
|
|
526
|
+
.axi-mast .axi-tabs { margin-left: 0; }
|
|
527
|
+
.axi-tabs { width: 100%; overflow-x: auto; }
|
|
383
528
|
/* The offset blocks eat horizontal room a phone does not have, so the panel
|
|
384
529
|
step drops to the control step here rather than to an invented number.
|
|
385
530
|
The card's hover has to come down with it - left alone it kept deepening
|
package/src/tokens.css
CHANGED
|
@@ -39,6 +39,28 @@
|
|
|
39
39
|
--axi-ink-on-fill below, on the other side of the same token. */
|
|
40
40
|
--axi-ground-deep: var(--axi-ink-line);
|
|
41
41
|
--axi-rule: #3a4250; /* internal rules, inside an outlined panel */
|
|
42
|
+
/* --- the well --- */
|
|
43
|
+
/* A field sunk INTO a surface rather than raised off one: a table picker's
|
|
44
|
+
list, a bar's trough, a column of cards standing on the page. The language
|
|
45
|
+
had no name for it, and every consumer that needed one reached for a second
|
|
46
|
+
.axi-panel inside the first - two identical fills parted by a line, which
|
|
47
|
+
reads as one field with a stray rule through it.
|
|
48
|
+
|
|
49
|
+
Two tokens because a well is the exact inverse of a panel on both counts.
|
|
50
|
+
The fill is a SUBTRACTION from whatever is behind it, not a colour of its
|
|
51
|
+
own: spelled as --axi-ground it is right in this theme, where surfaces are
|
|
52
|
+
opaque and the page really is what shows through a hole in one, and wrong
|
|
53
|
+
in a glass theme, where an opaque page colour inside a translucent pane is
|
|
54
|
+
a black patch rather than a recess. A theme restates it as a darkening at
|
|
55
|
+
alpha and the panel's own tint carries through.
|
|
56
|
+
|
|
57
|
+
The line is --axi-rule and not --axi-ink-line, because ink only reads
|
|
58
|
+
against a fill lighter than itself. Every outline in this language is
|
|
59
|
+
near-black on the assumption of a ground the lines can be darker than; a
|
|
60
|
+
well IS at ground level, so an ink edge round one is a black line on a
|
|
61
|
+
black field. Raised takes ink, sunk takes the rule. */
|
|
62
|
+
--axi-well-fill: var(--axi-ground);
|
|
63
|
+
--axi-well-line: var(--axi-rule);
|
|
42
64
|
--axi-text: #f4f6f9;
|
|
43
65
|
--axi-text-dim: #a7b0be;
|
|
44
66
|
--axi-text-faint: #7c8695;
|