@axiapps/axi-design 1.11.0 → 1.13.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
@@ -67,13 +67,16 @@ import '@axiapps/axi-design/themes/glass.css'
67
67
  <html data-axi-theme="glass">
68
68
  ```
69
69
 
70
- `glass` keeps the outline, the hard block and every ink exactly as they are,
71
- and repaints the surface layer: translucent panels over a colder ground, with
72
- popovers, drawers and modals blurring what sits behind them. Remove the
73
- attribute and you are back on the main theme with no other change — a theme
74
- restates tokens and never adds a component, so nothing in your markup knows
75
- which one is on. That is the point: the look is maintained here rather than
76
- copied into your app as a custom theme that then has to be kept in step.
70
+ `glass` repaints the material: translucent panels lit from the top-left over a
71
+ near-black page with three wide colour washes on it, hairline edges, rounded
72
+ corners and a soft drop instead of the main theme's hard offset block, with
73
+ popovers, drawers and modals blurring what sits behind them. The five saturated
74
+ inks, the measure and the type are untouched — they are the language, not the
75
+ paint. Remove the attribute and you are back on the main theme with no other
76
+ change: a theme restates tokens and never adds a component, so nothing in your
77
+ markup knows which one is on. That is the point — the look is maintained here
78
+ rather than copied into your app as a custom theme that then has to be kept in
79
+ step.
77
80
 
78
81
  The main theme is the language and a theme is a repaint of it — [the
79
82
  rules](docs/RULES.md#themes) say what a theme may and may not do.
package/dist/axi.css CHANGED
@@ -16,9 +16,32 @@
16
16
  :root {
17
17
  /* --- surface & text --- */
18
18
  --axi-ground: #15181d; /* the page itself */
19
+ /* An image layer painted over the ground colour, on the page and nowhere
20
+ else. The second toll the Themes section describes, and it exists for the
21
+ same reason --axi-surface-filter does: a glass theme wants the page lit
22
+ from a few directions, and it does not get to introduce a gradient any
23
+ more than it got to introduce a blur. Declared here inert (`none`), read
24
+ unconditionally by body, and restated by the theme.
25
+
26
+ Why a second token rather than letting a theme put the gradient in
27
+ --axi-ground itself: --axi-ground is read as `background-color` by the
28
+ plot and the select, where an image value is invalid and drops out, and
29
+ as the flat fill of the masthead, the tooltip row and a dozen inset
30
+ shapes, where a page-positioned radial would render as an arbitrary
31
+ wedge of colour. The ground stays a colour. This is the page's light. */
32
+ --axi-ground-image: none;
19
33
  --axi-surface: #222731; /* a panel raised off the page */
20
34
  --axi-surface-raised: #2b313d; /* a chip or popover raised off a panel */
21
35
  --axi-ink-line: #0c0e12; /* the outline every raised element is drawn with */
36
+ /* The tone a shape is made of when it is made of the line ink rather than
37
+ outlined in it - the tooltip, the titlebar strip, the switch's slug. Holds
38
+ --axi-ink-line today, which is why it reads as the same decision and is
39
+ not: one is the colour a shape's EDGE is drawn in, the other the colour a
40
+ shape is FILLED with. They only have to diverge once - a theme that
41
+ outlines in a light colour so its page can go near-black - for the
42
+ conflated spelling to turn the tooltip into a pale box. Same split as
43
+ --axi-ink-on-fill below, on the other side of the same token. */
44
+ --axi-ground-deep: var(--axi-ink-line);
22
45
  --axi-rule: #3a4250; /* internal rules, inside an outlined panel */
23
46
  --axi-text: #f4f6f9;
24
47
  --axi-text-dim: #a7b0be;
@@ -134,7 +157,13 @@ html { color-scheme: dark; }
134
157
 
135
158
  body {
136
159
  margin: 0;
137
- background: var(--axi-ground);
160
+ /* Longhands rather than the shorthand: the ground is a colour and the light
161
+ over it is an image, and `background:` would make setting either one
162
+ reset the other. Fixed so a theme's light stays put as the page scrolls
163
+ instead of stretching to the document's height. */
164
+ background-color: var(--axi-ground);
165
+ background-image: var(--axi-ground-image);
166
+ background-attachment: fixed;
138
167
  color: var(--axi-text);
139
168
  font: var(--axi-t-body);
140
169
  -webkit-font-smoothing: antialiased;
@@ -342,7 +371,7 @@ a { color: inherit; }
342
371
  top: 2px; bottom: 2px; left: 2px;
343
372
  width: var(--axi-switch-knob, 16px);
344
373
  border-radius: var(--axi-radius-sm);
345
- background: var(--axi-ink-line);
374
+ background: var(--axi-ground-deep);
346
375
  transition: transform .1s;
347
376
  }
348
377
  /* The travel is the track's inner width less the slug and its two insets.
@@ -922,7 +951,7 @@ textarea.axi-input {
922
951
  argument with one. */
923
952
  z-index: 70;
924
953
  padding: 5px 8px;
925
- background: var(--axi-ink-line);
954
+ background: var(--axi-ground-deep);
926
955
  color: var(--axi-text);
927
956
  border: var(--axi-border-hairline) solid var(--axi-rule);
928
957
  border-radius: var(--axi-radius-sm);
@@ -1071,7 +1100,7 @@ textarea.axi-input {
1071
1100
  flex: none;
1072
1101
  display: flex; align-items: center; gap: 12px;
1073
1102
  height: 38px; padding-left: 14px;
1074
- background: var(--axi-ink-line);
1103
+ background: var(--axi-ground-deep);
1075
1104
  border-bottom: var(--axi-border-control) solid var(--axi-ink-line);
1076
1105
  font: var(--axi-t-micro);
1077
1106
  letter-spacing: var(--axi-ls-micro);
@@ -2,10 +2,27 @@
2
2
  Edit the files in src/ and run `npm run build`; do not edit this file. */
3
3
 
4
4
  [data-axi-theme="glass"] {
5
- --axi-ground: #141924;
6
- --axi-surface: rgba(255, 255, 255, .09);
7
- --axi-surface-raised: rgba(255, 255, 255, .15);
8
- --axi-rule: rgba(255, 255, 255, .17);
9
- --axi-scrim: rgba(6, 7, 9, .55);
10
- --axi-surface-filter: blur(14px) saturate(140%);
5
+ --axi-ground: #0a0c10;
6
+ --axi-ground-image: radial-gradient(900px 500px at 15% 0%, rgba(120, 90, 255, .30), transparent 60%), radial-gradient(700px 500px at 95% 30%, rgba(0, 190, 255, .22), transparent 62%), radial-gradient(600px 600px at 60% 100%, rgba(255, 90, 170, .20), transparent 60%);
7
+ --axi-surface: linear-gradient(145deg, rgba(58, 68, 92, .55), rgba(28, 33, 46, .42));
8
+ --axi-surface-raised: linear-gradient(145deg, rgba(86, 99, 130, .5), rgba(44, 52, 71, .38));
9
+ --axi-surface-filter: blur(18px) saturate(140%);
10
+ --axi-ink-line: rgba(180, 200, 255, .16);
11
+ --axi-rule: rgba(180, 200, 255, .14);
12
+ --axi-ground-deep: #0c0e12;
13
+ --axi-accent-ink: #0c0e12;
14
+ --axi-ink-on-fill: #0c0e12;
15
+ --axi-text: #f2f5fb;
16
+ --axi-text-dim: #b3bdd0;
17
+ --axi-text-faint: #7f8aa0;
18
+ --axi-scrim: rgba(4, 6, 12, .66);
19
+ --axi-shadow-panel: 0 18px 44px rgba(0, 0, 0, .55), inset 0 1px 0 rgba(255, 255, 255, .14);
20
+ --axi-shadow-control: 0 6px 16px rgba(0, 0, 0, .45), inset 0 1px 0 rgba(255, 255, 255, .12);
21
+ --axi-shadow-panel-hover: 0 26px 60px rgba(0, 0, 0, .6), inset 0 1px 0 rgba(255, 255, 255, .2);
22
+ --axi-shadow-control-hover: 0 10px 24px rgba(0, 0, 0, .5), inset 0 1px 0 rgba(255, 255, 255, .18);
23
+ --axi-border-panel: 1px;
24
+ --axi-border-control: 1px;
25
+ --axi-border-hairline: 1px;
26
+ --axi-radius: 16px;
27
+ --axi-radius-sm: 10px;
11
28
  }
package/docs/RULES.md CHANGED
@@ -16,10 +16,11 @@ gridlines (a `repeating-linear-gradient` of hard stops, which is how N evenly
16
16
  spaced rules get drawn without asking every consumer to emit N empty divs).
17
17
  A gradient across a surface is still forbidden in every component file, and
18
18
  always will be. The single relief is a *theme* restating the surface tokens
19
- themselves — see [Themes](#themes) — which is what lets a glass theme exist
20
- without one component ever learning the word "glass". A component cannot reach
21
- for that exception, because it cannot see it: what it reads is the same surface
22
- token it was already reading.
19
+ themselves — and `--axi-ground-image`, the page's own light, which is declared
20
+ inert in `tokens.css` and read only by `body`; see [Themes](#themes). That is
21
+ what lets a glass theme exist without one component ever learning the word
22
+ "glass". A component cannot reach for that exception, because it cannot see it:
23
+ what it reads is the same surface token it was already reading.
23
24
 
24
25
  ## 2. No colour at partial opacity over the ground
25
26
 
@@ -28,13 +29,19 @@ just brown, and five muted inks over near-black are five browns. When something
28
29
  should be quieter, reach for a neutral from the ramp — that is what the ramp is
29
30
  for.
30
31
 
31
- A theme gets the same carve-out as rule 1 and not one inch more: it may hold the
32
- *surface* tokens at partial opacity, because a translucent surface is that
33
- surface's own definition rather than a colour laid over the ground. The inks are
34
- untouched. A muted `--axi-warn`, a faded accent, a status ink at 60% — still
35
- forbidden, in a theme exactly as in a component, because the paragraph above is
36
- about what happens to meaning when five inks become five browns, and changing
37
- which stylesheet does the muting does not change that.
32
+ A theme gets the same carve-out as rule 1: it may hold the *surface layer* at
33
+ partial opacity, because a translucent surface is that surface's own definition
34
+ rather than a colour laid over the ground. That layer is the two surfaces and
35
+ the lines that bound and divide them — `--axi-ink-line` and `--axi-rule` — for
36
+ the same reason: the edge of a translucent panel is as much the panel as its
37
+ face, and an opaque hairline around a pane of glass is a picture frame.
38
+
39
+ The five saturated inks are untouched. A muted `--axi-warn`, a faded accent, a
40
+ status ink at 60% — still forbidden, in a theme exactly as in a component,
41
+ because the paragraph above is about what happens to meaning when five inks
42
+ become five browns, and changing which stylesheet does the muting does not
43
+ change that. The neutral text ramp is likewise a theme's to restate but not to
44
+ fade: a theme picks where `--axi-text-dim` sits, it does not write text at 60%.
38
45
 
39
46
  ## 3. Every raised element is outlined and blocked
40
47
 
@@ -62,11 +69,20 @@ stops looking raised.
62
69
 
63
70
  There is exactly one element drawn in the line ink rather than on a surface,
64
71
  and it is named here so it stays an exception rather than becoming a habit:
65
- `.axi-tooltip` is filled with `--axi-ink-line` itself. A thing cannot be
66
- outlined in the colour it is already made of, and a block in that same ink
67
- under a box already made of it reads as the box being thicker rather than
68
- raised — so the tooltip takes a hairline in `--axi-rule` to hold its edge
69
- against the page, and carries no block. Nothing else may use that reasoning.
72
+ `.axi-tooltip` is filled with the line ink itself. A thing cannot be outlined
73
+ in the colour it is already made of, and a block in that same ink under a box
74
+ already made of it reads as the box being thicker rather than raised — so the
75
+ tooltip takes a hairline in `--axi-rule` to hold its edge against the page, and
76
+ carries no block. Nothing else may use that reasoning.
77
+
78
+ It spells that fill `--axi-ground-deep`, not `--axi-ink-line`, and so do the
79
+ two smaller shapes made of the same tone — the titlebar strip and the switch's
80
+ slug. The token holds the line ink today and is a different decision from it:
81
+ one is the colour a shape's edge is drawn in, the other the colour a shape is
82
+ filled with. A theme that outlines in a light colour so its page can go
83
+ near-black relights the first and leaves the second, and the tooltip stays a
84
+ dark box with light words on it instead of becoming a pale box with pale ones.
85
+ Anything else that comes to be made of this tone reads the same token.
70
86
 
71
87
  **What is mechanically enforced.** `tests/tokens.test.mjs` enforces both
72
88
  columns:
@@ -180,7 +196,7 @@ element becomes the containing block for any `position: fixed` descendant.
180
196
  An overlay positioned in viewport coordinates — `.axi-tooltip` is the one
181
197
  this language ships — must therefore live as a child of `<body>`, never
182
198
  inside the component it annotates, or the first hover lift re-anchors it.
183
- The class draws only the box (ink-line ground, hairline rule border, micro
199
+ The class draws only the box (`--axi-ground-deep` ground, hairline rule border, micro
184
200
  type); measuring the trigger and setting `left`/`top` is the consumer's
185
201
  half, and `gallery.js` is the reference wiring.
186
202
 
@@ -213,7 +229,7 @@ a third step in everything but name — and would put a thin frame around a
213
229
  heavier bar.
214
230
 
215
231
  A switch is the same rule in a slot. Its track fills to assert the setting's
216
- status and is empty otherwise; the slug that moves is `--axi-ink-line` in both
232
+ status and is empty otherwise; the slug that moves is `--axi-ground-deep` in both
217
233
  states, so on and off differ in what colour is *in* the slot and never in how
218
234
  bright the moving part is. It carries no block — a block belongs to things you
219
235
  press, and a switch is a slot with something sitting in it — but it keeps a
@@ -347,8 +363,8 @@ Three layers, in `src/tokens.css` — the only file permitted to contain a colou
347
363
  literal.
348
364
 
349
365
  - **Surface & text** — `--axi-ground`, `--axi-surface`, `--axi-surface-raised`,
350
- `--axi-ink-line`, `--axi-rule`, `--axi-text`, `--axi-text-dim`,
351
- `--axi-text-faint`, `--axi-scrim`
366
+ `--axi-ink-line`, `--axi-ground-deep`, `--axi-rule`, `--axi-text`,
367
+ `--axi-text-dim`, `--axi-text-faint`, `--axi-scrim`
352
368
  - **Accent & status** — `--axi-accent`, `--axi-accent-ink`, `--axi-meta`,
353
369
  `--axi-ok`, `--axi-warn`, `--axi-danger`. **This is the per-app override
354
370
  surface.** An app that sets `--axi-accent` and nothing else is correctly
@@ -465,13 +481,47 @@ main-theme token first.** A glass theme wants a `backdrop-filter`; it does not
465
481
  get to introduce one. `--axi-surface-filter` is declared in `tokens.css` with an
466
482
  inert default (`none`), the surfaces read it unconditionally, and the theme
467
483
  restates it. The main theme is unchanged in appearance and the hook is part of
468
- the language rather than part of the theme. Every theme pays this toll, and it
469
- is what keeps the mirror true: a token the theme could set that the main theme
470
- had never heard of is the first step back toward theme-only components.
471
-
472
- Rules 1 and 2 name the only relief a theme gets, and it is confined to the
473
- surface layer: a theme may put a gradient on a surface and may hold a surface
474
- token at partial opacity. It may not mute an ink. Read those two rules for why.
484
+ the language rather than part of the theme. `--axi-ground-image` is the second
485
+ of these, on the same terms: the page's light, inert at `none`, read by `body`
486
+ and nowhere else. Every theme pays this toll, and it is what keeps the mirror
487
+ true: a token the theme could set that the main theme had never heard of is the
488
+ first step back toward theme-only components.
489
+
490
+ ### What a theme may restate
491
+
492
+ Rules 1 and 2 name the relief a theme gets in the surface layer: a theme may
493
+ put a gradient on a surface and may hold a surface token at partial opacity. It
494
+ may not mute an ink. Read those two rules for why.
495
+
496
+ The relief is not confined to that layer, though, and the first draft of this
497
+ section said it was. **A theme may also restate the block and the corner** —
498
+ `--axi-shadow-*`, `--axi-border-*`, `--axi-radius` and `--axi-radius-sm`. Rule 3
499
+ requires that a raised element read as raised and an outlined one as outlined.
500
+ A hard offset block in the line ink is how the main theme answers that; a soft
501
+ drop with an inset top highlight is a different answer to the same question,
502
+ and a translucent panel wearing an opaque theme's block does not look like
503
+ glass, it looks like a bug. The block and the corner are how a theme says what
504
+ its material is.
505
+
506
+ What is **not** a theme's to restate, and each for its own reason:
507
+
508
+ - **The five saturated fills.** `--axi-accent`, `--axi-meta`, `--axi-ok`,
509
+ `--axi-warn`, `--axi-danger` carry meaning (rules 5, 6, 9, 10). Their `-ink`
510
+ companions are not on this list: `--axi-accent-ink` and `--axi-ink-on-fill`
511
+ are the colour a word is written in when it sits on a fill, and a theme that
512
+ lightens the outline has to be able to hold them dark.
513
+ - **The offset scale.** `--axi-offset-*` is what the main theme's block is
514
+ composed *from*. A theme restates the composed `--axi-shadow-*` wholesale or
515
+ leaves it; retuning the offsets underneath would leave the two spellings of
516
+ "the block" disagreeing.
517
+ - **The measure.** `--axi-page`, `--axi-gutter`. Where the text wraps is not a
518
+ look, and moving it would reflow every page rather than repaint it.
519
+ - **The type.** `--axi-sans`, `--axi-mono`, `--axi-t-*`, `--axi-ls-*`. The same
520
+ call, harder: the type scale is the voice.
521
+
522
+ The line to hold is the one-for-one rule above, not a list of layers. A theme
523
+ that restates the block still paints every component; a theme that invents one
524
+ does not.
475
525
 
476
526
  **What is mechanically enforced.** `tests/themes.test.mjs` reads every
477
527
  `dist/themes/*.css` and checks the mirror rather than trusting it: the file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axiapps/axi-design",
3
- "version": "1.11.0",
3
+ "version": "1.13.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/base.css CHANGED
@@ -11,7 +11,13 @@ html { color-scheme: dark; }
11
11
 
12
12
  body {
13
13
  margin: 0;
14
- background: var(--axi-ground);
14
+ /* Longhands rather than the shorthand: the ground is a colour and the light
15
+ over it is an image, and `background:` would make setting either one
16
+ reset the other. Fixed so a theme's light stays put as the page scrolls
17
+ instead of stretching to the document's height. */
18
+ background-color: var(--axi-ground);
19
+ background-image: var(--axi-ground-image);
20
+ background-attachment: fixed;
15
21
  color: var(--axi-text);
