@axiapps/axi-design 1.29.0 → 1.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -138,6 +138,8 @@ not "what does the system look like". Everything else is
138
138
  | `--axi-check-size` | the size of an `.axi-check` or `.axi-radio` box | `22px` | `<input type="checkbox" class="axi-check" style="--axi-check-size: 16px">` |
139
139
  | `--axi-check-fill` | the fill a checked `.axi-check` takes, and the colour of a checked `.axi-radio`'s diamond | `var(--axi-accent)` | `<input type="checkbox" class="axi-check" style="--axi-check-fill: var(--axi-danger)">` |
140
140
  | `--axi-textarea-h` | the minimum height of a `<textarea class="axi-input">` | `90px` | `<textarea class="axi-input" style="--axi-textarea-h: 200px">` |
141
+ | `--axi-btn-pad` | the padding inside an `.axi-btn`. The three named steps set it for you; reach for the knob only for a size they do not cover | `12px 20px` | `<button class="axi-btn" style="--axi-btn-pad: 3px 7px">` |
142
+ | `--axi-btn-size` | an `.axi-btn`'s text size, which travels with its padding | `13px` | `<button class="axi-btn" style="--axi-btn-size: 10px">` |
141
143
  | `--axi-input-pad` | the padding inside an `.axi-input`, for a field that is furniture in a header rather than a control on a page | `11px 12px` | `<input class="axi-input" style="--axi-input-pad: 7px 12px">` |
142
144
  | `--axi-input-size` | an `.axi-input`'s text size, which travels with its padding | `14px` | `<input class="axi-input" style="--axi-input-size: 13px">` |
143
145
  | `--axi-well-pad` | the padding inside an `.axi-well` | `10px` | `<div class="axi-well" style="--axi-well-pad: 18px">` |
package/dist/axi.css CHANGED
@@ -374,13 +374,15 @@ button.axi-panel--tile:hover,
374
374
  /* ---------- button ---------- */
375
375
  .axi-btn {
376
376
  display: inline-flex; align-items: center; gap: 8px;
377
- padding: 12px 20px;
377
+ padding: var(--axi-btn-pad, 12px 20px);
378
378
  border: var(--axi-border-control) solid var(--axi-ink-line);
379
379
  border-radius: var(--axi-radius-sm);
380
380
  background: var(--axi-ground);
381
381
  color: var(--axi-text-dim);
382
382
  font: var(--axi-t-label);
383
383
  letter-spacing: var(--axi-ls-label);
384
+ /* After the `font` shorthand, which sets 13px and would otherwise reset it. */
385
+ font-size: var(--axi-btn-size, 13px);
384
386
  text-transform: uppercase;
385
387
  text-decoration: none;
386
388
  cursor: pointer;
@@ -388,8 +390,18 @@ button.axi-panel--tile:hover,
388
390
  }
