@axiapps/axi-design 1.34.0 → 1.36.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 +190 -30
- package/docs/RULES.md +45 -0
- package/package.json +1 -1
- package/src/data.css +16 -5
- package/src/forms.css +3 -1
- package/src/primitives.css +72 -11
- package/src/prose.css +8 -6
- package/src/shells.css +71 -6
- package/src/tokens.css +20 -1
package/dist/axi.css
CHANGED
|
@@ -84,7 +84,26 @@
|
|
|
84
84
|
against a fill lighter than itself. Every outline in this language is
|
|
85
85
|
near-black on the assumption of a ground the lines can be darker than; a
|
|
86
86
|
well IS at ground level, so an ink edge round one is a black line on a
|
|
87
|
-
black field. Raised takes ink, sunk takes the rule.
|
|
87
|
+
black field. Raised takes ink, sunk takes the rule.
|
|
88
|
+
|
|
89
|
+
And what the fill names is not the .axi-well class - it is the level. A
|
|
90
|
+
button at rest, a field, a select, a checkbox, an avatar, a rank, a code
|
|
91
|
+
span, a hovered row in a popover: not one of them is raised off what holds
|
|
92
|
+
it, and every one of them was spelled --axi-ground, because on a theme
|
|
93
|
+
whose surfaces are opaque "flat on its container" and "the page colour"
|
|
94
|
+
are the same colour. They stop being the same colour the instant a theme
|
|
95
|
+
makes the container translucent, which is how the whole set went black at
|
|
96
|
+
once and why this is written here rather than fourteen times downstream.
|
|
97
|
+
Above this level is --axi-surface (.axi-kbd is the one control in the
|
|
98
|
+
language that is genuinely raised, and says so). Below it is --axi-ground
|
|
99
|
+
proper, which now means the page and only the page: body, the sheet, the
|
|
100
|
+
window, the masthead - the things content scrolls under, which need real
|
|
101
|
+
opacity and not a subtraction. Everything between the two is this token.
|
|
102
|
+
|
|
103
|
+
The LINE does not come along. --axi-well-line is for a well cut into the
|
|
104
|
+
page, where there is nothing left for ink to be darker than; a control is
|
|
105
|
+
cut into a SURFACE and its ink edge still has a fill above it to read
|
|
106
|
+
against. Every component below keeps --axi-ink-line. */
|
|
88
107
|
--axi-well-fill: var(--axi-ground);
|
|
89
108
|
--axi-well-line: var(--axi-rule);
|
|
90
109
|
--axi-text: #f4f6f9;
|
|
@@ -388,7 +407,12 @@ button.axi-panel--tile:hover,
|
|
|
388
407
|
padding: var(--axi-btn-pad, 12px 20px);
|
|
389
408
|
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
390
409
|
border-radius: var(--axi-radius-sm);
|
|
391
|
-
|
|
410
|
+
/* Flat on its container, which is --axi-well-fill and not the page colour -
|
|
411
|
+
the argument is in src/tokens.css at that token. A button at rest carries
|
|
412
|
+
no block: it is level with the panel holding it and lifts on hover, so the
|
|
413
|
+
resting fill is the level's, and --axi-btn--primary below is the one that
|
|
414
|
+
rests raised and fills with the accent instead. */
|
|
415
|
+
background: var(--axi-well-fill);
|
|
392
416
|
color: var(--axi-text-dim);
|
|
393
417
|
font: var(--axi-t-label);
|
|
394
418
|
letter-spacing: var(--axi-ls-label);
|
|
@@ -471,7 +495,8 @@ button.axi-panel--tile:hover,
|
|
|
471
495
|
padding: var(--axi-pill-pad, 12px 20px);
|
|
472
496
|
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
473
497
|
border-radius: var(--axi-radius-sm);
|
|
474
|
-
|
|
498
|
+
/* The button's fill for the button's reason - see .axi-btn above. */
|
|
499
|
+
background: var(--axi-well-fill);
|
|
475
500
|
color: var(--axi-text-dim);
|
|
476
501
|
font: var(--axi-t-label);
|
|
477
502
|
letter-spacing: var(--axi-ls-label);
|
|
@@ -665,7 +690,10 @@ button.axi-panel--tile:hover,
|
|
|
665
690
|
.axi-input {
|
|
666
691
|
width: 100%;
|
|
667
692
|
padding: var(--axi-input-pad, 11px 12px);
|
|
668
|
-
|
|
693
|
+
/* A field is the recessed thing in any bar it sits in - .axi-kbd's comment
|
|
694
|
+
below leans on exactly that - so it takes the level's fill. See
|
|
695
|
+
--axi-well-fill in src/tokens.css. */
|
|
696
|
+
background: var(--axi-well-fill);
|
|
669
697
|
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
670
698
|
border-radius: var(--axi-radius-sm);
|
|
671
699
|
color: var(--axi-text);
|
|
@@ -700,20 +728,64 @@ button.axi-panel--tile:hover,
|
|
|
700
728
|
`em`, not `px`: this lands in body copy and in 10px captions alike, and a
|
|
701
729
|
literal that does not track the text around it reads as a different voice.
|
|
702
730
|
|
|
703
|
-
It sits on --axi-
|
|
731
|
+
It sits on --axi-well-fill where .axi-kbd sits on --axi-surface, and the
|
|
704
732
|
reason is already written down one rule below: a key is raised off what it
|
|
705
|
-
is printed on, and a quoted literal is sunk into it.
|
|
733
|
+
is printed on, and a quoted literal is sunk into it. It said --axi-ground
|
|
734
|
+
for as long as sunk and the page were the same colour; see that token. */
|
|
706
735
|
.axi-code,
|
|
707
736
|
.axi-prose code {
|
|
708
737
|
font-family: var(--axi-mono);
|
|
709
738
|
font-size: .88em;
|
|
710
|
-
background: var(--axi-
|
|
739
|
+
background: var(--axi-well-fill);
|
|
711
740
|
color: var(--axi-text);
|
|
712
741
|
border: var(--axi-border-hairline) solid var(--axi-ink-line);
|
|
713
742
|
border-radius: var(--axi-radius-sm);
|
|
714
743
|
padding: 1px 5px;
|
|
715
744
|
}
|
|
716
745
|
|
|
746
|
+
/* ---------- link ---------- */
|
|
747
|
+
/* The same trap .axi-code was pulled out of, one component later. `.axi-prose
|
|
748
|
+
a` was the language's only word for a link, and it is reachable only by
|
|
749
|
+
adopting a whole typography layer - so a consumer with a "learn more" beside
|
|
750
|
+
a setting, or a docs URL in a modal footer, writes its own. One real consumer
|
|
751
|
+
had fifteen of them, hand-spelled, and a third of those were <button>s
|
|
752
|
+
calling an openExternal bridge rather than anchors at all.
|
|
753
|
+
|
|
754
|
+
Both selectors, one rule, for the reason written at .axi-code: a link in
|
|
755
|
+
rendered markdown and a link in interface copy are the same object, and the
|
|
756
|
+
last time this language spelled one object twice the two copies drifted.
|
|
757
|
+
There is a test that they stay one rule.
|
|
758
|
+
|
|
759
|
+
The underline is stated rather than inherited. An <a href> draws one by
|
|
760
|
+
default and a <button> does not, so leaving it to the user agent is exactly
|
|
761
|
+
how the two spellings would come apart - the declaration is a no-op on the
|
|
762
|
+
anchor and load-bearing on the button. Same for the background, border and
|
|
763
|
+
padding resets: they say nothing about an anchor and are the whole reason a
|
|
764
|
+
button can wear this class.
|
|
765
|
+
|
|
766
|
+
`font-family` and `font-size` inherit, but the weight does not: a link is
|
|
767
|
+
600 wherever it lands, and `font: inherit` would quietly take that away.
|
|
768
|
+
|
|
769
|
+
The hover is written `:where(:hover)` so it weighs one class and the ink
|
|
770
|
+
layer still lands on top - see rule 6's addendum. A link in a caption that
|
|
771
|
+
says `axi-link axi-ink-dim` must stay dim under the cursor, and the plain
|
|
772
|
+
`:hover` form is what made the danger button read white under one. */
|
|
773
|
+
.axi-link,
|
|
774
|
+
.axi-prose a {
|
|
775
|
+
padding: 0;
|
|
776
|
+
background: none;
|
|
777
|
+
border: 0;
|
|
778
|
+
font-family: inherit;
|
|
779
|
+
font-size: inherit;
|
|
780
|
+
font-weight: 600;
|
|
781
|
+
color: var(--axi-accent);
|
|
782
|
+
text-decoration: underline;
|
|
783
|
+
text-underline-offset: 2px;
|
|
784
|
+
cursor: pointer;
|
|
785
|
+
}
|
|
786
|
+
.axi-link:where(:hover),
|
|
787
|
+
.axi-prose a:where(:hover) { color: var(--axi-text); }
|
|
788
|
+
|
|
717
789
|
/* ---------- keyboard key ---------- */
|
|
718
790
|
/* A key on the keyboard, named in the interface: the Esc that closes a
|
|
719
791
|
palette, the Ctrl K that opens it. Drawn as a small control rather than as a
|
|
@@ -747,7 +819,10 @@ button.axi-panel--tile:hover,
|
|
|
747
819
|
padding: 10px 30px 10px 9px;
|
|
748
820
|
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
749
821
|
border-radius: var(--axi-radius-sm);
|
|
750
|
-
|
|
822
|
+
/* The field's fill, because a closed select is a field - see .axi-input. The
|
|
823
|
+
longhand stays: a caret rides in background-image and the shorthand would
|
|
824
|
+
reset it. */
|
|
825
|
+
background-color: var(--axi-well-fill);
|
|
751
826
|
color: var(--axi-text-dim);
|
|
752
827
|
font: var(--axi-t-label);
|
|
753
828
|
font-size: 12.5px;
|
|
@@ -806,8 +881,12 @@ button.axi-panel--tile:hover,
|
|
|
806
881
|
letter-spacing: var(--axi-ls-label);
|
|
807
882
|
text-transform: uppercase;
|
|
808
883
|
}
|
|
884
|
+
/* A row under the cursor sinks rather than lighting up, so the hover is the
|
|
885
|
+
level's fill - and this popover is filled with --axi-surface-raised, which
|
|
886
|
+
a theme may take translucent, so an opaque row here is the black patch
|
|
887
|
+
--axi-well-fill exists to prevent. Same for the picker's rows below. */
|
|
809
888
|
.axi-select option:hover, .axi-select option:focus {
|
|
810
|
-
background: var(--axi-
|
|
889
|
+
background: var(--axi-well-fill); color: var(--axi-text);
|
|
811
890
|
}
|
|
812
891
|
/* The page-wide focus ring sits 2px outside its element; inside a picker
|
|
813
892
|
that is 2px into the neighbouring row, so pull it back in. */
|
|
@@ -906,7 +985,7 @@ button.axi-panel--tile:hover,
|
|
|
906
985
|
font-weight: 900;
|
|
907
986
|
}
|
|
908
987
|
.axi-picker__opt:hover, .axi-picker__opt:focus {
|
|
909
|
-
background: var(--axi-
|
|
988
|
+
background: var(--axi-well-fill); color: var(--axi-text);
|
|
910
989
|
}
|
|
911
990
|
/* The page-wide focus ring sits 2px outside its element; inside a popover
|
|
912
991
|
this tight that is 2px into the neighbouring row, so pull it back in. */
|
|
@@ -921,7 +1000,8 @@ button.axi-panel--tile:hover,
|
|
|
921
1000
|
who wants round avatars sets --axi-radius-sm and gets rounded controls
|
|
922
1001
|
everywhere - which is the honest version of the request.
|
|
923
1002
|
Flat: outlined, no block. An avatar is content inside a panel, the same as
|
|
924
|
-
.axi-stat and .axi-table__rank, not a thing raised off it
|
|
1003
|
+
.axi-stat and .axi-table__rank, not a thing raised off it - which is what
|
|
1004
|
+
--axi-well-fill below is saying, and all three now say it the same way. */
|
|
925
1005
|
.axi-avatar {
|
|
926
1006
|
width: var(--axi-avatar-size, 40px);
|
|
927
1007
|
height: var(--axi-avatar-size, 40px);
|
|
@@ -929,7 +1009,7 @@ button.axi-panel--tile:hover,
|
|
|
929
1009
|
display: grid;
|
|
930
1010
|
place-items: center;
|
|
931
1011
|
overflow: hidden;
|
|
932
|
-
background: var(--axi-
|
|
1012
|
+
background: var(--axi-well-fill);
|
|
933
1013
|
color: var(--axi-text-dim);
|
|
934
1014
|
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
935
1015
|
border-radius: var(--axi-radius-sm);
|
|
@@ -994,7 +1074,9 @@ button.axi-panel--tile:hover,
|
|
|
994
1074
|
height: var(--axi-check-size, 22px);
|
|
995
1075
|
display: inline-grid;
|
|
996
1076
|
place-items: center;
|
|
997
|
-
|
|
1077
|
+
/* An empty box is a hole you put a mark in, so the level's fill - see
|
|
1078
|
+
--axi-well-fill in src/tokens.css. :checked below replaces it outright. */
|
|
1079
|
+
background: var(--axi-well-fill);
|
|
998
1080
|
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
999
1081
|
/* The text-sized step, not the control-sized one - see --axi-radius-xs in
|
|
1000
1082
|
src/tokens.css for why a 22px box cannot take a corner scaled for a 36px
|
|
@@ -1116,9 +1198,18 @@ textarea.axi-input {
|
|
|
1116
1198
|
}
|
|
1117
1199
|
|
|
1118
1200
|
/* ---------- masthead ---------- */
|
|
1201
|
+
/* Opaque, because content scrolls under it. Which is also why it paints the
|
|
1202
|
+
page's light as well as the page's colour: an opaque strip across the top of
|
|
1203
|
+
a lit page is a dark band over that light, and the band is there on every
|
|
1204
|
+
page of a site the whole time it is open. Attachment fixed, the same as
|
|
1205
|
+
`body`, so the strip's share of the light is the share the page would have
|
|
1206
|
+
shown there - a wash positioned to this element's own box instead would
|
|
1207
|
+
line up with nothing. */
|
|
1119
1208
|
.axi-mast {
|
|
1120
1209
|
position: sticky; top: 0; z-index: 40;
|
|
1121
|
-
background: var(--axi-ground);
|
|
1210
|
+
background-color: var(--axi-ground);
|
|
1211
|
+
background-image: var(--axi-ground-image);
|
|
1212
|
+
background-attachment: fixed;
|
|
1122
1213
|
border-bottom: var(--axi-border-panel) solid var(--axi-ink-line);
|
|
1123
1214
|
}
|
|
1124
1215
|
.axi-mast__in {
|
|
@@ -1329,6 +1420,41 @@ textarea.axi-input {
|
|
|
1329
1420
|
the faint tone, and inside a filled item it follows the item's ink. */
|
|
1330
1421
|
.axi-rail__item .axi-icon { color: var(--axi-text-faint); }
|
|
1331
1422
|
.axi-rail__item[aria-current] .axi-icon { color: var(--axi-accent-ink); }
|
|
1423
|
+
/* A rail nested inside a panel whose content already spends the accent: a
|
|
1424
|
+
metric picker beside the table it drives, a filter list beside its results.
|
|
1425
|
+
The language already refuses two fills at two levels inside one rail - that
|
|
1426
|
+
is why .axi-rail__subitem has no fill - and a rail that is itself the inner
|
|
1427
|
+
level is the same refusal one container further out. Two filled rails on
|
|
1428
|
+
one screen name two places, and only one of them is where you are.
|
|
1429
|
+
|
|
1430
|
+
What it cannot do is borrow the subitem's answer. A subitem is one of a
|
|
1431
|
+
handful of leaves under an open category; a picker like this is twenty rows
|
|
1432
|
+
and the primary control of its own panel, so brightened text alone loses
|
|
1433
|
+
the selection in the list. This is the third weight between the two: the
|
|
1434
|
+
row rises off the rail as any hovered row does, and the accent arrives on
|
|
1435
|
+
its leading edge. The accent still says which row without the row claiming
|
|
1436
|
+
to be the place.
|
|
1437
|
+
|
|
1438
|
+
The edge is the item's own border, which .axi-rail__item already reserves
|
|
1439
|
+
at the control weight and draws transparent - so the bar costs no shadow,
|
|
1440
|
+
no extra box and no reflow when it lights up. Logical, not left: this is
|
|
1441
|
+
the leading edge, the same correction .axi-rail--flush took.
|
|
1442
|
+
|
|
1443
|
+
The modifier goes on the list, not on .axi-rail, because a nested picker
|
|
1444
|
+
usually has no rail box around it - it sits directly in the panel or well
|
|
1445
|
+
that holds it, and inheriting a 208px width and a panel shadow is the
|
|
1446
|
+
opposite of what it wants. */
|
|
1447
|
+
.axi-rail__nav--quiet .axi-rail__item[aria-current],
|
|
1448
|
+
.axi-rail__nav--quiet .axi-rail__item[aria-current]:hover {
|
|
1449
|
+
background: var(--axi-surface-raised);
|
|
1450
|
+
color: var(--axi-text);
|
|
1451
|
+
border-color: transparent;
|
|
1452
|
+
border-inline-start-color: var(--axi-accent);
|
|
1453
|
+
}
|
|
1454
|
+
/* No accent fill here, so the icon has no accent ink to follow - it follows
|
|
1455
|
+
the row's own text, the way it does in an unselected item. */
|
|
1456
|
+
.axi-rail__nav--quiet .axi-rail__item[aria-current] .axi-icon { color: var(--axi-text); }
|
|
1457
|
+
|
|
1332
1458
|
/* The second level, indented under the item it belongs to. */
|
|
1333
1459
|
.axi-rail__sub { display: flex; flex-direction: column; margin: 3px 0 5px 10px; }
|
|
1334
1460
|
.axi-rail__subitem {
|
|
@@ -1494,7 +1620,8 @@ textarea.axi-input {
|
|
|
1494
1620
|
padding: 7px 8px; border-radius: var(--axi-radius-sm);
|
|
1495
1621
|
font-size: 13px; font-weight: 600; color: var(--axi-text-dim); cursor: pointer;
|
|
1496
1622
|
}
|
|
1497
|
-
|
|
1623
|
+
/* A hovered row sinks - the same fill the select's and the picker's rows use. */
|
|
1624
|
+
.axi-menu__pop label:hover { background: var(--axi-well-fill); color: var(--axi-text); }
|
|
1498
1625
|
.axi-menu__pop input { margin: 3px 0 0; }
|
|
1499
1626
|
|
|
1500
1627
|
/* ---------- command palette ---------- */
|
|
@@ -1608,7 +1735,7 @@ textarea.axi-input {
|
|
|
1608
1735
|
.axi-palette__trigger {
|
|
1609
1736
|
display: flex; align-items: center; width: 100%; overflow: hidden;
|
|
1610
1737
|
padding: 0 10px 0 0;
|
|
1611
|
-
background: var(--axi-
|
|
1738
|
+
background: var(--axi-well-fill);
|
|
1612
1739
|
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
1613
1740
|
border-radius: var(--axi-radius-sm);
|
|
1614
1741
|
color: var(--axi-text-dim);
|
|
@@ -1792,7 +1919,8 @@ textarea.axi-input {
|
|
|
1792
1919
|
width: 32px; height: 32px;
|
|
1793
1920
|
display: grid; place-items: center;
|
|
1794
1921
|
border-radius: var(--axi-radius-sm);
|
|
1795
|
-
|
|
1922
|
+
/* A control flat on the drawer's head - see --axi-well-fill in tokens. */
|
|
1923
|
+
background: var(--axi-well-fill);
|
|
1796
1924
|
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
1797
1925
|
color: var(--axi-text-dim);
|
|
1798
1926
|
font-size: 15px; font-weight: 900; cursor: pointer;
|
|
@@ -1858,6 +1986,13 @@ textarea.axi-input {
|
|
|
1858
1986
|
overflow-y: auto;
|
|
1859
1987
|
background-color: var(--axi-ground);
|
|
1860
1988
|
background-image: var(--axi-ground-image);
|
|
1989
|
+
/* Fixed for both of the reasons `body` is: the light stays put while the
|
|
1990
|
+
sheet's own content scrolls, and it is positioned to the viewport rather
|
|
1991
|
+
than to this box - which matters here because --axi-sheet-top pushes that
|
|
1992
|
+
box down under an app's titlebar, and a wash measured from its top edge
|
|
1993
|
+
would sit lower than the one on the page it covers. Opening a sheet would
|
|
1994
|
+
nudge the light sideways. */
|
|
1995
|
+
background-attachment: fixed;
|
|
1861
1996
|
padding: var(--axi-sheet-pad, 12px 16px);
|
|
1862
1997
|
}
|
|
1863
1998
|
/* The heading the sheet opened with, divided from the body by a rule. Rule 8:
|
|
@@ -1880,7 +2015,8 @@ textarea.axi-input {
|
|
|
1880
2015
|
margin: 0;
|
|
1881
2016
|
padding: 11px 13px;
|
|
1882
2017
|
border-left: var(--axi-border-panel) solid var(--axi-accent);
|
|
1883
|
-
|
|
2018
|
+
/* Sunk into the prose around it, so the well's fill - see .axi-stat. */
|
|
2019
|
+
background: var(--axi-well-fill);
|
|
1884
2020
|
border-radius: 0 var(--axi-radius-sm) var(--axi-radius-sm) 0;
|
|
1885
2021
|
}
|
|
1886
2022
|
.axi-quote :where(p) { margin: 0; font-size: 13px; font-style: italic; color: var(--axi-text-dim); line-height: 1.5; }
|
|
@@ -1897,13 +2033,24 @@ textarea.axi-input {
|
|
|
1897
2033
|
takes the panel outline. It carries no block, because a block is an
|
|
1898
2034
|
element's shadow on the surface behind it and there is nothing behind a
|
|
1899
2035
|
window that this language is entitled to draw on.
|
|
2036
|
+
|
|
2037
|
+
It is the page. Not a page inside one - the only one, for as long as the app
|
|
2038
|
+
is running, which is why it paints the ground's IMAGE as well as the
|
|
2039
|
+
ground's colour. `body` is behind it and lights nothing, because a window
|
|
2040
|
+
covers the viewport and is opaque by the same requirement that makes it
|
|
2041
|
+
opaque over its own content. A window that skipped the image would render a
|
|
2042
|
+
theme whose character is the light - glass - as flat near-black: every panel
|
|
2043
|
+
inside it correctly translucent, over nothing. Longhands for the reason
|
|
2044
|
+
.axi-sheet gives at length below; no attachment, because this element's box
|
|
2045
|
+
IS the viewport and never scrolls, so fixed and scroll paint the same pixels.
|
|
1900
2046
|
The titlebar is filled with the ink line rather than a surface: it is the
|
|
1901
2047
|
outline widened into a strip, which is why the app's content reads as
|
|
1902
2048
|
sitting inside the outline instead of under a second toolbar. */
|
|
1903
2049
|
.axi-window {
|
|
1904
2050
|
height: 100vh;
|
|
1905
2051
|
display: flex; flex-direction: column; overflow: hidden;
|
|
1906
|
-
background: var(--axi-ground);
|
|
2052
|
+
background-color: var(--axi-ground);
|
|
2053
|
+
background-image: var(--axi-ground-image);
|
|
1907
2054
|
border: var(--axi-border-panel) solid var(--axi-ink-line);
|
|
1908
2055
|
border-radius: var(--axi-radius);
|
|
1909
2056
|
}
|
|
@@ -2170,10 +2317,18 @@ textarea.axi-input {
|
|
|
2170
2317
|
an outline and no block - it is content, not something raised off the
|
|
2171
2318
|
surface it sits on. The number stays in --axi-text unless it has a real
|
|
2172
2319
|
state: rule 5 applies to a figure exactly as it applies to a chip, and a
|
|
2173
|
-
tile coloured for emphasis is decoration impersonating status.
|
|
2320
|
+
tile coloured for emphasis is decoration impersonating status.
|
|
2321
|
+
|
|
2322
|
+
Flat on the ground is the WELL's fill and not the page's. A tile is a recess
|
|
2323
|
+
cut into the panel holding it, which is the same shape .axi-well names, and
|
|
2324
|
+
spelled --axi-ground it stops being a recess the moment a theme makes that
|
|
2325
|
+
panel translucent: an opaque page colour inside a pane of glass is a black
|
|
2326
|
+
patch. The outline stays the ink line rather than following the well's,
|
|
2327
|
+
because a tile is cut into a SURFACE and an ink edge still has something to
|
|
2328
|
+
be darker than there. Same split in .axi-meter and .axi-plot below. */
|
|
2174
2329
|
.axi-stat {
|
|
2175
2330
|
padding: 12px 14px;
|
|
2176
|
-
background: var(--axi-
|
|
2331
|
+
background: var(--axi-well-fill);
|
|
2177
2332
|
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
2178
2333
|
border-radius: var(--axi-radius-sm);
|
|
2179
2334
|
}
|
|
@@ -2260,7 +2415,8 @@ textarea.axi-input {
|
|
|
2260
2415
|
display: grid; place-items: center;
|
|
2261
2416
|
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
2262
2417
|
border-radius: var(--axi-radius-sm);
|
|
2263
|
-
|
|
2418
|
+
/* Flat in the row, like the tile above and the avatar it stands beside. */
|
|
2419
|
+
background: var(--axi-well-fill);
|
|
2264
2420
|
font: var(--axi-t-micro);
|
|
2265
2421
|
color: var(--axi-text-faint);
|
|
2266
2422
|
}
|
|
@@ -2364,7 +2520,8 @@ textarea.axi-input {
|
|
|
2364
2520
|
.axi-meter {
|
|
2365
2521
|
display: flex;
|
|
2366
2522
|
height: var(--axi-meter-h, 12px);
|
|
2367
|
-
|
|
2523
|
+
/* The trough is a recess, so it takes the well's fill - see .axi-stat. */
|
|
2524
|
+
background: var(--axi-well-fill);
|
|
2368
2525
|
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
2369
2526
|
border-radius: var(--axi-radius-sm);
|
|
2370
2527
|
overflow: hidden;
|
|
@@ -2480,7 +2637,8 @@ textarea.axi-input {
|
|
|
2480
2637
|
.axi-plot {
|
|
2481
2638
|
position: relative;
|
|
2482
2639
|
height: var(--axi-plot-h, 180px);
|
|
2483
|
-
|
|
2640
|
+
/* A frame cut into the panel, not laid on the page - see .axi-stat. */
|
|
2641
|
+
background-color: var(--axi-well-fill);
|
|
2484
2642
|
background-image: repeating-linear-gradient(
|
|
2485
2643
|
to top,
|
|
2486
2644
|
var(--axi-rule) 0 var(--axi-border-hairline),
|
|
@@ -2630,10 +2788,9 @@ textarea.axi-input {
|
|
|
2630
2788
|
|
|
2631
2789
|
.axi-prose p { margin: 0 0 16px; }
|
|
2632
2790
|
.axi-prose strong { color: var(--axi-text); font-weight: 800; }
|
|
2633
|
-
/*
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
.axi-prose a:hover { color: var(--axi-text); }
|
|
2791
|
+
/* A link is drawn beside .axi-code in primitives.css, in one rule with
|
|
2792
|
+
`.axi-link`, because a link in an article and a link in interface copy are
|
|
2793
|
+
the same object. See the comment there. */
|
|
2637
2794
|
|
|
2638
2795
|
.axi-prose ul, .axi-prose ol { margin: 0 0 16px; padding-left: 22px; }
|
|
2639
2796
|
.axi-prose li { margin: 0 0 7px; }
|
|
@@ -2650,9 +2807,12 @@ textarea.axi-input {
|
|
|
2650
2807
|
|
|
2651
2808
|
/* .axi-prose code is declared beside .axi-code in primitives.css: one rule,
|
|
2652
2809
|
both selectors, because they are one object. */
|
|
2810
|
+
/* A code block and a quote are both fields sunk into the prose around them,
|
|
2811
|
+
so both take --axi-well-fill rather than the page colour - the reason is
|
|
2812
|
+
written out at .axi-stat in src/data.css. */
|
|
2653
2813
|
.axi-prose pre {
|
|
2654
2814
|
margin: 0 0 18px; padding: 14px 16px; overflow-x: auto;
|
|
2655
|
-
background: var(--axi-
|
|
2815
|
+
background: var(--axi-well-fill);
|
|
2656
2816
|
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
2657
2817
|
border-radius: var(--axi-radius-sm);
|
|
2658
2818
|
box-shadow: var(--axi-shadow-control);
|
|
@@ -2663,7 +2823,7 @@ textarea.axi-input {
|
|
|
2663
2823
|
margin: 0 0 18px;
|
|
2664
2824
|
padding: 11px 13px;
|
|
2665
2825
|
border-left: var(--axi-border-panel) solid var(--axi-accent);
|
|
2666
|
-
background: var(--axi-
|
|
2826
|
+
background: var(--axi-well-fill);
|
|
2667
2827
|
border-radius: 0 var(--axi-radius-sm) var(--axi-radius-sm) 0;
|
|
2668
2828
|
}
|
|
2669
2829
|
.axi-prose blockquote p { margin: 0; font-style: italic; }
|
package/docs/RULES.md
CHANGED
|
@@ -702,3 +702,48 @@ whole section exists to prevent.
|
|
|
702
702
|
|
|
703
703
|
The test to write is not "does `.axi-code` exist". It is "are both spellings in
|
|
704
704
|
the same rule", because only the second one fails when someone splits them.
|
|
705
|
+
|
|
706
|
+
This has now happened twice. `.axi-prose a` was the language's only word for a
|
|
707
|
+
link, and the same consumer had fifteen hand-spelled ones — a third of them
|
|
708
|
+
`<button>`s calling a desktop bridge rather than anchors at all, which is what
|
|
709
|
+
made the drift concrete rather than theoretical: an `<a href>` draws its own
|
|
710
|
+
underline and a `<button>` does not, so the two spellings were not merely
|
|
711
|
+
allowed to come apart, they had already come apart. When you lift a
|
|
712
|
+
layer-scoped style out, look for the declarations the layer was getting for
|
|
713
|
+
free from its element. Those are the ones the new spelling silently loses.
|
|
714
|
+
|
|
715
|
+
Two instances is a pattern, so the check belongs at the top of the list when
|
|
716
|
+
adding anything: grep `src/` for the component's style living behind a layer
|
|
717
|
+
prefix. If it does, it has consumers you cannot see, and they have already
|
|
718
|
+
drawn their own.
|
|
719
|
+
|
|
720
|
+
### A refusal holds at every level, not just the one it was written for
|
|
721
|
+
|
|
722
|
+
The rail refuses two accent fills inside itself. That is why
|
|
723
|
+
`.axi-rail__subitem` is brightened text and not a second filled row: with two
|
|
724
|
+
fills the reader has to decide which of them answers "where am I", and the
|
|
725
|
+
answer to one question cannot be two things.
|
|
726
|
+
|
|
727
|
+
Written that way, the refusal sounds like it is about indentation. It is not.
|
|
728
|
+
It is about how many times one screen may claim to be a place, and nesting a
|
|
729
|
+
whole rail inside a panel is the same arithmetic as nesting a row inside a
|
|
730
|
+
rail. A stats page with a category rail down the side and a twenty-row metric
|
|
731
|
+
picker inside each section has two rails, both correct on their own, and both
|
|
732
|
+
filled — so the screen makes the claim twice and neither wins. The app that
|
|
733
|
+
hit this had already reasoned its way to the same conclusion and written the
|
|
734
|
+
quiet treatment by hand, in its own stylesheet, with a comment giving exactly
|
|
735
|
+
this reason. Two parties deriving the same rule independently is the signal
|
|
736
|
+
that the rule belongs in the language, not in either party's override file.
|
|
737
|
+
|
|
738
|
+
So when adding a component, take every refusal the neighbouring components
|
|
739
|
+
state and ask what it is really counting. If the answer is "per screen" rather
|
|
740
|
+
than "per box", the component needs a way to stand down — and the modifier is
|
|
741
|
+
cheaper than the override every consumer writes instead. `.axi-rail__nav--quiet`
|
|
742
|
+
is that: the fill goes, the accent stays as the leading edge, and nothing else
|
|
743
|
+
moves.
|
|
744
|
+
|
|
745
|
+
What the modifier must not do is reach for the weaker treatment that already
|
|
746
|
+
exists. A subitem drops the fill *and* the weight, which is right for a few
|
|
747
|
+
leaves under an open category and wrong for a picker that is the primary
|
|
748
|
+
control of its own panel — there, brightened text loses the selection in the
|
|
749
|
+
list. Standing down is one step, not two.
|
package/package.json
CHANGED
package/src/data.css
CHANGED
|
@@ -15,10 +15,18 @@
|
|
|
15
15
|
an outline and no block - it is content, not something raised off the
|
|
16
16
|
surface it sits on. The number stays in --axi-text unless it has a real
|
|
17
17
|
state: rule 5 applies to a figure exactly as it applies to a chip, and a
|
|
18
|
-
tile coloured for emphasis is decoration impersonating status.
|
|
18
|
+
tile coloured for emphasis is decoration impersonating status.
|
|
19
|
+
|
|
20
|
+
Flat on the ground is the WELL's fill and not the page's. A tile is a recess
|
|
21
|
+
cut into the panel holding it, which is the same shape .axi-well names, and
|
|
22
|
+
spelled --axi-ground it stops being a recess the moment a theme makes that
|
|
23
|
+
panel translucent: an opaque page colour inside a pane of glass is a black
|
|
24
|
+
patch. The outline stays the ink line rather than following the well's,
|
|
25
|
+
because a tile is cut into a SURFACE and an ink edge still has something to
|
|
26
|
+
be darker than there. Same split in .axi-meter and .axi-plot below. */
|
|
19
27
|
.axi-stat {
|
|
20
28
|
padding: 12px 14px;
|
|
21
|
-
background: var(--axi-
|
|
29
|
+
background: var(--axi-well-fill);
|
|
22
30
|
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
23
31
|
border-radius: var(--axi-radius-sm);
|
|
24
32
|
}
|
|
@@ -105,7 +113,8 @@
|
|
|
105
113
|
display: grid; place-items: center;
|
|
106
114
|
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
107
115
|
border-radius: var(--axi-radius-sm);
|
|
108
|
-
|
|
116
|
+
/* Flat in the row, like the tile above and the avatar it stands beside. */
|
|
117
|
+
background: var(--axi-well-fill);
|
|
109
118
|
font: var(--axi-t-micro);
|
|
110
119
|
color: var(--axi-text-faint);
|
|
111
120
|
}
|
|
@@ -209,7 +218,8 @@
|
|
|
209
218
|
.axi-meter {
|
|
210
219
|
display: flex;
|
|
211
220
|
height: var(--axi-meter-h, 12px);
|
|
212
|
-
|
|
221
|
+
/* The trough is a recess, so it takes the well's fill - see .axi-stat. */
|
|
222
|
+
background: var(--axi-well-fill);
|
|
213
223
|
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
214
224
|
border-radius: var(--axi-radius-sm);
|
|
215
225
|
overflow: hidden;
|
|
@@ -325,7 +335,8 @@
|
|
|
325
335
|
.axi-plot {
|
|
326
336
|
position: relative;
|
|
327
337
|
height: var(--axi-plot-h, 180px);
|
|
328
|
-
|
|
338
|
+
/* A frame cut into the panel, not laid on the page - see .axi-stat. */
|
|
339
|
+
background-color: var(--axi-well-fill);
|
|
329
340
|
background-image: repeating-linear-gradient(
|
|
330
341
|
to top,
|
|
331
342
|
var(--axi-rule) 0 var(--axi-border-hairline),
|
package/src/forms.css
CHANGED
|
@@ -34,7 +34,9 @@
|
|
|
34
34
|
height: var(--axi-check-size, 22px);
|
|
35
35
|
display: inline-grid;
|
|
36
36
|
place-items: center;
|
|
37
|
-
|
|
37
|
+
/* An empty box is a hole you put a mark in, so the level's fill - see
|
|
38
|
+
--axi-well-fill in src/tokens.css. :checked below replaces it outright. */
|
|
39
|
+
background: var(--axi-well-fill);
|
|
38
40
|
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
39
41
|
/* The text-sized step, not the control-sized one - see --axi-radius-xs in
|
|
40
42
|
src/tokens.css for why a 22px box cannot take a corner scaled for a 36px
|
package/src/primitives.css
CHANGED
|
@@ -114,7 +114,12 @@ button.axi-panel--tile:hover,
|
|
|
114
114
|
padding: var(--axi-btn-pad, 12px 20px);
|
|
115
115
|
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
116
116
|
border-radius: var(--axi-radius-sm);
|
|
117
|
-
|
|
117
|
+
/* Flat on its container, which is --axi-well-fill and not the page colour -
|
|
118
|
+
the argument is in src/tokens.css at that token. A button at rest carries
|
|
119
|
+
no block: it is level with the panel holding it and lifts on hover, so the
|
|
120
|
+
resting fill is the level's, and --axi-btn--primary below is the one that
|
|
121
|
+
rests raised and fills with the accent instead. */
|
|
122
|
+
background: var(--axi-well-fill);
|
|
118
123
|
color: var(--axi-text-dim);
|
|
119
124
|
font: var(--axi-t-label);
|
|
120
125
|
letter-spacing: var(--axi-ls-label);
|
|
@@ -197,7 +202,8 @@ button.axi-panel--tile:hover,
|
|
|
197
202
|
padding: var(--axi-pill-pad, 12px 20px);
|
|
198
203
|
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
199
204
|
border-radius: var(--axi-radius-sm);
|
|
200
|
-
|
|
205
|
+
/* The button's fill for the button's reason - see .axi-btn above. */
|
|
206
|
+
background: var(--axi-well-fill);
|
|
201
207
|
color: var(--axi-text-dim);
|
|
202
208
|
font: var(--axi-t-label);
|
|
203
209
|
letter-spacing: var(--axi-ls-label);
|
|
@@ -391,7 +397,10 @@ button.axi-panel--tile:hover,
|
|
|
391
397
|
.axi-input {
|
|
392
398
|
width: 100%;
|
|
393
399
|
padding: var(--axi-input-pad, 11px 12px);
|
|
394
|
-
|
|
400
|
+
/* A field is the recessed thing in any bar it sits in - .axi-kbd's comment
|
|
401
|
+
below leans on exactly that - so it takes the level's fill. See
|
|
402
|
+
--axi-well-fill in src/tokens.css. */
|
|
403
|
+
background: var(--axi-well-fill);
|
|
395
404
|
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
396
405
|
border-radius: var(--axi-radius-sm);
|
|
397
406
|
color: var(--axi-text);
|
|
@@ -426,20 +435,64 @@ button.axi-panel--tile:hover,
|
|
|
426
435
|
`em`, not `px`: this lands in body copy and in 10px captions alike, and a
|
|
427
436
|
literal that does not track the text around it reads as a different voice.
|
|
428
437
|
|
|
429
|
-
It sits on --axi-
|
|
438
|
+
It sits on --axi-well-fill where .axi-kbd sits on --axi-surface, and the
|
|
430
439
|
reason is already written down one rule below: a key is raised off what it
|
|
431
|
-
is printed on, and a quoted literal is sunk into it.
|
|
440
|
+
is printed on, and a quoted literal is sunk into it. It said --axi-ground
|
|
441
|
+
for as long as sunk and the page were the same colour; see that token. */
|
|
432
442
|
.axi-code,
|
|
433
443
|
.axi-prose code {
|
|
434
444
|
font-family: var(--axi-mono);
|
|
435
445
|
font-size: .88em;
|
|
436
|
-
background: var(--axi-
|
|
446
|
+
background: var(--axi-well-fill);
|
|
437
447
|
color: var(--axi-text);
|
|
438
448
|
border: var(--axi-border-hairline) solid var(--axi-ink-line);
|
|
439
449
|
border-radius: var(--axi-radius-sm);
|
|
440
450
|
padding: 1px 5px;
|
|
441
451
|
}
|
|
442
452
|
|
|
453
|
+
/* ---------- link ---------- */
|
|
454
|
+
/* The same trap .axi-code was pulled out of, one component later. `.axi-prose
|
|
455
|
+
a` was the language's only word for a link, and it is reachable only by
|
|
456
|
+
adopting a whole typography layer - so a consumer with a "learn more" beside
|
|
457
|
+
a setting, or a docs URL in a modal footer, writes its own. One real consumer
|
|
458
|
+
had fifteen of them, hand-spelled, and a third of those were <button>s
|
|
459
|
+
calling an openExternal bridge rather than anchors at all.
|
|
460
|
+
|
|
461
|
+
Both selectors, one rule, for the reason written at .axi-code: a link in
|
|
462
|
+
rendered markdown and a link in interface copy are the same object, and the
|
|
463
|
+
last time this language spelled one object twice the two copies drifted.
|
|
464
|
+
There is a test that they stay one rule.
|
|
465
|
+
|
|
466
|
+
The underline is stated rather than inherited. An <a href> draws one by
|
|
467
|
+
default and a <button> does not, so leaving it to the user agent is exactly
|
|
468
|
+
how the two spellings would come apart - the declaration is a no-op on the
|
|
469
|
+
anchor and load-bearing on the button. Same for the background, border and
|
|
470
|
+
padding resets: they say nothing about an anchor and are the whole reason a
|
|
471
|
+
button can wear this class.
|
|
472
|
+
|
|
473
|
+
`font-family` and `font-size` inherit, but the weight does not: a link is
|
|
474
|
+
600 wherever it lands, and `font: inherit` would quietly take that away.
|
|
475
|
+
|
|
476
|
+
The hover is written `:where(:hover)` so it weighs one class and the ink
|
|
477
|
+
layer still lands on top - see rule 6's addendum. A link in a caption that
|
|
478
|
+
says `axi-link axi-ink-dim` must stay dim under the cursor, and the plain
|
|
479
|
+
`:hover` form is what made the danger button read white under one. */
|
|
480
|
+
.axi-link,
|
|
481
|
+
.axi-prose a {
|
|
482
|
+
padding: 0;
|
|
483
|
+
background: none;
|
|
484
|
+
border: 0;
|
|
485
|
+
font-family: inherit;
|
|
486
|
+
font-size: inherit;
|
|
487
|
+
font-weight: 600;
|
|
488
|
+
color: var(--axi-accent);
|
|
489
|
+
text-decoration: underline;
|
|
490
|
+
text-underline-offset: 2px;
|
|
491
|
+
cursor: pointer;
|
|
492
|
+
}
|
|
493
|
+
.axi-link:where(:hover),
|
|
494
|
+
.axi-prose a:where(:hover) { color: var(--axi-text); }
|
|
495
|
+
|
|
443
496
|
/* ---------- keyboard key ---------- */
|
|
444
497
|
/* A key on the keyboard, named in the interface: the Esc that closes a
|
|
445
498
|
palette, the Ctrl K that opens it. Drawn as a small control rather than as a
|
|
@@ -473,7 +526,10 @@ button.axi-panel--tile:hover,
|
|
|
473
526
|
padding: 10px 30px 10px 9px;
|
|
474
527
|
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
475
528
|
border-radius: var(--axi-radius-sm);
|
|
476
|
-
|
|
529
|
+
/* The field's fill, because a closed select is a field - see .axi-input. The
|
|
530
|
+
longhand stays: a caret rides in background-image and the shorthand would
|
|
531
|
+
reset it. */
|
|
532
|
+
background-color: var(--axi-well-fill);
|
|
477
533
|
color: var(--axi-text-dim);
|
|
478
534
|
font: var(--axi-t-label);
|
|
479
535
|
font-size: 12.5px;
|
|
@@ -532,8 +588,12 @@ button.axi-panel--tile:hover,
|
|
|
532
588
|
letter-spacing: var(--axi-ls-label);
|
|
533
589
|
text-transform: uppercase;
|
|
534
590
|
}
|
|
591
|
+
/* A row under the cursor sinks rather than lighting up, so the hover is the
|
|
592
|
+
level's fill - and this popover is filled with --axi-surface-raised, which
|
|
593
|
+
a theme may take translucent, so an opaque row here is the black patch
|
|
594
|
+
--axi-well-fill exists to prevent. Same for the picker's rows below. */
|
|
535
595
|
.axi-select option:hover, .axi-select option:focus {
|
|
536
|
-
background: var(--axi-
|
|
596
|
+
background: var(--axi-well-fill); color: var(--axi-text);
|
|
537
597
|
}
|
|
538
598
|
/* The page-wide focus ring sits 2px outside its element; inside a picker
|
|
539
599
|
that is 2px into the neighbouring row, so pull it back in. */
|
|
@@ -632,7 +692,7 @@ button.axi-panel--tile:hover,
|
|
|
632
692
|
font-weight: 900;
|
|
633
693
|
}
|
|
634
694
|
.axi-picker__opt:hover, .axi-picker__opt:focus {
|
|
635
|
-
background: var(--axi-
|
|
695
|
+
background: var(--axi-well-fill); color: var(--axi-text);
|
|
636
696
|
}
|
|
637
697
|
/* The page-wide focus ring sits 2px outside its element; inside a popover
|
|
638
698
|
this tight that is 2px into the neighbouring row, so pull it back in. */
|
|
@@ -647,7 +707,8 @@ button.axi-panel--tile:hover,
|
|
|
647
707
|
who wants round avatars sets --axi-radius-sm and gets rounded controls
|
|
648
708
|
everywhere - which is the honest version of the request.
|
|
649
709
|
Flat: outlined, no block. An avatar is content inside a panel, the same as
|
|
650
|
-
.axi-stat and .axi-table__rank, not a thing raised off it
|
|
710
|
+
.axi-stat and .axi-table__rank, not a thing raised off it - which is what
|
|
711
|
+
--axi-well-fill below is saying, and all three now say it the same way. */
|
|
651
712
|
.axi-avatar {
|
|
652
713
|
width: var(--axi-avatar-size, 40px);
|
|
653
714
|
height: var(--axi-avatar-size, 40px);
|
|
@@ -655,7 +716,7 @@ button.axi-panel--tile:hover,
|
|
|
655
716
|
display: grid;
|
|
656
717
|
place-items: center;
|
|
657
718
|
overflow: hidden;
|
|
658
|
-
background: var(--axi-
|
|
719
|
+
background: var(--axi-well-fill);
|
|
659
720
|
color: var(--axi-text-dim);
|
|
660
721
|
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
661
722
|
border-radius: var(--axi-radius-sm);
|
package/src/prose.css
CHANGED
|
@@ -28,10 +28,9 @@
|
|
|
28
28
|
|
|
29
29
|
.axi-prose p { margin: 0 0 16px; }
|
|
30
30
|
.axi-prose strong { color: var(--axi-text); font-weight: 800; }
|
|
31
|
-
/*
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
.axi-prose a:hover { color: var(--axi-text); }
|
|
31
|
+
/* A link is drawn beside .axi-code in primitives.css, in one rule with
|
|
32
|
+
`.axi-link`, because a link in an article and a link in interface copy are
|
|
33
|
+
the same object. See the comment there. */
|
|
35
34
|
|
|
36
35
|
.axi-prose ul, .axi-prose ol { margin: 0 0 16px; padding-left: 22px; }
|
|
37
36
|
.axi-prose li { margin: 0 0 7px; }
|
|
@@ -48,9 +47,12 @@
|
|
|
48
47
|
|
|
49
48
|
/* .axi-prose code is declared beside .axi-code in primitives.css: one rule,
|
|
50
49
|
both selectors, because they are one object. */
|
|
50
|
+
/* A code block and a quote are both fields sunk into the prose around them,
|
|
51
|
+
so both take --axi-well-fill rather than the page colour - the reason is
|
|
52
|
+
written out at .axi-stat in src/data.css. */
|
|
51
53
|
.axi-prose pre {
|
|
52
54
|
margin: 0 0 18px; padding: 14px 16px; overflow-x: auto;
|
|
53
|
-
background: var(--axi-
|
|
55
|
+
background: var(--axi-well-fill);
|
|
54
56
|
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
55
57
|
border-radius: var(--axi-radius-sm);
|
|
56
58
|
box-shadow: var(--axi-shadow-control);
|
|
@@ -61,7 +63,7 @@
|
|
|
61
63
|
margin: 0 0 18px;
|
|
62
64
|
padding: 11px 13px;
|
|
63
65
|
border-left: var(--axi-border-panel) solid var(--axi-accent);
|
|
64
|
-
background: var(--axi-
|
|
66
|
+
background: var(--axi-well-fill);
|
|
65
67
|
border-radius: 0 var(--axi-radius-sm) var(--axi-radius-sm) 0;
|
|
66
68
|
}
|
|
67
69
|
.axi-prose blockquote p { margin: 0; font-style: italic; }
|
package/src/shells.css
CHANGED
|
@@ -13,9 +13,18 @@
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
/* ---------- masthead ---------- */
|
|
16
|
+
/* Opaque, because content scrolls under it. Which is also why it paints the
|
|
17
|
+
page's light as well as the page's colour: an opaque strip across the top of
|
|
18
|
+
a lit page is a dark band over that light, and the band is there on every
|
|
19
|
+
page of a site the whole time it is open. Attachment fixed, the same as
|
|
20
|
+
`body`, so the strip's share of the light is the share the page would have
|
|
21
|
+
shown there - a wash positioned to this element's own box instead would
|
|
22
|
+
line up with nothing. */
|
|
16
23
|
.axi-mast {
|
|
17
24
|
position: sticky; top: 0; z-index: 40;
|
|
18
|
-
background: var(--axi-ground);
|
|
25
|
+
background-color: var(--axi-ground);
|
|
26
|
+
background-image: var(--axi-ground-image);
|
|
27
|
+
background-attachment: fixed;
|
|
19
28
|
border-bottom: var(--axi-border-panel) solid var(--axi-ink-line);
|
|
20
29
|
}
|
|
21
30
|
.axi-mast__in {
|
|
@@ -226,6 +235,41 @@
|
|
|
226
235
|
the faint tone, and inside a filled item it follows the item's ink. */
|
|
227
236
|
.axi-rail__item .axi-icon { color: var(--axi-text-faint); }
|
|
228
237
|
.axi-rail__item[aria-current] .axi-icon { color: var(--axi-accent-ink); }
|
|
238
|
+
/* A rail nested inside a panel whose content already spends the accent: a
|
|
239
|
+
metric picker beside the table it drives, a filter list beside its results.
|
|
240
|
+
The language already refuses two fills at two levels inside one rail - that
|
|
241
|
+
is why .axi-rail__subitem has no fill - and a rail that is itself the inner
|
|
242
|
+
level is the same refusal one container further out. Two filled rails on
|
|
243
|
+
one screen name two places, and only one of them is where you are.
|
|
244
|
+
|
|
245
|
+
What it cannot do is borrow the subitem's answer. A subitem is one of a
|
|
246
|
+
handful of leaves under an open category; a picker like this is twenty rows
|
|
247
|
+
and the primary control of its own panel, so brightened text alone loses
|
|
248
|
+
the selection in the list. This is the third weight between the two: the
|
|
249
|
+
row rises off the rail as any hovered row does, and the accent arrives on
|
|
250
|
+
its leading edge. The accent still says which row without the row claiming
|
|
251
|
+
to be the place.
|
|
252
|
+
|
|
253
|
+
The edge is the item's own border, which .axi-rail__item already reserves
|
|
254
|
+
at the control weight and draws transparent - so the bar costs no shadow,
|
|
255
|
+
no extra box and no reflow when it lights up. Logical, not left: this is
|
|
256
|
+
the leading edge, the same correction .axi-rail--flush took.
|
|
257
|
+
|
|
258
|
+
The modifier goes on the list, not on .axi-rail, because a nested picker
|
|
259
|
+
usually has no rail box around it - it sits directly in the panel or well
|
|
260
|
+
that holds it, and inheriting a 208px width and a panel shadow is the
|
|
261
|
+
opposite of what it wants. */
|
|
262
|
+
.axi-rail__nav--quiet .axi-rail__item[aria-current],
|
|
263
|
+
.axi-rail__nav--quiet .axi-rail__item[aria-current]:hover {
|
|
264
|
+
background: var(--axi-surface-raised);
|
|
265
|
+
color: var(--axi-text);
|
|
266
|
+
border-color: transparent;
|
|
267
|
+
border-inline-start-color: var(--axi-accent);
|
|
268
|
+
}
|
|
269
|
+
/* No accent fill here, so the icon has no accent ink to follow - it follows
|
|
270
|
+
the row's own text, the way it does in an unselected item. */
|
|
271
|
+
.axi-rail__nav--quiet .axi-rail__item[aria-current] .axi-icon { color: var(--axi-text); }
|
|
272
|
+
|
|
229
273
|
/* The second level, indented under the item it belongs to. */
|
|
230
274
|
.axi-rail__sub { display: flex; flex-direction: column; margin: 3px 0 5px 10px; }
|
|
231
275
|
.axi-rail__subitem {
|
|
@@ -391,7 +435,8 @@
|
|
|
391
435
|
padding: 7px 8px; border-radius: var(--axi-radius-sm);
|
|
392
436
|
font-size: 13px; font-weight: 600; color: var(--axi-text-dim); cursor: pointer;
|
|
393
437
|
}
|
|
394
|
-
|
|
438
|
+
/* A hovered row sinks - the same fill the select's and the picker's rows use. */
|
|
439
|
+
.axi-menu__pop label:hover { background: var(--axi-well-fill); color: var(--axi-text); }
|
|
395
440
|
.axi-menu__pop input { margin: 3px 0 0; }
|
|
396
441
|
|
|
397
442
|
/* ---------- command palette ---------- */
|
|
@@ -505,7 +550,7 @@
|
|
|
505
550
|
.axi-palette__trigger {
|
|
506
551
|
display: flex; align-items: center; width: 100%; overflow: hidden;
|
|
507
552
|
padding: 0 10px 0 0;
|
|
508
|
-
background: var(--axi-
|
|
553
|
+
background: var(--axi-well-fill);
|
|
509
554
|
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
510
555
|
border-radius: var(--axi-radius-sm);
|
|
511
556
|
color: var(--axi-text-dim);
|
|
@@ -689,7 +734,8 @@
|
|
|
689
734
|
width: 32px; height: 32px;
|
|
690
735
|
display: grid; place-items: center;
|
|
691
736
|
border-radius: var(--axi-radius-sm);
|
|
692
|
-
|
|
737
|
+
/* A control flat on the drawer's head - see --axi-well-fill in tokens. */
|
|
738
|
+
background: var(--axi-well-fill);
|
|
693
739
|
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
694
740
|
color: var(--axi-text-dim);
|
|
695
741
|
font-size: 15px; font-weight: 900; cursor: pointer;
|
|
@@ -755,6 +801,13 @@
|
|
|
755
801
|
overflow-y: auto;
|
|
756
802
|
background-color: var(--axi-ground);
|
|
757
803
|
background-image: var(--axi-ground-image);
|
|
804
|
+
/* Fixed for both of the reasons `body` is: the light stays put while the
|
|
805
|
+
sheet's own content scrolls, and it is positioned to the viewport rather
|
|
806
|
+
than to this box - which matters here because --axi-sheet-top pushes that
|
|
807
|
+
box down under an app's titlebar, and a wash measured from its top edge
|
|
808
|
+
would sit lower than the one on the page it covers. Opening a sheet would
|
|
809
|
+
nudge the light sideways. */
|
|
810
|
+
background-attachment: fixed;
|
|
758
811
|
padding: var(--axi-sheet-pad, 12px 16px);
|
|
759
812
|
}
|
|
760
813
|
/* The heading the sheet opened with, divided from the body by a rule. Rule 8:
|
|
@@ -777,7 +830,8 @@
|
|
|
777
830
|
margin: 0;
|
|
778
831
|
padding: 11px 13px;
|
|
779
832
|
border-left: var(--axi-border-panel) solid var(--axi-accent);
|
|
780
|
-
|
|
833
|
+
/* Sunk into the prose around it, so the well's fill - see .axi-stat. */
|
|
834
|
+
background: var(--axi-well-fill);
|
|
781
835
|
border-radius: 0 var(--axi-radius-sm) var(--axi-radius-sm) 0;
|
|
782
836
|
}
|
|
783
837
|
.axi-quote :where(p) { margin: 0; font-size: 13px; font-style: italic; color: var(--axi-text-dim); line-height: 1.5; }
|
|
@@ -794,13 +848,24 @@
|
|
|
794
848
|
takes the panel outline. It carries no block, because a block is an
|
|
795
849
|
element's shadow on the surface behind it and there is nothing behind a
|
|
796
850
|
window that this language is entitled to draw on.
|
|
851
|
+
|
|
852
|
+
It is the page. Not a page inside one - the only one, for as long as the app
|
|
853
|
+
is running, which is why it paints the ground's IMAGE as well as the
|
|
854
|
+
ground's colour. `body` is behind it and lights nothing, because a window
|
|
855
|
+
covers the viewport and is opaque by the same requirement that makes it
|
|
856
|
+
opaque over its own content. A window that skipped the image would render a
|
|
857
|
+
theme whose character is the light - glass - as flat near-black: every panel
|
|
858
|
+
inside it correctly translucent, over nothing. Longhands for the reason
|
|
859
|
+
.axi-sheet gives at length below; no attachment, because this element's box
|
|
860
|
+
IS the viewport and never scrolls, so fixed and scroll paint the same pixels.
|
|
797
861
|
The titlebar is filled with the ink line rather than a surface: it is the
|
|
798
862
|
outline widened into a strip, which is why the app's content reads as
|
|
799
863
|
sitting inside the outline instead of under a second toolbar. */
|
|
800
864
|
.axi-window {
|
|
801
865
|
height: 100vh;
|
|
802
866
|
display: flex; flex-direction: column; overflow: hidden;
|
|
803
|
-
background: var(--axi-ground);
|
|
867
|
+
background-color: var(--axi-ground);
|
|
868
|
+
background-image: var(--axi-ground-image);
|
|
804
869
|
border: var(--axi-border-panel) solid var(--axi-ink-line);
|
|
805
870
|
border-radius: var(--axi-radius);
|
|
806
871
|
}
|
package/src/tokens.css
CHANGED
|
@@ -80,7 +80,26 @@
|
|
|
80
80
|
against a fill lighter than itself. Every outline in this language is
|
|
81
81
|
near-black on the assumption of a ground the lines can be darker than; a
|
|
82
82
|
well IS at ground level, so an ink edge round one is a black line on a
|
|
83
|
-
black field. Raised takes ink, sunk takes the rule.
|
|
83
|
+
black field. Raised takes ink, sunk takes the rule.
|
|
84
|
+
|
|
85
|
+
And what the fill names is not the .axi-well class - it is the level. A
|
|
86
|
+
button at rest, a field, a select, a checkbox, an avatar, a rank, a code
|
|
87
|
+
span, a hovered row in a popover: not one of them is raised off what holds
|
|
88
|
+
it, and every one of them was spelled --axi-ground, because on a theme
|
|
89
|
+
whose surfaces are opaque "flat on its container" and "the page colour"
|
|
90
|
+
are the same colour. They stop being the same colour the instant a theme
|
|
91
|
+
makes the container translucent, which is how the whole set went black at
|
|
92
|
+
once and why this is written here rather than fourteen times downstream.
|
|
93
|
+
Above this level is --axi-surface (.axi-kbd is the one control in the
|
|
94
|
+
language that is genuinely raised, and says so). Below it is --axi-ground
|
|
95
|
+
proper, which now means the page and only the page: body, the sheet, the
|
|
96
|
+
window, the masthead - the things content scrolls under, which need real
|
|
97
|
+
opacity and not a subtraction. Everything between the two is this token.
|
|
98
|
+
|
|
99
|
+
The LINE does not come along. --axi-well-line is for a well cut into the
|
|
100
|
+
page, where there is nothing left for ink to be darker than; a control is
|
|
101
|
+
cut into a SURFACE and its ink edge still has a fill above it to read
|
|
102
|
+
against. Every component below keeps --axi-ink-line. */
|
|
84
103
|
--axi-well-fill: var(--axi-ground);
|
|
85
104
|
--axi-well-line: var(--axi-rule);
|
|
86
105
|
--axi-text: #f4f6f9;
|