@axiapps/axi-design 1.21.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 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
- .axi-notice p { margin: 0; font: var(--axi-t-small); color: var(--axi-text-dim); }
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;
@@ -1845,8 +1849,14 @@ textarea.axi-input {
1845
1849
  /* `thead th`, not `th`. A row's name is a `<th scope="row">` - that is what
1846
1850
  tells a screen reader which cell labels the row - and it is a name, not a
1847
1851
  column heading: uppercase micro type down the first column would shout every
1848
- row's label at the reader. A row header takes the cell treatment below. */
1849
- .axi-table thead th {
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) {
1850
1860
  padding: 0 10px 10px;
1851
1861
  font: var(--axi-t-micro);
1852
1862
  letter-spacing: var(--axi-ls-micro);
@@ -1854,8 +1864,7 @@ textarea.axi-input {
1854
1864
  color: var(--axi-text-faint);
1855
1865
  border-bottom: var(--axi-border-control) solid var(--axi-rule);
1856
1866
  }
1857
- .axi-table td,
1858
- .axi-table tbody th {
1867
+ .axi-table :where(td, tbody th) {
1859
1868
  padding: 9px 10px;
1860
1869
  font: var(--axi-t-small);
1861
1870
  font-weight: 700;
@@ -2311,3 +2320,59 @@ textarea.axi-input {
2311
2320
  }
2312
2321
 
2313
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axiapps/axi-design",
3
- "version": "1.21.0",
3
+ "version": "1.22.0",
4
4
  "description": "The design language for the axi suite — flat and outlined, dark, drawn in saturated ink.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/data.css CHANGED
@@ -67,8 +67,14 @@
67
67
  /* `thead th`, not `th`. A row's name is a `<th scope="row">` - that is what
68
68
  tells a screen reader which cell labels the row - and it is a name, not a
69
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
- .axi-table thead th {
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) {
72
78
  padding: 0 10px 10px;
73
79
  font: var(--axi-t-micro);
74
80
  letter-spacing: var(--axi-ls-micro);
@@ -76,8 +82,7 @@
76
82
  color: var(--axi-text-faint);
77
83
  border-bottom: var(--axi-border-control) solid var(--axi-rule);
78
84
  }
79
- .axi-table td,
80
- .axi-table tbody th {
85
+ .axi-table :where(td, tbody th) {
81
86
  padding: 9px 10px;
82
87
  font: var(--axi-t-small);
83
88
  font-weight: 700;
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
- .axi-notice p { margin: 0; font: var(--axi-t-small); color: var(--axi-text-dim); }
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); }