389
391
  /* Hover lifts. Nothing in this language fades or glows: the block deepens and
390
392
  the element moves against it, which reads instantly even in peripheral
391
- vision and costs no colour. */
392
- .axi-btn:hover {
393
+ vision and costs no colour.
394
+
395
+ :where() around the state, not decoration. `.axi-btn:hover` weighs two
396
+ classes, so it beat the ink layer's one and a button written
397
+ `class="axi-btn axi-ink-danger"` went from danger red to plain white under
398
+ the cursor - the verdict disappearing at the moment the reader reaches for
399
+ it. That is rule 6's "a component may not out-rank its own modifiers" with
400
+ a pseudo-class doing the out-ranking instead of an element, and it wants the
401
+ same fix. The dim-to-plain brighten still happens: it is the fallback now,
402
+ not an override, so it applies to exactly the buttons that have not been
403
+ told what colour they are. */
404
+ .axi-btn:where(:hover) {
393
405
  color: var(--axi-text);
394
406
  box-shadow: var(--axi-shadow-control);
395
407
  transform: translate(-2px, -2px);
@@ -402,13 +414,33 @@ button.axi-panel--tile:hover,
402
414
  /* The one button that rests with a block already under it, so the generic
403
415
  hover's translate alone would move element and block together and leave the
404
416
  lower-right edge where it started - the "grows rather than lifts" failure
405
- rule 4 warns about. It lifts the way a panel does: the block deepens too. */
417
+ rule 4 warns about. It lifts the way a panel does: the block deepens too.
418
+
419
+ Not wrapped in :where() the way the generic hover is: this colour is the
420
+ accent fill's contrast pair, not a default a consumer might mean to replace.
421
+ An ink reaching it would put a status colour on top of the accent block and
422
+ the label would stop being legible, which is rule 5's reason for the chip. */
406
423
  .axi-btn--primary:hover {
407
424
  color: var(--axi-accent-ink);
408
425
  box-shadow: var(--axi-shadow-control-hover);
409
426
  }
410
427
  .axi-btn--ghost { background: transparent; }
411
428
  .axi-btn--dashed { background: transparent; border-style: dashed; }
429
+ /* Three sizes, because one was a fiction. A language that ships a single
430
+ button leaves every dense place in an app - a filter beside 11px type, a
431
+ toolbar, a row of controls in a card header - to spell its own padding and
432
+ font-size in utilities, and at that point the button is only borrowing the
433
+ border. The steps are named rather than left to the knobs for the same
434
+ reason .axi-table--dense is: a consumer saying "this is the small one"
435
+ should not have to pick the numbers, and the numbers should move together
436
+ when they move.
437
+ The icon step is the one the language got wrong rather than omitted. An
438
+ icon-only button has no label to pad around, so the 20px sides meant for
439
+ one produced a wide rectangle around a single glyph - visible in this
440
+ language's own gallery, in the example titled "Alone in a button". */
441
+ .axi-btn--sm { --axi-btn-pad: 8px 13px; --axi-btn-size: 12px; }
442
+ .axi-btn--xs { --axi-btn-pad: 5px 9px; --axi-btn-size: 11px; }
443
+ .axi-btn--icon { --axi-btn-pad: 7px; }
412
444
 
413
445
  /* ---------- pill ---------- */
414
446
  /* A filter toggle. Its pressed state fills with whatever colour the consumer
@@ -429,7 +461,10 @@ button.axi-panel--tile:hover,
429
461
  cursor: pointer;
430
462
  transition: transform .1s, box-shadow .1s;
431
463
  }
432
- .axi-pill:hover {
464
+ /* :where() for the same reason the button's hover has it - the brighten is
465
+ what a pill that has not been told its colour falls back to, not an override
466
+ of one that has. See rule 6's addendum. */
467
+ .axi-pill:where(:hover) {
433
468
  color: var(--axi-text);
434
469
  box-shadow: var(--axi-shadow-control);
435
470
  transform: translate(-2px, -2px);
@@ -439,11 +474,14 @@ button.axi-panel--tile:hover,
439
474
  color: var(--axi-accent-ink);
440
475
  box-shadow: var(--axi-shadow-control);
441
476
  }
442
- /* Spelled out rather than left to source order: `.axi-pill:hover` and
443
- `.axi-pill[aria-pressed="true"]` have identical specificity, so without this
444
- rule a pressed pill under the cursor kept its pressed block while still
445
- taking the hover's translate - a slide, not a lift, decided by which rule
446
- happened to come last in the file. A pressed pill keeps its fill and lifts. */
477
+ /* Spelled out rather than left to the cascade. This was originally about equal
478
+ specificity - the two rules weighed the same, so which one a pressed pill
479
+ under the cursor took was decided by file order. The hover's :where() ended
480
+ the tie in the pressed rule's favour, and made this rule matter more rather
481
+ than less: `transform` is set only by the hover, so a pressed pill would take
482
+ its own fill and block from the higher-weight rule while still taking the
483
+ hover's translate - a slide, not a lift, which is the failure rule 4 names.
484
+ A pressed pill keeps its fill and lifts. */
447
485
  .axi-pill[aria-pressed="true"]:hover {
448
486
  background: var(--axi-pill-fill, var(--axi-accent));
449
487
  color: var(--axi-accent-ink);
package/docs/RULES.md CHANGED
@@ -260,12 +260,27 @@ there is no object, only a mark.
260
260
 
261
261
  They live in `src/utilities.css`, last in the cascade, which is how a single
262
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.
263
+ an obligation on components in the other direction: **a component's base and
264
+ state rules may not out-rank its own modifiers.** A rule written `.axi-table td`
265
+ weighs a class and a type, so it silently defeats both `.axi-table__num` and any
266
+ ink a consumer puts on the cell — the fix is `:where()` around the element,
267
+ which spends no specificity. Where a component holds a consumer's content, wrap
268
+ the element part.
269
+
270
+ A pseudo-class does the same arithmetic. `.axi-btn:hover` weighs two classes, so
271
+ a button written `class="axi-btn axi-ink-danger"` was danger red at rest and
272
+ plain white under the cursor: the verdict vanishing at the moment the reader
273
+ reaches for it. Where a state rule restates a colour the consumer might have
274
+ meant to set — a generic control's hover brighten, which applies to whatever has
275
+ not been told what it is — wrap the state: `.axi-btn:where(:hover)`. The
276
+ brighten becomes the fallback it always was.
277
+
278
+ Two things this does not apply to. A colour that *is* the state's meaning stays
279
+ at full weight, because there is nothing for an ink to add and an ink reaching it
280
+ would be wrong: a selected rail item, a palette's cursor row, a pressed pill. And
281
+ a colour that is a fill's contrast pair — `.axi-btn--primary`'s accent ink — stays
282
+ too, because an ink there would put a status colour on the accent block and cost
283
+ the label its legibility, which is rule 5's reason for the chip.
269
284
 
270
285
  ## 7. The diamond is the family motif
271
286
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axiapps/axi-design",
3
- "version": "1.29.0",
3
+ "version": "1.30.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",
@@ -100,13 +100,15 @@ button.axi-panel--tile:hover,
100
100
  /* ---------- button ---------- */
101
101
  .axi-btn {
102
102
  display: inline-flex; align-items: center; gap: 8px;
103
- padding: 12px 20px;
103
+ padding: var(--axi-btn-pad, 12px 20px);
104
104
  border: var(--axi-border-control) solid var(--axi-ink-line);
105
105
  border-radius: var(--axi-radius-sm);
106
106
  background: var(--axi-ground);
107
107
  color: var(--axi-text-dim);
108
108
  font: var(--axi-t-label);
109
109
  letter-spacing: var(--axi-ls-label);
110
+ /* After the `font` shorthand, which sets 13px and would otherwise reset it. */
111
+ font-size: var(--axi-btn-size, 13px);
110
112
  text-transform: uppercase;
111
113
  text-decoration: none;
112
114
  cursor: pointer;
@@ -114,8 +116,18 @@ button.axi-panel--tile:hover,
114
116
  }
115
117
  /* Hover lifts. Nothing in this language fades or glows: the block deepens and
116
118
  the element moves against it, which reads instantly even in peripheral
117
- vision and costs no colour. */
118
- .axi-btn:hover {
119
+ vision and costs no colour.
120
+
121
+ :where() around the state, not decoration. `.axi-btn:hover` weighs two
122
+ classes, so it beat the ink layer's one and a button written
123
+ `class="axi-btn axi-ink-danger"` went from danger red to plain white under
124
+ the cursor - the verdict disappearing at the moment the reader reaches for
125
+ it. That is rule 6's "a component may not out-rank its own modifiers" with
126
+ a pseudo-class doing the out-ranking instead of an element, and it wants the
127
+ same fix. The dim-to-plain brighten still happens: it is the fallback now,
128
+ not an override, so it applies to exactly the buttons that have not been
129
+ told what colour they are. */
130
+ .axi-btn:where(:hover) {
119
131
  color: var(--axi-text);
120
132
  box-shadow: var(--axi-shadow-control);
121
133
  transform: translate(-2px, -2px);
@@ -128,13 +140,33 @@ button.axi-panel--tile:hover,
128
140
  /* The one button that rests with a block already under it, so the generic
129
141
  hover's translate alone would move element and block together and leave the
130
142
  lower-right edge where it started - the "grows rather than lifts" failure
131
- rule 4 warns about. It lifts the way a panel does: the block deepens too. */
143
+ rule 4 warns about. It lifts the way a panel does: the block deepens too.
144
+
145
+ Not wrapped in :where() the way the generic hover is: this colour is the
146
+ accent fill's contrast pair, not a default a consumer might mean to replace.
147
+ An ink reaching it would put a status colour on top of the accent block and
148
+ the label would stop being legible, which is rule 5's reason for the chip. */
132
149
  .axi-btn--primary:hover {
133
150
  color: var(--axi-accent-ink);
134
151
  box-shadow: var(--axi-shadow-control-hover);
135
152
  }
136
153
  .axi-btn--ghost { background: transparent; }
137
154
  .axi-btn--dashed { background: transparent; border-style: dashed; }
155
+ /* Three sizes, because one was a fiction. A language that ships a single
156
+ button leaves every dense place in an app - a filter beside 11px type, a
157
+ toolbar, a row of controls in a card header - to spell its own padding and
158
+ font-size in utilities, and at that point the button is only borrowing the
159
+ border. The steps are named rather than left to the knobs for the same
160
+ reason .axi-table--dense is: a consumer saying "this is the small one"
161
+ should not have to pick the numbers, and the numbers should move together
162
+ when they move.
163
+ The icon step is the one the language got wrong rather than omitted. An
164
+ icon-only button has no label to pad around, so the 20px sides meant for
165
+ one produced a wide rectangle around a single glyph - visible in this
166
+ language's own gallery, in the example titled "Alone in a button". */
167
+ .axi-btn--sm { --axi-btn-pad: 8px 13px; --axi-btn-size: 12px; }
168
+ .axi-btn--xs { --axi-btn-pad: 5px 9px; --axi-btn-size: 11px; }
169
+ .axi-btn--icon { --axi-btn-pad: 7px; }
138
170
 
139
171
  /* ---------- pill ---------- */
140
172
  /* A filter toggle. Its pressed state fills with whatever colour the consumer
@@ -155,7 +187,10 @@ button.axi-panel--tile:hover,
155
187
  cursor: pointer;
156
188
  transition: transform .1s, box-shadow .1s;
157
189
  }
158
- .axi-pill:hover {
190
+ /* :where() for the same reason the button's hover has it - the brighten is
191
+ what a pill that has not been told its colour falls back to, not an override
192
+ of one that has. See rule 6's addendum. */
193
+ .axi-pill:where(:hover) {
159
194
  color: var(--axi-text);
160
195
  box-shadow: var(--axi-shadow-control);
161
196
  transform: translate(-2px, -2px);
@@ -165,11 +200,14 @@ button.axi-panel--tile:hover,
165
200
  color: var(--axi-accent-ink);
166
201
  box-shadow: var(--axi-shadow-control);
167
202
  }
168
- /* Spelled out rather than left to source order: `.axi-pill:hover` and
169
- `.axi-pill[aria-pressed="true"]` have identical specificity, so without this
170
- rule a pressed pill under the cursor kept its pressed block while still
171
- taking the hover's translate - a slide, not a lift, decided by which rule
172
- happened to come last in the file. A pressed pill keeps its fill and lifts. */
203
+ /* Spelled out rather than left to the cascade. This was originally about equal
204
+ specificity - the two rules weighed the same, so which one a pressed pill
205
+ under the cursor took was decided by file order. The hover's :where() ended
206
+ the tie in the pressed rule's favour, and made this rule matter more rather
207
+ than less: `transform` is set only by the hover, so a pressed pill would take
208
+ its own fill and block from the higher-weight rule while still taking the
209
+ hover's translate - a slide, not a lift, which is the failure rule 4 names.
210
+ A pressed pill keeps its fill and lifts. */
173
211
  .axi-pill[aria-pressed="true"]:hover {
174
212
  background: var(--axi-pill-fill, var(--axi-accent));
175
213
  color: var(--axi-accent-ink);