@axiapps/axi-design 1.20.0 → 1.22.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 +163 -6
- package/docs/RULES.md +36 -0
- package/package.json +1 -1
- package/src/data.css +100 -3
- package/src/shells.css +7 -3
- package/src/utilities.css +54 -0
package/dist/axi.css
CHANGED
|
@@ -1183,7 +1183,11 @@ textarea.axi-input {
|
|
|
1183
1183
|
font-size: 14px; font-weight: 900;
|
|
1184
1184
|
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
1185
1185
|
}
|
|
1186
|
-
|
|
1186
|
+
/* :where() around the element, here and on the card's and the quote's copy,
|
|
1187
|
+
for the reason spelled out over `.axi-table :where(thead th)` in data.css: a
|
|
1188
|
+
slot that holds a consumer's words must not out-rank the ink the consumer
|
|
1189
|
+
puts on them. */
|
|
1190
|
+
.axi-notice :where(p) { margin: 0; font: var(--axi-t-small); color: var(--axi-text-dim); }
|
|
1187
1191
|
.axi-notice b { color: var(--axi-accent); font-weight: 800; }
|
|
1188
1192
|
|
|
1189
1193
|
/* ---------- toolbar ---------- */
|
|
@@ -1452,7 +1456,7 @@ textarea.axi-input {
|
|
|
1452
1456
|
color: var(--axi-text-faint);
|
|
1453
1457
|
letter-spacing: .08em; text-transform: uppercase;
|
|
1454
1458
|
}
|
|
1455
|
-
.axi-card p { margin: 0; font: var(--axi-t-small); color: var(--axi-text-dim); }
|
|
1459
|
+
.axi-card :where(p) { margin: 0; font: var(--axi-t-small); color: var(--axi-text-dim); }
|
|
1456
1460
|
/* Pushed to the bottom so that in an equalised grid row every card's meta
|
|
1457
1461
|
strip lands on the same line and the rules read as continuous across the
|
|
1458
1462
|
grid. The cost is empty space on cards with less content. */
|
|
@@ -1515,7 +1519,7 @@ textarea.axi-input {
|
|
|
1515
1519
|
background: var(--axi-ground);
|
|
1516
1520
|
border-radius: 0 var(--axi-radius-sm) var(--axi-radius-sm) 0;
|
|
1517
1521
|
}
|
|
1518
|
-
.axi-quote p { margin: 0; font-size: 13px; font-style: italic; color: var(--axi-text-dim); line-height: 1.5; }
|
|
1522
|
+
.axi-quote :where(p) { margin: 0; font-size: 13px; font-style: italic; color: var(--axi-text-dim); line-height: 1.5; }
|
|
1519
1523
|
.axi-quote cite {
|
|
1520
1524
|
display: block; margin-top: 7px;
|
|
1521
1525
|
font-style: normal; font-size: 11.5px; font-weight: 700;
|
|
@@ -1842,7 +1846,17 @@ textarea.axi-input {
|
|
|
1842
1846
|
.axi-table td { text-align: right; white-space: nowrap; }
|
|
1843
1847
|
.axi-table th:first-child,
|
|
1844
1848
|
.axi-table td:first-child { text-align: left; }
|
|
1845
|
-
|
|
1849
|
+
/* `thead th`, not `th`. A row's name is a `<th scope="row">` - that is what
|
|
1850
|
+
tells a screen reader which cell labels the row - and it is a name, not a
|
|
1851
|
+
column heading: uppercase micro type down the first column would shout every
|
|
1852
|
+
row's label at the reader. A row header takes the cell treatment below.
|
|
1853
|
+
|
|
1854
|
+
The element part is wrapped in :where() so this rule weighs one class, not a
|
|
1855
|
+
class and a type. A base rule that out-ranks the component's own modifiers is
|
|
1856
|
+
a base rule nothing can correct: at (0,1,1) this beat `.axi-table__num` and
|
|
1857
|
+
every ink class a consumer could put on a cell, so the column that carries
|
|
1858
|
+
the measured value was silently still the dim one. */
|
|
1859
|
+
.axi-table :where(thead th) {
|
|
1846
1860
|
padding: 0 10px 10px;
|
|
1847
1861
|
font: var(--axi-t-micro);
|
|
1848
1862
|
letter-spacing: var(--axi-ls-micro);
|
|
@@ -1850,7 +1864,7 @@ textarea.axi-input {
|
|
|
1850
1864
|
color: var(--axi-text-faint);
|
|
1851
1865
|
border-bottom: var(--axi-border-control) solid var(--axi-rule);
|
|
1852
1866
|
}
|
|
1853
|
-
.axi-table td {
|
|
1867
|
+
.axi-table :where(td, tbody th) {
|
|
1854
1868
|
padding: 9px 10px;
|
|
1855
1869
|
font: var(--axi-t-small);
|
|
1856
1870
|
font-weight: 700;
|
|
@@ -1861,7 +1875,7 @@ textarea.axi-input {
|
|
|
1861
1875
|
hovering a row is not a status, and forty rows that each flash a colour on
|
|
1862
1876
|
the way past the one you want is the tinted-everything failure rule 2 is
|
|
1863
1877
|
about. */
|
|
1864
|
-
.axi-table tbody tr:hover td { background: var(--axi-surface-raised); color: var(--axi-text); }
|
|
1878
|
+
.axi-table tbody tr:hover :is(td, th) { background: var(--axi-surface-raised); color: var(--axi-text); }
|
|
1865
1879
|
/* The measured value in a row, as opposed to its supporting numbers. */
|
|
1866
1880
|
.axi-table__num { color: var(--axi-text); }
|
|
1867
1881
|
/* A name cell: an icon, a diamond or a rank beside the label. */
|
|
@@ -1879,6 +1893,93 @@ textarea.axi-input {
|
|
|
1879
1893
|
}
|
|
1880
1894
|
.axi-table__rank--top { background: var(--axi-accent); color: var(--axi-accent-ink); }
|
|
1881
1895
|
|
|
1896
|
+
/* A table too wide and too tall for the panel holding it: twenty numeric
|
|
1897
|
+
columns against forty players, scrolled in both directions. Rule 8 still
|
|
1898
|
+
decides what every line means - the head is a lid at the control step, the
|
|
1899
|
+
rows are hairlines - and the only new thing here is that the lid and the
|
|
1900
|
+
row's name have to stay put while the numbers move under them.
|
|
1901
|
+
|
|
1902
|
+
`border-collapse: separate` is load-bearing, not housekeeping. A collapsed
|
|
1903
|
+
table hoists its cell borders onto the table itself, and a border owned by
|
|
1904
|
+
the table scrolls away with it, so a sticky head drawn under `collapse`
|
|
1905
|
+
loses the very line that makes it a lid. Separate borders stay with the
|
|
1906
|
+
cell that declared them. Each modifier sets it, so either works alone.
|
|
1907
|
+
|
|
1908
|
+
Note what is NOT here. No block: an edge that has to hold under scrolling
|
|
1909
|
+
is a border, and a block in this language is an offset rectangle, which is
|
|
1910
|
+
not the shape of the top of a column. No scrolled-under state either - the
|
|
1911
|
+
head's edge is drawn whether anything has moved beneath it or not, so there
|
|
1912
|
+
is nothing for scrolling to reveal. */
|
|
1913
|
+
/* The isolation is the reason the three z-indexes below can be 1, 2 and 3:
|
|
1914
|
+
they are resolved in here and cannot compete with the page stack. See the
|
|
1915
|
+
layer table in docs/RULES.md. */
|
|
1916
|
+
.axi-table__scroll { overflow: auto; isolation: isolate; }
|
|
1917
|
+
/* The head stays. */
|
|
1918
|
+
.axi-table--sticky { border-collapse: separate; border-spacing: 0; }
|
|
1919
|
+
.axi-table--sticky thead th {
|
|
1920
|
+
position: sticky;
|
|
1921
|
+
top: 0;
|
|
1922
|
+
z-index: 2;
|
|
1923
|
+
/* The float surface, not the raised one, and this is the whole reason
|
|
1924
|
+
--axi-surface-float exists: rows travel behind both of these, and a glass
|
|
1925
|
+
theme's ordinary surfaces are alpha, so a head at --axi-surface-raised
|
|
1926
|
+
would have numbers sliding through it. Opaque is not a look here, it is
|
|
1927
|
+
the requirement. */
|
|
1928
|
+
background: var(--axi-surface-float);
|
|
1929
|
+
border-bottom: var(--axi-border-control) solid var(--axi-ink-line);
|
|
1930
|
+
}
|
|
1931
|
+
/* The first column stays. It holds the row's name, and a name scrolled out of
|
|
1932
|
+
sight makes every number left of it anonymous - which is the whole reason a
|
|
1933
|
+
wide table is readable at all. Its edge is the cut the head carries, turned
|
|
1934
|
+
ninety degrees. */
|
|
1935
|
+
.axi-table--pinned { border-collapse: separate; border-spacing: 0; }
|
|
1936
|
+
.axi-table--pinned :is(thead, tbody) :is(th, td):first-child {
|
|
1937
|
+
position: sticky;
|
|
1938
|
+
left: 0;
|
|
1939
|
+
z-index: 1;
|
|
1940
|
+
background: var(--axi-surface-float);
|
|
1941
|
+
border-right: var(--axi-border-control) solid var(--axi-ink-line);
|
|
1942
|
+
}
|
|
1943
|
+
/* The corner belongs to both and has to outrank both. */
|
|
1944
|
+
.axi-table--sticky.axi-table--pinned thead th:first-child { z-index: 3; }
|
|
1945
|
+
/* Twenty columns do not get a leaderboard's padding. The type steps down with
|
|
1946
|
+
them; everything else about the table is unchanged, which is the point of
|
|
1947
|
+
its being a modifier rather than a second table. */
|
|
1948
|
+
.axi-table--dense thead th { padding: 0 7px 6px; }
|
|
1949
|
+
.axi-table--dense :is(td, tbody th) { padding: 5px 7px; font: var(--axi-t-micro); }
|
|
1950
|
+
/* The sorted column, named with the attribute a screen reader already needs
|
|
1951
|
+
in order to announce it. A class beside `aria-sort` would be a second
|
|
1952
|
+
source of truth for one fact. The mark is ink on the head and its edge -
|
|
1953
|
+
not a fill down the column, which is rule 2's tinted-everything failure
|
|
1954
|
+
applied to the one column you are trying to read. */
|
|
1955
|
+
.axi-table th[aria-sort] {
|
|
1956
|
+
color: var(--axi-accent);
|
|
1957
|
+
border-bottom-color: var(--axi-accent);
|
|
1958
|
+
}
|
|
1959
|
+
/* CSS cannot reach "the cells under the sorted head", so the consumer marks
|
|
1960
|
+
them. Full-strength text against the column's dimmed neighbours. */
|
|
1961
|
+
.axi-table__cell--sorted { color: var(--axi-text); }
|
|
1962
|
+
/* A sortable heading is a button, because sorting is an action and a <th> you
|
|
1963
|
+
can click but not tab to is a column the keyboard cannot sort. It inherits
|
|
1964
|
+
everything and adds nothing, so a heading looks the same whether it sorts or
|
|
1965
|
+
not - the only thing that marks a sorted column is aria-sort above. */
|
|
1966
|
+
.axi-table__sort {
|
|
1967
|
+
display: flex;
|
|
1968
|
+
align-items: center;
|
|
1969
|
+
justify-content: flex-end;
|
|
1970
|
+
gap: 6px;
|
|
1971
|
+
width: 100%;
|
|
1972
|
+
padding: 0;
|
|
1973
|
+
background: none;
|
|
1974
|
+
border: 0;
|
|
1975
|
+
font: inherit;
|
|
1976
|
+
color: inherit;
|
|
1977
|
+
letter-spacing: inherit;
|
|
1978
|
+
text-transform: inherit;
|
|
1979
|
+
cursor: pointer;
|
|
1980
|
+
}
|
|
1981
|
+
.axi-table th:first-child .axi-table__sort { justify-content: flex-start; }
|
|
1982
|
+
|
|
1882
1983
|
/* ---------- meter ---------- */
|
|
1883
1984
|
/* Rule 9: a proportion is a length. The track is the ground, the fill is the
|
|
1884
1985
|
value, and the fill is one ink at full strength - a tinted or faded bar is
|
|
@@ -2219,3 +2320,59 @@ textarea.axi-input {
|
|
|
2219
2320
|
}
|
|
2220
2321
|
|
|
2221
2322
|
.axi-prose img { max-width: 100%; height: auto; border-radius: var(--axi-radius-sm); }
|
|
2323
|
+
|
|
2324
|
+
/* --- utilities.css --- */
|
|
2325
|
+
/* axi design language - utilities.
|
|
2326
|
+
Two families of single-purpose class, and the file is last in the cascade
|
|
2327
|
+
because that is the whole point of it: a utility has to be able to overrule
|
|
2328
|
+
the component it sits inside, and it is not allowed to buy that with
|
|
2329
|
+
!important or with a padded selector.
|
|
2330
|
+
|
|
2331
|
+
Why the language needs them at all. Rules 5 and 6 draw a distinction the
|
|
2332
|
+
reader is expected to make at a glance - a filled status against a cool ink
|
|
2333
|
+
that is commentary - and until now the only way to say either was to be a
|
|
2334
|
+
component that said it for you. Real markup is full of places that are not
|
|
2335
|
+
components: a number in a sentence, a word in a legend, a value beside a
|
|
2336
|
+
label. An app with no way to say "this reading is bad" in those places will
|
|
2337
|
+
reach for a colour literal, and at that moment the theme stops reaching it.
|
|
2338
|
+
So the language names the meanings, and the app spends them by name.
|
|
2339
|
+
|
|
2340
|
+
What they deliberately are not. An ink sets colour and nothing else - no
|
|
2341
|
+
size, no weight, no spacing, no surface - and an edge sets border-colour
|
|
2342
|
+
without asserting that there is a border. Neither is a shorthand for a
|
|
2343
|
+
component: a status that belongs to a whole object still wants the chip or
|
|
2344
|
+
the cap that rule 5 describes, because an object's verdict should be a shape
|
|
2345
|
+
the eye finds, not a recoloured word inside it. These are for the marks
|
|
2346
|
+
inside those shapes.
|
|
2347
|
+
|
|
2348
|
+
And there is no fill family. Under rule 2 colour at partial opacity over the
|
|
2349
|
+
ground is not available, and an opaque status fill behind arbitrary text is a
|
|
2350
|
+
chip - which the language already ships. A surface stays a surface token. */
|
|
2351
|
+
|
|
2352
|
+
/* The five meanings. Ordered as rule 5 orders them: the three statuses, then
|
|
2353
|
+
the cool ink that is not a status, then the accent - which means "the thing
|
|
2354
|
+
you picked" and never "good". */
|
|
2355
|
+
.axi-ink-ok { color: var(--axi-ok); }
|
|
2356
|
+
.axi-ink-warn { color: var(--axi-warn); }
|
|
2357
|
+
.axi-ink-danger { color: var(--axi-danger); }
|
|
2358
|
+
.axi-ink-meta { color: var(--axi-meta); }
|
|
2359
|
+
.axi-ink-accent { color: var(--axi-accent); }
|
|
2360
|
+
|
|
2361
|
+
/* The three neutral steps. `plain` rather than `text` so that reading the class
|
|
2362
|
+
list does not suggest it does something to the text other than ink it, and so
|
|
2363
|
+
the three read as one scale. */
|
|
2364
|
+
.axi-ink-plain { color: var(--axi-text); }
|
|
2365
|
+
.axi-ink-dim { color: var(--axi-text-dim); }
|
|
2366
|
+
.axi-ink-faint { color: var(--axi-text-faint); }
|
|
2367
|
+
|
|
2368
|
+
/* Edges. `rule` and `line` are the two neutral weights of meaning, not of
|
|
2369
|
+
width: --axi-rule is the quiet separator inside a surface, --axi-ink-line the
|
|
2370
|
+
full ink a raised thing is outlined in. Width stays with whoever drew the
|
|
2371
|
+
border. */
|
|
2372
|
+
.axi-edge-ok { border-color: var(--axi-ok); }
|
|
2373
|
+
.axi-edge-warn { border-color: var(--axi-warn); }
|
|
2374
|
+
.axi-edge-danger { border-color: var(--axi-danger); }
|
|
2375
|
+
.axi-edge-meta { border-color: var(--axi-meta); }
|
|
2376
|
+
.axi-edge-accent { border-color: var(--axi-accent); }
|
|
2377
|
+
.axi-edge-rule { border-color: var(--axi-rule); }
|
|
2378
|
+
.axi-edge-line { border-color: var(--axi-ink-line); }
|
package/docs/RULES.md
CHANGED
|
@@ -243,6 +243,30 @@ see is a slug floating in the card.
|
|
|
243
243
|
meaning. It is the only ink guaranteed not to mean "how bad is this" — which is
|
|
244
244
|
what makes it readable as commentary at a glance.
|
|
245
245
|
|
|
246
|
+
### Saying a meaning outside a component
|
|
247
|
+
|
|
248
|
+
Rules 5 and 6 are about marks, not only about components. A number inside a
|
|
249
|
+
sentence, a word in a legend, a value beside its label — each of these can be a
|
|
250
|
+
status or can be commentary, and a reader is entitled to see which. So the five
|
|
251
|
+
meanings are available as inks: `.axi-ink-ok`, `.axi-ink-warn`,
|
|
252
|
+
`.axi-ink-danger`, `.axi-ink-meta`, `.axi-ink-accent`, with the three neutral
|
|
253
|
+
steps as `.axi-ink-plain`, `.axi-ink-dim` and `.axi-ink-faint`, and the same
|
|
254
|
+
list again as `.axi-edge-*` for a border's colour.
|
|
255
|
+
|
|
256
|
+
An ink colours a mark. It is not a way to skip the chip or the status cap, which
|
|
257
|
+
are what rule 5 asks for when the verdict belongs to a whole object: a shape the
|
|
258
|
+
eye finds beats a recoloured word inside a paragraph. Reach for an ink when
|
|
259
|
+
there is no object, only a mark.
|
|
260
|
+
|
|
261
|
+
They live in `src/utilities.css`, last in the cascade, which is how a single
|
|
262
|
+
class overrules the component it sits inside without `!important`. That places
|
|
263
|
+
an obligation on components in the other direction: **a component's base rules
|
|
264
|
+
may not out-rank its own modifiers.** A rule written `.axi-table td` weighs a
|
|
265
|
+
class and a type, so it silently defeats both `.axi-table__num` and any ink a
|
|
266
|
+
consumer puts on the cell — the fix is `:where()` around the element, which
|
|
267
|
+
spends no specificity. Where a component holds a consumer's content, wrap the
|
|
268
|
+
element part.
|
|
269
|
+
|
|
246
270
|
## 7. The diamond is the family motif
|
|
247
271
|
|
|
248
272
|
A 45°-rotated outlined square. Bullet, status dot, language marker, and scaled
|
|
@@ -461,6 +485,9 @@ instead of joining it.
|
|
|
461
485
|
|
|
462
486
|
| Layer | z-index | What sits here |
|
|
463
487
|
|---|---|---|
|
|
488
|
+
| Pinned column | 1 | `.axi-table--pinned`'s first column |
|
|
489
|
+
| Sticky head | 2 | `.axi-table--sticky`'s `thead th` |
|
|
490
|
+
| Table corner | 3 | where the two cross |
|
|
464
491
|
| Sticky chrome | 40 | `.axi-mast` |
|
|
465
492
|
| Popovers | 41 | `.axi-menu__pop`, `.axi-picker__pop` |
|
|
466
493
|
| Scrim | 50 | `.axi-scrim` |
|
|
@@ -473,6 +500,15 @@ The modal has no number on purpose. A `<dialog>` opened with `showModal()` is
|
|
|
473
500
|
promoted to the browser's top layer, which sits above every `z-index` there is;
|
|
474
501
|
writing a number in that row would describe a competition the modal is not in.
|
|
475
502
|
|
|
503
|
+
The first three are sealed rather than low. `.axi-table__scroll` sets
|
|
504
|
+
`isolation: isolate`, so those numbers are resolved inside the scroll container
|
|
505
|
+
and never compete with the page layers under them - a table's sticky head
|
|
506
|
+
cannot climb over a mast, and a table inside a drawer cannot reach out of it.
|
|
507
|
+
They are listed because a table needs three orderings among its own cells and a
|
|
508
|
+
number that is never written down is a number that drifts; the isolation is
|
|
509
|
+
what keeps 1, 2 and 3 from meaning anything outside the table that declares
|
|
510
|
+
them.
|
|
511
|
+
|
|
476
512
|
A negative `z-index` inside a component's own `isolation` context — the sigil's
|
|
477
513
|
backing shape — is not a layer and is not listed. It is invisible outside the
|
|
478
514
|
component that owns it.
|
package/package.json
CHANGED
package/src/data.css
CHANGED
|
@@ -64,7 +64,17 @@
|
|
|
64
64
|
.axi-table td { text-align: right; white-space: nowrap; }
|
|
65
65
|
.axi-table th:first-child,
|
|
66
66
|
.axi-table td:first-child { text-align: left; }
|
|
67
|
-
|
|
67
|
+
/* `thead th`, not `th`. A row's name is a `<th scope="row">` - that is what
|
|
68
|
+
tells a screen reader which cell labels the row - and it is a name, not a
|
|
69
|
+
column heading: uppercase micro type down the first column would shout every
|
|
70
|
+
row's label at the reader. A row header takes the cell treatment below.
|
|
71
|
+
|
|
72
|
+
The element part is wrapped in :where() so this rule weighs one class, not a
|
|
73
|
+
class and a type. A base rule that out-ranks the component's own modifiers is
|
|
74
|
+
a base rule nothing can correct: at (0,1,1) this beat `.axi-table__num` and
|
|
75
|
+
every ink class a consumer could put on a cell, so the column that carries
|
|
76
|
+
the measured value was silently still the dim one. */
|
|
77
|
+
.axi-table :where(thead th) {
|
|
68
78
|
padding: 0 10px 10px;
|
|
69
79
|
font: var(--axi-t-micro);
|
|
70
80
|
letter-spacing: var(--axi-ls-micro);
|
|
@@ -72,7 +82,7 @@
|
|
|
72
82
|
color: var(--axi-text-faint);
|
|
73
83
|
border-bottom: var(--axi-border-control) solid var(--axi-rule);
|
|
74
84
|
}
|
|
75
|
-
.axi-table td {
|
|
85
|
+
.axi-table :where(td, tbody th) {
|
|
76
86
|
padding: 9px 10px;
|
|
77
87
|
font: var(--axi-t-small);
|
|
78
88
|
font-weight: 700;
|
|
@@ -83,7 +93,7 @@
|
|
|
83
93
|
hovering a row is not a status, and forty rows that each flash a colour on
|
|
84
94
|
the way past the one you want is the tinted-everything failure rule 2 is
|
|
85
95
|
about. */
|
|
86
|
-
.axi-table tbody tr:hover td { background: var(--axi-surface-raised); color: var(--axi-text); }
|
|
96
|
+
.axi-table tbody tr:hover :is(td, th) { background: var(--axi-surface-raised); color: var(--axi-text); }
|
|
87
97
|
/* The measured value in a row, as opposed to its supporting numbers. */
|
|
88
98
|
.axi-table__num { color: var(--axi-text); }
|
|
89
99
|
/* A name cell: an icon, a diamond or a rank beside the label. */
|
|
@@ -101,6 +111,93 @@
|
|
|
101
111
|
}
|
|
102
112
|
.axi-table__rank--top { background: var(--axi-accent); color: var(--axi-accent-ink); }
|
|
103
113
|
|
|
114
|
+
/* A table too wide and too tall for the panel holding it: twenty numeric
|
|
115
|
+
columns against forty players, scrolled in both directions. Rule 8 still
|
|
116
|
+
decides what every line means - the head is a lid at the control step, the
|
|
117
|
+
rows are hairlines - and the only new thing here is that the lid and the
|
|
118
|
+
row's name have to stay put while the numbers move under them.
|
|
119
|
+
|
|
120
|
+
`border-collapse: separate` is load-bearing, not housekeeping. A collapsed
|
|
121
|
+
table hoists its cell borders onto the table itself, and a border owned by
|
|
122
|
+
the table scrolls away with it, so a sticky head drawn under `collapse`
|
|
123
|
+
loses the very line that makes it a lid. Separate borders stay with the
|
|
124
|
+
cell that declared them. Each modifier sets it, so either works alone.
|
|
125
|
+
|
|
126
|
+
Note what is NOT here. No block: an edge that has to hold under scrolling
|
|
127
|
+
is a border, and a block in this language is an offset rectangle, which is
|
|
128
|
+
not the shape of the top of a column. No scrolled-under state either - the
|
|
129
|
+
head's edge is drawn whether anything has moved beneath it or not, so there
|
|
130
|
+
is nothing for scrolling to reveal. */
|
|
131
|
+
/* The isolation is the reason the three z-indexes below can be 1, 2 and 3:
|
|
132
|
+
they are resolved in here and cannot compete with the page stack. See the
|
|
133
|
+
layer table in docs/RULES.md. */
|
|
134
|
+
.axi-table__scroll { overflow: auto; isolation: isolate; }
|
|
135
|
+
/* The head stays. */
|
|
136
|
+
.axi-table--sticky { border-collapse: separate; border-spacing: 0; }
|
|
137
|
+
.axi-table--sticky thead th {
|
|
138
|
+
position: sticky;
|
|
139
|
+
top: 0;
|
|
140
|
+
z-index: 2;
|
|
141
|
+
/* The float surface, not the raised one, and this is the whole reason
|
|
142
|
+
--axi-surface-float exists: rows travel behind both of these, and a glass
|
|
143
|
+
theme's ordinary surfaces are alpha, so a head at --axi-surface-raised
|
|
144
|
+
would have numbers sliding through it. Opaque is not a look here, it is
|
|
145
|
+
the requirement. */
|
|
146
|
+
background: var(--axi-surface-float);
|
|
147
|
+
border-bottom: var(--axi-border-control) solid var(--axi-ink-line);
|
|
148
|
+
}
|
|
149
|
+
/* The first column stays. It holds the row's name, and a name scrolled out of
|
|
150
|
+
sight makes every number left of it anonymous - which is the whole reason a
|
|
151
|
+
wide table is readable at all. Its edge is the cut the head carries, turned
|
|
152
|
+
ninety degrees. */
|
|
153
|
+
.axi-table--pinned { border-collapse: separate; border-spacing: 0; }
|
|
154
|
+
.axi-table--pinned :is(thead, tbody) :is(th, td):first-child {
|
|
155
|
+
position: sticky;
|
|
156
|
+
left: 0;
|
|
157
|
+
z-index: 1;
|
|
158
|
+
background: var(--axi-surface-float);
|
|
159
|
+
border-right: var(--axi-border-control) solid var(--axi-ink-line);
|
|
160
|
+
}
|
|
161
|
+
/* The corner belongs to both and has to outrank both. */
|
|
162
|
+
.axi-table--sticky.axi-table--pinned thead th:first-child { z-index: 3; }
|
|
163
|
+
/* Twenty columns do not get a leaderboard's padding. The type steps down with
|
|
164
|
+
them; everything else about the table is unchanged, which is the point of
|
|
165
|
+
its being a modifier rather than a second table. */
|
|
166
|
+
.axi-table--dense thead th { padding: 0 7px 6px; }
|
|
167
|
+
.axi-table--dense :is(td, tbody th) { padding: 5px 7px; font: var(--axi-t-micro); }
|
|
168
|
+
/* The sorted column, named with the attribute a screen reader already needs
|
|
169
|
+
in order to announce it. A class beside `aria-sort` would be a second
|
|
170
|
+
source of truth for one fact. The mark is ink on the head and its edge -
|
|
171
|
+
not a fill down the column, which is rule 2's tinted-everything failure
|
|
172
|
+
applied to the one column you are trying to read. */
|
|
173
|
+
.axi-table th[aria-sort] {
|
|
174
|
+
color: var(--axi-accent);
|
|
175
|
+
border-bottom-color: var(--axi-accent);
|
|
176
|
+
}
|
|
177
|
+
/* CSS cannot reach "the cells under the sorted head", so the consumer marks
|
|
178
|
+
them. Full-strength text against the column's dimmed neighbours. */
|
|
179
|
+
.axi-table__cell--sorted { color: var(--axi-text); }
|
|
180
|
+
/* A sortable heading is a button, because sorting is an action and a <th> you
|
|
181
|
+
can click but not tab to is a column the keyboard cannot sort. It inherits
|
|
182
|
+
everything and adds nothing, so a heading looks the same whether it sorts or
|
|
183
|
+
not - the only thing that marks a sorted column is aria-sort above. */
|
|
184
|
+
.axi-table__sort {
|
|
185
|
+
display: flex;
|
|
186
|
+
align-items: center;
|
|
187
|
+
justify-content: flex-end;
|
|
188
|
+
gap: 6px;
|
|
189
|
+
width: 100%;
|
|
190
|
+
padding: 0;
|
|
191
|
+
background: none;
|
|
192
|
+
border: 0;
|
|
193
|
+
font: inherit;
|
|
194
|
+
color: inherit;
|
|
195
|
+
letter-spacing: inherit;
|
|
196
|
+
text-transform: inherit;
|
|
197
|
+
cursor: pointer;
|
|
198
|
+
}
|
|
199
|
+
.axi-table th:first-child .axi-table__sort { justify-content: flex-start; }
|
|
200
|
+
|
|
104
201
|
/* ---------- meter ---------- */
|
|
105
202
|
/* Rule 9: a proportion is a length. The track is the ground, the fill is the
|
|
106
203
|
value, and the fill is one ink at full strength - a tinted or faded bar is
|
package/src/shells.css
CHANGED
|
@@ -264,7 +264,11 @@
|
|
|
264
264
|
font-size: 14px; font-weight: 900;
|
|
265
265
|
border: var(--axi-border-control) solid var(--axi-ink-line);
|
|
266
266
|
}
|
|
267
|
-
|
|
267
|
+
/* :where() around the element, here and on the card's and the quote's copy,
|
|
268
|
+
for the reason spelled out over `.axi-table :where(thead th)` in data.css: a
|
|
269
|
+
slot that holds a consumer's words must not out-rank the ink the consumer
|
|
270
|
+
puts on them. */
|
|
271
|
+
.axi-notice :where(p) { margin: 0; font: var(--axi-t-small); color: var(--axi-text-dim); }
|
|
268
272
|
.axi-notice b { color: var(--axi-accent); font-weight: 800; }
|
|
269
273
|
|
|
270
274
|
/* ---------- toolbar ---------- */
|
|
@@ -533,7 +537,7 @@
|
|
|
533
537
|
color: var(--axi-text-faint);
|
|
534
538
|
letter-spacing: .08em; text-transform: uppercase;
|
|
535
539
|
}
|
|
536
|
-
.axi-card p { margin: 0; font: var(--axi-t-small); color: var(--axi-text-dim); }
|
|
540
|
+
.axi-card :where(p) { margin: 0; font: var(--axi-t-small); color: var(--axi-text-dim); }
|
|
537
541
|
/* Pushed to the bottom so that in an equalised grid row every card's meta
|
|
538
542
|
strip lands on the same line and the rules read as continuous across the
|
|
539
543
|
grid. The cost is empty space on cards with less content. */
|
|
@@ -596,7 +600,7 @@
|
|
|
596
600
|
background: var(--axi-ground);
|
|
597
601
|
border-radius: 0 var(--axi-radius-sm) var(--axi-radius-sm) 0;
|
|
598
602
|
}
|
|
599
|
-
.axi-quote p { margin: 0; font-size: 13px; font-style: italic; color: var(--axi-text-dim); line-height: 1.5; }
|
|
603
|
+
.axi-quote :where(p) { margin: 0; font-size: 13px; font-style: italic; color: var(--axi-text-dim); line-height: 1.5; }
|
|
600
604
|
.axi-quote cite {
|
|
601
605
|
display: block; margin-top: 7px;
|
|
602
606
|
font-style: normal; font-size: 11.5px; font-weight: 700;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/* axi design language - utilities.
|
|
2
|
+
Two families of single-purpose class, and the file is last in the cascade
|
|
3
|
+
because that is the whole point of it: a utility has to be able to overrule
|
|
4
|
+
the component it sits inside, and it is not allowed to buy that with
|
|
5
|
+
!important or with a padded selector.
|
|
6
|
+
|
|
7
|
+
Why the language needs them at all. Rules 5 and 6 draw a distinction the
|
|
8
|
+
reader is expected to make at a glance - a filled status against a cool ink
|
|
9
|
+
that is commentary - and until now the only way to say either was to be a
|
|
10
|
+
component that said it for you. Real markup is full of places that are not
|
|
11
|
+
components: a number in a sentence, a word in a legend, a value beside a
|
|
12
|
+
label. An app with no way to say "this reading is bad" in those places will
|
|
13
|
+
reach for a colour literal, and at that moment the theme stops reaching it.
|
|
14
|
+
So the language names the meanings, and the app spends them by name.
|
|
15
|
+
|
|
16
|
+
What they deliberately are not. An ink sets colour and nothing else - no
|
|
17
|
+
size, no weight, no spacing, no surface - and an edge sets border-colour
|
|
18
|
+
without asserting that there is a border. Neither is a shorthand for a
|
|
19
|
+
component: a status that belongs to a whole object still wants the chip or
|
|
20
|
+
the cap that rule 5 describes, because an object's verdict should be a shape
|
|
21
|
+
the eye finds, not a recoloured word inside it. These are for the marks
|
|
22
|
+
inside those shapes.
|
|
23
|
+
|
|
24
|
+
And there is no fill family. Under rule 2 colour at partial opacity over the
|
|
25
|
+
ground is not available, and an opaque status fill behind arbitrary text is a
|
|
26
|
+
chip - which the language already ships. A surface stays a surface token. */
|
|
27
|
+
|
|
28
|
+
/* The five meanings. Ordered as rule 5 orders them: the three statuses, then
|
|
29
|
+
the cool ink that is not a status, then the accent - which means "the thing
|
|
30
|
+
you picked" and never "good". */
|
|
31
|
+
.axi-ink-ok { color: var(--axi-ok); }
|
|
32
|
+
.axi-ink-warn { color: var(--axi-warn); }
|
|
33
|
+
.axi-ink-danger { color: var(--axi-danger); }
|
|
34
|
+
.axi-ink-meta { color: var(--axi-meta); }
|
|
35
|
+
.axi-ink-accent { color: var(--axi-accent); }
|
|
36
|
+
|
|
37
|
+
/* The three neutral steps. `plain` rather than `text` so that reading the class
|
|
38
|
+
list does not suggest it does something to the text other than ink it, and so
|
|
39
|
+
the three read as one scale. */
|
|
40
|
+
.axi-ink-plain { color: var(--axi-text); }
|
|
41
|
+
.axi-ink-dim { color: var(--axi-text-dim); }
|
|
42
|
+
.axi-ink-faint { color: var(--axi-text-faint); }
|
|
43
|
+
|
|
44
|
+
/* Edges. `rule` and `line` are the two neutral weights of meaning, not of
|
|
45
|
+
width: --axi-rule is the quiet separator inside a surface, --axi-ink-line the
|
|
46
|
+
full ink a raised thing is outlined in. Width stays with whoever drew the
|
|
47
|
+
border. */
|
|
48
|
+
.axi-edge-ok { border-color: var(--axi-ok); }
|
|
49
|
+
.axi-edge-warn { border-color: var(--axi-warn); }
|
|
50
|
+
.axi-edge-danger { border-color: var(--axi-danger); }
|
|
51
|
+
.axi-edge-meta { border-color: var(--axi-meta); }
|
|
52
|
+
.axi-edge-accent { border-color: var(--axi-accent); }
|
|
53
|
+
.axi-edge-rule { border-color: var(--axi-rule); }
|
|
54
|
+
.axi-edge-line { border-color: var(--axi-ink-line); }
|