@axiapps/axi-design 1.6.0 → 1.7.1

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/accents.json ADDED
@@ -0,0 +1,13 @@
1
+ [
2
+ { "id": "axi-gold", "label": "Axi Gold", "hex": "#ffc53d" },
3
+ { "id": "electric-blue", "label": "Electric Blue", "hex": "#3b82f6" },
4
+ { "id": "refined-cyan", "label": "Refined Cyan", "hex": "#5eadd5" },
5
+ { "id": "amber-warm", "label": "Amber Warm", "hex": "#f59e0b" },
6
+ { "id": "emerald-mint", "label": "Emerald Mint", "hex": "#34d399" },
7
+ { "id": "rose-pink", "label": "Rose Pink", "hex": "#f43f5e" },
8
+ { "id": "violet-purple", "label": "Violet Purple", "hex": "#8b5cf6" },
9
+ { "id": "crimson-red", "label": "Crimson Red", "hex": "#ef4444" },
10
+ { "id": "slate-silver", "label": "Slate Silver", "hex": "#94a3b8" },
11
+ { "id": "teal-ocean", "label": "Teal Ocean", "hex": "#14b8a6" },
12
+ { "id": "gold-bronze", "label": "Gold Bronze", "hex": "#d4a017" }
13
+ ]
@@ -0,0 +1,14 @@
1
+ /* axi design language - generated by scripts/build.mjs.
2
+ Edit the files in src/ and run `npm run build`; do not edit this file. */
3
+
4
+ [data-axi-accent="axi-gold"] { --axi-accent: #ffc53d; }
5
+ [data-axi-accent="electric-blue"] { --axi-accent: #3b82f6; }
6
+ [data-axi-accent="refined-cyan"] { --axi-accent: #5eadd5; }
7
+ [data-axi-accent="amber-warm"] { --axi-accent: #f59e0b; }
8
+ [data-axi-accent="emerald-mint"] { --axi-accent: #34d399; }
9
+ [data-axi-accent="rose-pink"] { --axi-accent: #f43f5e; }
10
+ [data-axi-accent="violet-purple"] { --axi-accent: #8b5cf6; }
11
+ [data-axi-accent="crimson-red"] { --axi-accent: #ef4444; }
12
+ [data-axi-accent="slate-silver"] { --axi-accent: #94a3b8; }
13
+ [data-axi-accent="teal-ocean"] { --axi-accent: #14b8a6; }
14
+ [data-axi-accent="gold-bronze"] { --axi-accent: #d4a017; }
package/dist/axi.css CHANGED
@@ -49,8 +49,17 @@
49
49
  block instead of admitting bare literals. */
50
50
  --axi-offset-panel-hover: 10px;
51
51
  --axi-offset-control-hover: 6px;
52
- --axi-radius: 10px;
53
- --axi-radius-sm: 6px;
52
+ /* The form is sharp. A hard outline, a hard block and a rounded corner are
53
+ three decisions, and the rounded one softens the other two: the corner is
54
+ the first place a flat outlined shape can start looking like a button in
55
+ some other language. Two tokens rather than one because the scale still
56
+ has to be able to come back - a consumer that wants soft corners sets
57
+ these and gets them everywhere, which is the whole point of the pass that
58
+ took the hand-written 8px, 9px, 5px, 4px, 2px and 99px out of the
59
+ components. Panel-sized surfaces read --axi-radius; everything
60
+ control-sized reads --axi-radius-sm. */
61
+ --axi-radius: 0;
62
+ --axi-radius-sm: 0;
54
63
  /* Not a form step: a hairline used inside prose (inline code, table rules,
55
64
  the list bullet) where the two form steps above would read as too heavy
56
65
  for a line of running text. */
@@ -181,7 +190,7 @@ a { color: inherit; }
181
190
  display: inline-flex; align-items: center; gap: 8px;
182
191
  padding: 12px 20px;
183
192
  border: var(--axi-border-control) solid var(--axi-ink-line);
184
- border-radius: 8px;
193
+ border-radius: var(--axi-radius-sm);
185
194
  background: var(--axi-ground);
186
195
  color: var(--axi-text-dim);
187
196
  font: var(--axi-t-label);
@@ -225,7 +234,7 @@ a { color: inherit; }
225
234
  display: inline-flex; align-items: center; gap: 6px;
226
235
  padding: 10px 9px;
227
236
  border: var(--axi-border-control) solid var(--axi-ink-line);
228
- border-radius: 8px;
237
+ border-radius: var(--axi-radius-sm);
229
238
  background: var(--axi-ground);
230
239
  color: var(--axi-text-dim);
231
240
  font: var(--axi-t-label);
@@ -295,7 +304,7 @@ a { color: inherit; }
295
304
  position: absolute;
296
305
  top: 2px; bottom: 2px; left: 2px;
297
306
  width: var(--axi-switch-knob, 16px);
298
- border-radius: 2px;
307
+ border-radius: var(--axi-radius-sm);
299
308
  background: var(--axi-ink-line);
300
309
  transition: transform .1s;
301
310
  }
@@ -340,7 +349,7 @@ a { color: inherit; }
340
349
  .axi-badge-count {
341
350
  display: inline-block;
342
351
  padding: 1px 7px;
343
- border-radius: 99px;
352
+ border-radius: var(--axi-radius-sm);
344
353
  background: var(--axi-accent);
345
354
  color: var(--axi-accent-ink);
346
355
  font: var(--axi-t-micro);
@@ -372,7 +381,7 @@ a { color: inherit; }
372
381
  padding: 11px 12px;
373
382
  background: var(--axi-ground);
374
383
  border: var(--axi-border-control) solid var(--axi-ink-line);
375
- border-radius: 8px;
384
+ border-radius: var(--axi-radius-sm);
376
385
  color: var(--axi-text);
377
386
  font: var(--axi-t-label);
378
387
  font-size: 14px;
@@ -396,7 +405,7 @@ a { color: inherit; }
396
405
  appearance: none;
397
406
  padding: 10px 30px 10px 9px;
398
407
  border: var(--axi-border-control) solid var(--axi-ink-line);
399
- border-radius: 8px;
408
+ border-radius: var(--axi-radius-sm);
400
409
  background-color: var(--axi-ground);
401
410
  color: var(--axi-text-dim);
402
411
  font: var(--axi-t-label);
@@ -432,7 +441,7 @@ a { color: inherit; }
432
441
  .axi-select::picker(select) {
433
442
  margin-top: 9px; padding: 6px;
434
443
  border: var(--axi-border-panel) solid var(--axi-ink-line);
435
- border-radius: 9px;
444
+ border-radius: var(--axi-radius);
436
445
  background: var(--axi-surface-raised);
437
446
  box-shadow: var(--axi-offset-panel) var(--axi-offset-panel) 0 var(--axi-ink-line);
438
447
  }
@@ -546,7 +555,7 @@ a { color: inherit; }
546
555
  .axi-sigil::before {
547
556
  content: ""; position: absolute; inset: 0; z-index: -1;
548
557
  transform: rotate(45deg);
549
- border-radius: 5px;
558
+ border-radius: var(--axi-radius-sm);
550
559
  background: var(--axi-accent);
551
560
  border: var(--axi-border-control) solid var(--axi-ink-line);
552
561
  }
@@ -563,7 +572,7 @@ a { color: inherit; }
563
572
  font: 800 13.5px/1 var(--axi-sans);
564
573
  color: var(--axi-text-dim); text-decoration: none;
565
574
  border: var(--axi-border-control) solid transparent;
566
- border-radius: 8px;
575
+ border-radius: var(--axi-radius-sm);
567
576
  }
568
577
  .axi-tabs a:hover { color: var(--axi-text); border-color: var(--axi-rule); }
569
578
  /* The current tab is filled and blocked - the same treatment a pressed pill
@@ -617,7 +626,7 @@ a { color: inherit; }
617
626
  max-height: 340px; overflow-y: auto; padding: 8px;
618
627
  background: var(--axi-surface-raised);
619
628
  border: var(--axi-border-panel) solid var(--axi-ink-line);
620
- border-radius: 9px;
629
+ border-radius: var(--axi-radius);
621
630
  box-shadow: var(--axi-offset-panel) var(--axi-offset-panel) 0 var(--axi-ink-line);
622
631
  }
623
632
  .axi-menu__pop[hidden] { display: none; }
@@ -659,7 +668,7 @@ a { color: inherit; }
659
668
  .axi-card__glyph {
660
669
  flex: none; width: 40px; height: 40px;
661
670
  display: grid; place-items: center;
662
- border-radius: 8px;
671
+ border-radius: var(--axi-radius-sm);
663
672
  border: var(--axi-border-control) solid var(--axi-ink-line);
664
673
  background: var(--axi-surface-raised);
665
674
  color: var(--axi-text);
@@ -714,7 +723,7 @@ a { color: inherit; }
714
723
  position: absolute; top: 16px; right: 18px;
715
724
  width: 32px; height: 32px;
716
725
  display: grid; place-items: center;
717
- border-radius: 8px;
726
+ border-radius: var(--axi-radius-sm);
718
727
  background: var(--axi-ground);
719
728
  border: var(--axi-border-control) solid var(--axi-ink-line);
720
729
  color: var(--axi-text-dim);
@@ -834,7 +843,7 @@ a { color: inherit; }
834
843
  padding: 12px 14px;
835
844
  background: var(--axi-ground);
836
845
  border: var(--axi-border-control) solid var(--axi-ink-line);
837
- border-radius: 8px;
846
+ border-radius: var(--axi-radius-sm);
838
847
  }
839
848
  .axi-stat__n {
840
849
  display: block;
@@ -1109,7 +1118,7 @@ a { color: inherit; }
1109
1118
  font-family: var(--axi-mono); font-size: .88em;
1110
1119
  background: var(--axi-ground); color: var(--axi-text);
1111
1120
  border: var(--axi-border-hairline) solid var(--axi-ink-line);
1112
- border-radius: 4px; padding: 1px 5px;
1121
+ border-radius: var(--axi-radius-sm); padding: 1px 5px;
1113
1122
  }
1114
1123
  .axi-prose pre {
1115
1124
  margin: 0 0 18px; padding: 14px 16px; overflow-x: auto;
package/docs/RULES.md CHANGED
@@ -95,10 +95,21 @@ deliberate asymmetry: it costs the (rare, deliberate) case of a bare named
95
95
  colour smuggled inside a data-URI SVG, in exchange for never blocking a font
96
96
  stack, a filename or a `content` string again.
97
97
 
98
- Radii are the one part of the form that is **not** enforced: `--axi-radius`
99
- and `--axi-radius-sm` exist, but controls carry a literal `8px` (and `9px`,
100
- `5px`, `4px` appear elsewhere). Treat the radius scale as convention, not
101
- contract, until it is tokenised.
98
+ **The corner is square.** `--axi-radius` and `--axi-radius-sm` are both `0`.
99
+ A hard outline, a hard block and a rounded corner are three decisions, and the
100
+ rounded one quietly undoes the other two — the corner is the first place a
101
+ flat outlined shape starts looking like a button borrowed from some other
102
+ system. Nothing in the language rounds.
103
+
104
+ The two tokens stay because the scale has to be able to come back: a consumer
105
+ that wants soft corners sets them once and gets them everywhere. That is only
106
+ true now that the components read them. They previously carried a hand-written
107
+ `8px` on every control, with `9px`, `5px`, `4px`, `2px` and a `99px` lozenge
108
+ scattered elsewhere, and this paragraph used to say to treat the scale as
109
+ convention rather than contract. It is a contract now: panel-sized surfaces
110
+ read `--axi-radius`, everything control-sized reads `--axi-radius-sm`, and
111
+ `tests/tokens.test.mjs` fails on a literal radius in a component file the same
112
+ way it fails on a literal border weight.
102
113
 
103
114
  ## 4. Hover lifts
104
115
 
@@ -181,6 +192,40 @@ spends the brightest thing on screen on the fact that a list has a first line.
181
192
  The hover on a row is the neutral ramp, not an ink, for the same reason: moving
182
193
  the cursor down a table is not a series of status changes.
183
194
 
195
+ ### The counterpart: a short list of things you act on
196
+
197
+ Rule 8 is about a *reading*. Forty rows of numbers exist to be compared, and
198
+ the comparison happens down a column — outlining the rows cuts the column into
199
+ boxes and takes away the only reason the table was worth drawing.
200
+
201
+ A launcher's list is not that. Each row is one thing, with its own name, its
202
+ own state and its own verb sitting at the end of it. Nobody scans down such a
203
+ list comparing rows; they find the one they came for and press it. There is no
204
+ column to protect, and the flat interior treatment actively lies about what the
205
+ row is, because the thing you are about to click looks like a line of a table.
206
+
207
+ So: **a short list of objects you act on is drawn as a stack of cards** —
208
+ outline and block, the same as any other raised thing.
209
+
210
+ Three bounds, and the rule is only sound with all three:
211
+
212
+ - **Short.** A dozen or so. The block is what says "press me", and past about
213
+ that many the blocks stop reading as depth and start reading as texture — at
214
+ which point the list has become a table again and rule 8 has it back.
215
+ - **Control weight, not panel.** These cards sit *inside* a panel that already
216
+ carries a 6px block. A second 6px block nested in the first reads as two
217
+ planes arguing; the 3px control step reads as the contents of a box. This is
218
+ also the honest weight: each row is a control you press, not a surface.
219
+ - **The outline stays `--axi-ink-line`.** Status goes on a filled shape inside
220
+ the row — the icon tile, a chip — and never on the row's own edge. Colouring
221
+ the edge is exactly the full-height stripe rule 5 rejects: five states become
222
+ five coloured frames and the colour stops saying anything about any one of
223
+ them.
224
+
225
+ The test for which rule applies: *does the eye run down a column?* If yes, it
226
+ is a table and it is drawn in rules. If every row ends in a button, it is a
227
+ stack and it is drawn in cards.
228
+
184
229
  ## 9. A quantity is drawn as length, never intensity
185
230
 
186
231
  A proportion is a bar: the track is the ground, the fill is the value, and the
@@ -264,6 +309,25 @@ literal, so a light theme is a second palette block, not a rewrite. It is not
264
309
  a token swap either — the saturated inks that read as vivid on near-black go
265
310
  washed out on white and would need retuning.
266
311
 
312
+ ## The official accents
313
+
314
+ `--axi-accent` is the per-app theming surface, and the family now agrees on
315
+ what may go in it. The official accents live in `accents.json` — id, label,
316
+ hex — and `dist/accents.css` is generated from it: one
317
+ `[data-axi-accent="<id>"]` selector per accent, setting `--axi-accent` and
318
+ nothing else. An app opts in by importing `accents.css` alongside `axi.css`
319
+ and setting `data-axi-accent` on its root element; an app that renders a
320
+ picker reads `accents.json` for the ids and labels.
321
+
322
+ accents.json is the second sanctioned home for a colour literal, after
323
+ tokens.css — sanctioned because it is data the build generates from, not
324
+ stylesheet source. Adding an accent means editing accents.json and running
325
+ the build; hand-editing dist/accents.css is exactly as wrong as hand-editing
326
+ dist/axi.css.
327
+
328
+ The default remains `#ffc53d` Axi Gold, declared in tokens.css: an app that
329
+ sets no `data-axi-accent` is gold, and correctly themed.
330
+
267
331
  ## Adding a component
268
332
 
269
333
  1. Which rule justifies it? If none, write the rule first or stop.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axiapps/axi-design",
3
- "version": "1.6.0",
3
+ "version": "1.7.1",
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",
@@ -14,6 +14,8 @@
14
14
  "exports": {
15
15
  "./axi.css": "./dist/axi.css",
16
16
  "./tokens.css": "./src/tokens.css",
17
+ "./accents.css": "./dist/accents.css",
18
+ "./accents.json": "./accents.json",
17
19
  "./package.json": "./package.json"
18
20
  },
19
21
  "//sideEffects": "A stylesheet is nothing but a side effect. Without this a bundler treating the import as dead code would drop the whole design language from a production build.",
@@ -25,6 +27,7 @@
25
27
  "dist",
26
28
  "src",
27
29
  "docs",
30
+ "accents.json",
28
31
  "README.md"
29
32
  ],
30
33
  "scripts": {
package/src/data.css CHANGED
@@ -20,7 +20,7 @@
20
20
  padding: 12px 14px;
21
21
  background: var(--axi-ground);
22
22
  border: var(--axi-border-control) solid var(--axi-ink-line);
23
- border-radius: 8px;
23
+ border-radius: var(--axi-radius-sm);
24
24
  }
25
25
  .axi-stat__n {
26
26
  display: block;
@@ -24,7 +24,7 @@
24
24
  display: inline-flex; align-items: center; gap: 8px;
25
25
  padding: 12px 20px;
26
26
  border: var(--axi-border-control) solid var(--axi-ink-line);
27
- border-radius: 8px;
27
+ border-radius: var(--axi-radius-sm);
28
28
  background: var(--axi-ground);
29
29
  color: var(--axi-text-dim);
30
30
  font: var(--axi-t-label);
@@ -68,7 +68,7 @@
68
68
  display: inline-flex; align-items: center; gap: 6px;
69
69
  padding: 10px 9px;
70
70
  border: var(--axi-border-control) solid var(--axi-ink-line);
71
- border-radius: 8px;
71
+ border-radius: var(--axi-radius-sm);
72
72
  background: var(--axi-ground);
73
73
  color: var(--axi-text-dim);
74
74
  font: var(--axi-t-label);
@@ -138,7 +138,7 @@
138
138
  position: absolute;
139
139
  top: 2px; bottom: 2px; left: 2px;
140
140
  width: var(--axi-switch-knob, 16px);
141
- border-radius: 2px;
141
+ border-radius: var(--axi-radius-sm);
142
142
  background: var(--axi-ink-line);
143
143
  transition: transform .1s;
144
144
  }
@@ -183,7 +183,7 @@
183
183
  .axi-badge-count {
184
184
  display: inline-block;
185
185
  padding: 1px 7px;
186
- border-radius: 99px;
186
+ border-radius: var(--axi-radius-sm);
187
187
  background: var(--axi-accent);
188
188
  color: var(--axi-accent-ink);
189
189
  font: var(--axi-t-micro);
@@ -215,7 +215,7 @@
215
215
  padding: 11px 12px;
216
216
  background: var(--axi-ground);
217
217
  border: var(--axi-border-control) solid var(--axi-ink-line);
218
- border-radius: 8px;
218
+ border-radius: var(--axi-radius-sm);
219
219
  color: var(--axi-text);
220
220
  font: var(--axi-t-label);
221
221
  font-size: 14px;
@@ -239,7 +239,7 @@
239
239
  appearance: none;
240
240
  padding: 10px 30px 10px 9px;
241
241
  border: var(--axi-border-control) solid var(--axi-ink-line);
242
- border-radius: 8px;
242
+ border-radius: var(--axi-radius-sm);
243
243
  background-color: var(--axi-ground);
244
244
  color: var(--axi-text-dim);
245
245
  font: var(--axi-t-label);
@@ -275,7 +275,7 @@
275
275
  .axi-select::picker(select) {
276
276
  margin-top: 9px; padding: 6px;
277
277
  border: var(--axi-border-panel) solid var(--axi-ink-line);
278
- border-radius: 9px;
278
+ border-radius: var(--axi-radius);
279
279
  background: var(--axi-surface-raised);
280
280
  box-shadow: var(--axi-offset-panel) var(--axi-offset-panel) 0 var(--axi-ink-line);
281
281
  }
package/src/prose.css CHANGED
@@ -50,7 +50,7 @@
50
50
  font-family: var(--axi-mono); font-size: .88em;
51
51
  background: var(--axi-ground); color: var(--axi-text);
52
52
  border: var(--axi-border-hairline) solid var(--axi-ink-line);
53
- border-radius: 4px; padding: 1px 5px;
53
+ border-radius: var(--axi-radius-sm); padding: 1px 5px;
54
54
  }
55
55
  .axi-prose pre {
56
56
  margin: 0 0 18px; padding: 14px 16px; overflow-x: auto;
package/src/shells.css CHANGED
@@ -37,7 +37,7 @@
37
37
  .axi-sigil::before {
38
38
  content: ""; position: absolute; inset: 0; z-index: -1;
39
39
  transform: rotate(45deg);
40
- border-radius: 5px;
40
+ border-radius: var(--axi-radius-sm);
41
41
  background: var(--axi-accent);
42
42
  border: var(--axi-border-control) solid var(--axi-ink-line);
43
43
  }
@@ -54,7 +54,7 @@
54
54
  font: 800 13.5px/1 var(--axi-sans);
55
55
  color: var(--axi-text-dim); text-decoration: none;
56
56
  border: var(--axi-border-control) solid transparent;
57
- border-radius: 8px;
57
+ border-radius: var(--axi-radius-sm);
58
58
  }
59
59
  .axi-tabs a:hover { color: var(--axi-text); border-color: var(--axi-rule); }
60
60
  /* The current tab is filled and blocked - the same treatment a pressed pill
@@ -108,7 +108,7 @@
108
108
  max-height: 340px; overflow-y: auto; padding: 8px;
109
109
  background: var(--axi-surface-raised);
110
110
  border: var(--axi-border-panel) solid var(--axi-ink-line);
111
- border-radius: 9px;
111
+ border-radius: var(--axi-radius);
112
112
  box-shadow: var(--axi-offset-panel) var(--axi-offset-panel) 0 var(--axi-ink-line);
113
113
  }
114
114
  .axi-menu__pop[hidden] { display: none; }
@@ -150,7 +150,7 @@
150
150
  .axi-card__glyph {
151
151
  flex: none; width: 40px; height: 40px;
152
152
  display: grid; place-items: center;
153
- border-radius: 8px;
153
+ border-radius: var(--axi-radius-sm);
154
154
  border: var(--axi-border-control) solid var(--axi-ink-line);
155
155
  background: var(--axi-surface-raised);
156
156
  color: var(--axi-text);
@@ -205,7 +205,7 @@
205
205
  position: absolute; top: 16px; right: 18px;
206
206
  width: 32px; height: 32px;
207
207
  display: grid; place-items: center;
208
- border-radius: 8px;
208
+ border-radius: var(--axi-radius-sm);
209
209
  background: var(--axi-ground);
210
210
  border: var(--axi-border-control) solid var(--axi-ink-line);
211
211
  color: var(--axi-text-dim);
package/src/tokens.css CHANGED
@@ -45,8 +45,17 @@
45
45
  block instead of admitting bare literals. */
46
46
  --axi-offset-panel-hover: 10px;
47
47
  --axi-offset-control-hover: 6px;
48
- --axi-radius: 10px;
49
- --axi-radius-sm: 6px;
48
+ /* The form is sharp. A hard outline, a hard block and a rounded corner are
49
+ three decisions, and the rounded one softens the other two: the corner is
50
+ the first place a flat outlined shape can start looking like a button in
51
+ some other language. Two tokens rather than one because the scale still
52
+ has to be able to come back - a consumer that wants soft corners sets
53
+ these and gets them everywhere, which is the whole point of the pass that
54
+ took the hand-written 8px, 9px, 5px, 4px, 2px and 99px out of the
55
+ components. Panel-sized surfaces read --axi-radius; everything
56
+ control-sized reads --axi-radius-sm. */
57
+ --axi-radius: 0;
58
+ --axi-radius-sm: 0;
50
59
  /* Not a form step: a hairline used inside prose (inline code, table rules,
51
60
  the list bullet) where the two form steps above would read as too heavy
52
61
  for a line of running text. */