16
22
  font: var(--axi-t-body);
17
23
  -webkit-font-smoothing: antialiased;
@@ -149,7 +149,7 @@
149
149
  top: 2px; bottom: 2px; left: 2px;
150
150
  width: var(--axi-switch-knob, 16px);
151
151
  border-radius: var(--axi-radius-sm);
152
- background: var(--axi-ink-line);
152
+ background: var(--axi-ground-deep);
153
153
  transition: transform .1s;
154
154
  }
155
155
  /* The travel is the track's inner width less the slug and its two insets.
package/src/shells.css CHANGED
@@ -140,7 +140,7 @@
140
140
  argument with one. */
141
141
  z-index: 70;
142
142
  padding: 5px 8px;
143
- background: var(--axi-ink-line);
143
+ background: var(--axi-ground-deep);
144
144
  color: var(--axi-text);
145
145
  border: var(--axi-border-hairline) solid var(--axi-rule);
146
146
  border-radius: var(--axi-radius-sm);
@@ -289,7 +289,7 @@
289
289
  flex: none;
290
290
  display: flex; align-items: center; gap: 12px;
291
291
  height: 38px; padding-left: 14px;
292
- background: var(--axi-ink-line);
292
+ background: var(--axi-ground-deep);
293
293
  border-bottom: var(--axi-border-control) solid var(--axi-ink-line);
