@axiapps/axi-design 1.16.0 → 1.17.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,9 @@ 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-well-pad` | the padding inside an `.axi-well` | `10px` | `<div class="axi-well" style="--axi-well-pad: 18px">` |
142
+ | `--axi-rail-w` | an `.axi-rail`'s width, for labels longer than the default holds | `208px` | `<aside class="axi-rail" style="--axi-rail-w: 260px">` |
143
+ | `--axi-rail-pad` | the padding inside an `.axi-rail` | `10px` | `<aside class="axi-rail" style="--axi-rail-pad: 6px">` |
141
144
  | `--axi-avatar-size` | the size of an `.axi-avatar` square | `40px` | `<span class="axi-avatar" style="--axi-avatar-size: 28px">MS</span>` |
142
145
  | `--axi-modal-width` | the maximum width of an `.axi-modal`, before the viewport clamp | `560px` | `<dialog class="axi-modal" style="--axi-modal-width: 760px">` |
143
146
  | `--axi-card-strip` | the colour of `.axi-card--strip`'s top strip | `var(--axi-accent)` | `<a class="axi-card axi-card--strip" style="--axi-card-strip: var(--axi-ok)">` |
package/dist/axi.css CHANGED
@@ -43,6 +43,28 @@
43
43
  --axi-ink-on-fill below, on the other side of the same token. */
44
44
  --axi-ground-deep: var(--axi-ink-line);
45
45
  --axi-rule: #3a4250; /* internal rules, inside an outlined panel */
46
+ /* --- the well --- */
47
+ /* A field sunk INTO a surface rather than raised off one: a table picker's
48
+ list, a bar's trough, a column of cards standing on the page. The language
49
+ had no name for it, and every consumer that needed one reached for a second
50
+ .axi-panel inside the first - two identical fills parted by a line, which
51
+ reads as one field with a stray rule through it.
52
+
53
+ Two tokens because a well is the exact inverse of a panel on both counts.
54
+ The fill is a SUBTRACTION from whatever is behind it, not a colour of its
55
+ own: spelled as --axi-ground it is right in this theme, where surfaces are
56
+ opaque and the page really is what shows through a hole in one, and wrong
57
+ in a glass theme, where an opaque page colour inside a translucent pane is
58
+ a black patch rather than a recess. A theme restates it as a darkening at
59
+ alpha and the panel's own tint carries through.
60
+
61
+ The line is --axi-rule and not --axi-ink-line, because ink only reads
62
+ against a fill lighter than itself. Every outline in this language is
63
+ near-black on the assumption of a ground the lines can be darker than; a
64
+ well IS at ground level, so an ink edge round one is a black line on a
65
+ black field. Raised takes ink, sunk takes the rule. */
66
+ --axi-well-fill: var(--axi-ground);
67
+ --axi-well-line: var(--axi-rule);
46
68
  --axi-text: #f4f6f9;
47
69
  --axi-text-dim: #a7b0be;
48
70
  --axi-text-faint: #7c8695;
@@ -251,6 +273,22 @@ a { color: inherit; }
251
273
  padding: var(--axi-panel-pad, 26px);
252
274
  }
253
275
 
276
+ /* ---------- well ---------- */
277
+ /* The panel's inverse: a field sunk into the surface around it rather than
278
+ raised off it. No block, because a recess casts nothing, and the rule rather
279
+ than the ink for its edge - see --axi-well-fill in src/tokens.css for why
280
+ both of those follow from the same fact. Reach for this wherever a second
281
+ panel inside a panel was the instinct: a picker list beside a table, a column
282
+ of cards standing on the page, a trough a bar fills. */
283
+ .axi-well {
284
+ background: var(--axi-well-fill);
285
+ border: var(--axi-border-control) solid var(--axi-well-line);
286
+ border-radius: var(--axi-radius);
287
+ /* Tighter than a panel's 26px: a well is a container for objects that carry
288
+ their own padding, not a page margin. */
289
+ padding: var(--axi-well-pad, 10px);
290
+ }
291
+
254
292
  /* ---------- button ---------- */
255
293
  .axi-btn {
256
294
  display: inline-flex; align-items: center; gap: 8px;
@@ -935,6 +973,107 @@ textarea.axi-input {
935
973
  }
936
974
  .axi-tabs .axi-tabs__close:hover { color: var(--axi-text); border-color: transparent; }
937
975
 
976
+ /* ---------- rail ---------- */
977
+ /* Vertical navigation, for a set too large for a tab strip to hold. Same
978
+ grammar as .axi-tabs deliberately - the item you are on is filled with the
979
+ accent and blocked, the rest are transparent until hovered - because "where
980
+ am I" is one question and should have one answer however the app happens to
981
+ be laying it out.
982
+
983
+ What a rail adds over a strip is a second level. A category is a place and
984
+ takes the fill; a section under it is where the page is scrolled to, which is
985
+ a smaller claim and is drawn as one: brightened text against the dim list
986
+ around it, no fill, no edge. Two fills at two levels would leave the reader
987
+ deciding which of them is the answer.
988
+
989
+ --axi-rail-w is declared nowhere and read with a fallback, i.e. it is the hook
990
+ a consumer overrides to widen the rail for longer labels. */
991
+ .axi-rail {
992
+ display: flex;
993
+ flex-direction: column;
994
+ width: var(--axi-rail-w, 208px);
995
+ flex: none;
996
+ overflow-y: auto;
997
+ padding: var(--axi-rail-pad, 10px);
998
+ background: var(--axi-surface);
999
+ backdrop-filter: var(--axi-surface-filter);
1000
+ border: var(--axi-border-panel) solid var(--axi-ink-line);
1001
+ border-radius: var(--axi-radius);
1002
+ }
1003
+ /* A rail that IS the edge of the page rather than a panel standing in the
1004
+ layout - pinned to the viewport, or a drawer's contents. It keeps the one
1005
+ border that faces the content and drops everything that says "this is an
1006
+ object with space around it": no radius on the flush side, and no block,
1007
+ because a block needs somewhere to fall.
1008
+
1009
+ The block is stated as what a free-standing rail has rather than as
1010
+ something the flush one turns off, so there is exactly one box-shadow here
1011
+ and it names the panel block. `box-shadow: none` would be a second spelling
1012
+ of the raise for a theme to miss. */
1013
+ .axi-rail:not(.axi-rail--flush) { box-shadow: var(--axi-shadow-panel); }
1014
+ .axi-rail--flush {
1015
+ border: 0;
1016
+ border-right: var(--axi-border-panel) solid var(--axi-ink-line);
1017
+ border-radius: 0;
1018
+ }
1019
+ .axi-rail__nav { display: flex; flex-direction: column; gap: 2px; }
1020
+ .axi-rail__item {
1021
+ display: flex;
1022
+ align-items: center;
1023
+ gap: 10px;
1024
+ width: 100%;
1025
+ padding: 8px 10px;
1026
+ font: 800 12.5px/1 var(--axi-sans);
1027
+ color: var(--axi-text-dim);
1028
+ text-align: left;
1029
+ background: transparent;
1030
+ border: var(--axi-border-control) solid transparent;
1031
+ border-radius: var(--axi-radius-sm);
1032
+ cursor: pointer;
1033
+ }
1034
+ .axi-rail__item:hover { color: var(--axi-text); background: var(--axi-surface-raised); }
1035
+ /* The same treatment a pressed pill and a current tab get. Only ever one of
1036
+ these on screen. Any aria-current value, because the right one depends on
1037
+ what the rail navigates - `page` for a view, `location` for a place within
1038
+ one - and the fill means the same thing either way. */
1039
+ .axi-rail__item[aria-current],
1040
+ .axi-rail__item[aria-current]:hover {
1041
+ background: var(--axi-accent);
1042
+ color: var(--axi-accent-ink);
1043
+ border-color: var(--axi-ink-line);
1044
+ box-shadow: var(--axi-shadow-control);
1045
+ }
1046
+ /* An icon beside a label is a label for it, not a second accent - so it takes
1047
+ the faint tone, and inside a filled item it follows the item's ink. */
1048
+ .axi-rail__item .axi-icon { color: var(--axi-text-faint); }
1049
+ .axi-rail__item[aria-current] .axi-icon { color: var(--axi-accent-ink); }
1050
+ /* The second level, indented under the item it belongs to. */
1051
+ .axi-rail__sub { display: flex; flex-direction: column; margin: 3px 0 5px 10px; }
1052
+ .axi-rail__subitem {
1053
+ padding: 5px 8px;
1054
+ font: 700 11.5px/1.3 var(--axi-sans);
1055
+ text-align: left;
1056
+ color: var(--axi-text-faint);
1057
+ background: transparent;
1058
+ border: 0;
1059
+ border-radius: var(--axi-radius-sm);
1060
+ cursor: pointer;
1061
+ }
1062
+ .axi-rail__subitem:hover { color: var(--axi-text-dim); background: var(--axi-surface-raised); }
1063
+ .axi-rail__subitem[aria-current] { color: var(--axi-text); }
1064
+ /* An annotation on an item, not a measurement of it, so rule 5 draws it
1065
+ outlined rather than filled. */
1066
+ .axi-rail__mark {
1067
+ margin-left: auto;
1068
+ padding: 2px 5px;
1069
+ font: var(--axi-t-micro);
1070
+ letter-spacing: .04em;
1071
+ text-transform: uppercase;
1072
+ color: var(--axi-meta);
1073
+ border: var(--axi-border-hairline) solid var(--axi-meta);
1074
+ border-radius: var(--axi-radius-sm);
1075
+ }
1076
+
938
1077
  /* ---------- notice ---------- */
939
1078
  .axi-notice {
940
1079
  display: flex; gap: 14px; padding: 15px 18px;
@@ -10,6 +10,7 @@
10
10
  --axi-ink-line: rgba(180, 200, 255, .16);
11
11
  --axi-rule: rgba(180, 200, 255, .14);
12
12
  --axi-ground-deep: #0c0e12;
13
+ --axi-well-fill: rgba(6, 8, 14, .32);
13
14
  --axi-accent-ink: #0c0e12;
14
15
  --axi-ink-on-fill: #0c0e12;
15
16
  --axi-text: #f2f5fb;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axiapps/axi-design",
3
- "version": "1.16.0",
3
+ "version": "1.17.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",
@@ -29,6 +29,22 @@
29
29
  padding: var(--axi-panel-pad, 26px);
30
30
  }
31
31
 
32
+ /* ---------- well ---------- */
33
+ /* The panel's inverse: a field sunk into the surface around it rather than
34
+ raised off it. No block, because a recess casts nothing, and the rule rather
35
+ than the ink for its edge - see --axi-well-fill in src/tokens.css for why
36
+ both of those follow from the same fact. Reach for this wherever a second
37
+ panel inside a panel was the instinct: a picker list beside a table, a column
38
+ of cards standing on the page, a trough a bar fills. */
39
+ .axi-well {
40
+ background: var(--axi-well-fill);
41
+ border: var(--axi-border-control) solid var(--axi-well-line);
42
+ border-radius: var(--axi-radius);
43
+ /* Tighter than a panel's 26px: a well is a container for objects that carry
44
+ their own padding, not a page margin. */
45
+ padding: var(--axi-well-pad, 10px);
46
+ }
47
+
32
48
  /* ---------- button ---------- */
33
49
  .axi-btn {
34
50
  display: inline-flex; align-items: center; gap: 8px;
package/src/shells.css CHANGED
@@ -109,6 +109,107 @@
109
109
  }
110
110
  .axi-tabs .axi-tabs__close:hover { color: var(--axi-text); border-color: transparent; }
111
111
 
112
+ /* ---------- rail ---------- */
113
+ /* Vertical navigation, for a set too large for a tab strip to hold. Same
114
+ grammar as .axi-tabs deliberately - the item you are on is filled with the
115
+ accent and blocked, the rest are transparent until hovered - because "where
116
+ am I" is one question and should have one answer however the app happens to
117
+ be laying it out.
118
+
119
+ What a rail adds over a strip is a second level. A category is a place and
120
+ takes the fill; a section under it is where the page is scrolled to, which is
121
+ a smaller claim and is drawn as one: brightened text against the dim list
122
+ around it, no fill, no edge. Two fills at two levels would leave the reader
123
+ deciding which of them is the answer.
124
+
125
+ --axi-rail-w is declared nowhere and read with a fallback, i.e. it is the hook
126
+ a consumer overrides to widen the rail for longer labels. */
127
+ .axi-rail {
128
+ display: flex;
129
+ flex-direction: column;
130
+ width: var(--axi-rail-w, 208px);
131
+ flex: none;
132
+ overflow-y: auto;
133
+ padding: var(--axi-rail-pad, 10px);
134
+ background: var(--axi-surface);
135
+ backdrop-filter: var(--axi-surface-filter);
136
+ border: var(--axi-border-panel) solid var(--axi-ink-line);
137
+ border-radius: var(--axi-radius);
138
+ }
139
+ /* A rail that IS the edge of the page rather than a panel standing in the
140
+ layout - pinned to the viewport, or a drawer's contents. It keeps the one
141
+ border that faces the content and drops everything that says "this is an
142
+ object with space around it": no radius on the flush side, and no block,
143
+ because a block needs somewhere to fall.
144
+
145
+ The block is stated as what a free-standing rail has rather than as
146
+ something the flush one turns off, so there is exactly one box-shadow here
147
+ and it names the panel block. `box-shadow: none` would be a second spelling
148
+ of the raise for a theme to miss. */
149
+ .axi-rail:not(.axi-rail--flush) { box-shadow: var(--axi-shadow-panel); }
150
+ .axi-rail--flush {
151
+ border: 0;
152
+ border-right: var(--axi-border-panel) solid var(--axi-ink-line);
153
+ border-radius: 0;
154
+ }
155
+ .axi-rail__nav { display: flex; flex-direction: column; gap: 2px; }
156
+ .axi-rail__item {
157
+ display: flex;
158
+ align-items: center;
159
+ gap: 10px;
160
+ width: 100%;
161
+ padding: 8px 10px;
162
+ font: 800 12.5px/1 var(--axi-sans);
163
+ color: var(--axi-text-dim);
164
+ text-align: left;
165
+ background: transparent;
166
+ border: var(--axi-border-control) solid transparent;
167
+ border-radius: var(--axi-radius-sm);
168
+ cursor: pointer;
169
+ }
170
+ .axi-rail__item:hover { color: var(--axi-text); background: var(--axi-surface-raised); }
171
+ /* The same treatment a pressed pill and a current tab get. Only ever one of
172
+ these on screen. Any aria-current value, because the right one depends on
173
+ what the rail navigates - `page` for a view, `location` for a place within
174
+ one - and the fill means the same thing either way. */
175
+ .axi-rail__item[aria-current],
176
+ .axi-rail__item[aria-current]:hover {
177
+ background: var(--axi-accent);
178
+ color: var(--axi-accent-ink);
179
+ border-color: var(--axi-ink-line);
180
+ box-shadow: var(--axi-shadow-control);
181
+ }
182
+ /* An icon beside a label is a label for it, not a second accent - so it takes
183
+ the faint tone, and inside a filled item it follows the item's ink. */
184
+ .axi-rail__item .axi-icon { color: var(--axi-text-faint); }
185
+ .axi-rail__item[aria-current] .axi-icon { color: var(--axi-accent-ink); }
186
+ /* The second level, indented under the item it belongs to. */
187
+ .axi-rail__sub { display: flex; flex-direction: column; margin: 3px 0 5px 10px; }
188
+ .axi-rail__subitem {
189
+ padding: 5px 8px;
190
+ font: 700 11.5px/1.3 var(--axi-sans);
191
+ text-align: left;
192
+ color: var(--axi-text-faint);
193
+ background: transparent;
194
+ border: 0;
195
+ border-radius: var(--axi-radius-sm);
196
+ cursor: pointer;
197
+ }
198
+ .axi-rail__subitem:hover { color: var(--axi-text-dim); background: var(--axi-surface-raised); }
199
+ .axi-rail__subitem[aria-current] { color: var(--axi-text); }
200
+ /* An annotation on an item, not a measurement of it, so rule 5 draws it
201
+ outlined rather than filled. */
202
+ .axi-rail__mark {
203
+ margin-left: auto;
204
+ padding: 2px 5px;
205
+ font: var(--axi-t-micro);
206
+ letter-spacing: .04em;
207
+ text-transform: uppercase;
208
+ color: var(--axi-meta);
209
+ border: var(--axi-border-hairline) solid var(--axi-meta);
210
+ border-radius: var(--axi-radius-sm);
211
+ }
212
+
112
213
  /* ---------- notice ---------- */
113
214
  .axi-notice {
114
215
  display: flex; gap: 14px; padding: 15px 18px;
package/src/tokens.css CHANGED
@@ -39,6 +39,28 @@
39
39
  --axi-ink-on-fill below, on the other side of the same token. */
40
40
  --axi-ground-deep: var(--axi-ink-line);
41
41
  --axi-rule: #3a4250; /* internal rules, inside an outlined panel */
42
+ /* --- the well --- */
43
+ /* A field sunk INTO a surface rather than raised off one: a table picker's
44
+ list, a bar's trough, a column of cards standing on the page. The language
45
+ had no name for it, and every consumer that needed one reached for a second
46
+ .axi-panel inside the first - two identical fills parted by a line, which
47
+ reads as one field with a stray rule through it.
48
+
49
+ Two tokens because a well is the exact inverse of a panel on both counts.
50
+ The fill is a SUBTRACTION from whatever is behind it, not a colour of its
51
+ own: spelled as --axi-ground it is right in this theme, where surfaces are
52
+ opaque and the page really is what shows through a hole in one, and wrong
53
+ in a glass theme, where an opaque page colour inside a translucent pane is
54
+ a black patch rather than a recess. A theme restates it as a darkening at
55
+ alpha and the panel's own tint carries through.
56
+
57
+ The line is --axi-rule and not --axi-ink-line, because ink only reads
58
+ against a fill lighter than itself. Every outline in this language is
59
+ near-black on the assumption of a ground the lines can be darker than; a
60
+ well IS at ground level, so an ink edge round one is a black line on a
61
+ black field. Raised takes ink, sunk takes the rule. */
62
+ --axi-well-fill: var(--axi-ground);
63
+ --axi-well-line: var(--axi-rule);
42
64
  --axi-text: #f4f6f9;
43
65
  --axi-text-dim: #a7b0be;
44
66
  --axi-text-faint: #7c8695;