294
294
  font: var(--axi-t-micro);
295
295
  letter-spacing: var(--axi-ls-micro);
package/src/tokens.css CHANGED
@@ -12,9 +12,32 @@
12
12
  :root {
13
13
  /* --- surface & text --- */
14
14
  --axi-ground: #15181d; /* the page itself */
15
+ /* An image layer painted over the ground colour, on the page and nowhere
16
+ else. The second toll the Themes section describes, and it exists for the
17
+ same reason --axi-surface-filter does: a glass theme wants the page lit
18
+ from a few directions, and it does not get to introduce a gradient any
19
+ more than it got to introduce a blur. Declared here inert (`none`), read
20
+ unconditionally by body, and restated by the theme.
21
+
22
+ Why a second token rather than letting a theme put the gradient in
23
+ --axi-ground itself: --axi-ground is read as `background-color` by the
24
+ plot and the select, where an image value is invalid and drops out, and
25
+ as the flat fill of the masthead, the tooltip row and a dozen inset
26
+ shapes, where a page-positioned radial would render as an arbitrary
27
+ wedge of colour. The ground stays a colour. This is the page's light. */
28
+ --axi-ground-image: none;
15
29
  --axi-surface: #222731; /* a panel raised off the page */
16
30
  --axi-surface-raised: #2b313d; /* a chip or popover raised off a panel */
17
31
  --axi-ink-line: #0c0e12; /* the outline every raised element is drawn with */
32
+ /* The tone a shape is made of when it is made of the line ink rather than
33
+ outlined in it - the tooltip, the titlebar strip, the switch's slug. Holds
34
+ --axi-ink-line today, which is why it reads as the same decision and is
35
+ not: one is the colour a shape's EDGE is drawn in, the other the colour a
36
+ shape is FILLED with. They only have to diverge once - a theme that
37
+ outlines in a light colour so its page can go near-black - for the
38
+ conflated spelling to turn the tooltip into a pale box. Same split as
39
+ --axi-ink-on-fill below, on the other side of the same token. */
40
+ --axi-ground-deep: var(--axi-ink-line);
18
41
  --axi-rule: #3a4250; /* internal rules, inside an outlined panel */
19
42
  --axi-text: #f4f6f9;
20
43
  --axi-text-dim: #a7b0be;