@wtfalch/design 0.6.0 → 0.8.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.
Files changed (59) hide show
  1. package/dist/components/Button.d.ts +19 -27
  2. package/dist/components/Button.js +61 -1
  3. package/dist/components/Callout.d.ts +0 -21
  4. package/dist/components/Callout.js +7 -0
  5. package/dist/components/Card.js +7 -0
  6. package/dist/components/Checkbox.d.ts +0 -31
  7. package/dist/components/Checkbox.js +7 -0
  8. package/dist/components/Command.js +10 -2
  9. package/dist/components/DangerZone.js +7 -0
  10. package/dist/components/Dialog.d.ts +0 -21
  11. package/dist/components/Dialog.js +7 -0
  12. package/dist/components/Field.d.ts +22 -50
  13. package/dist/components/Field.js +18 -9
  14. package/dist/components/Identity.d.ts +0 -25
  15. package/dist/components/Identity.js +7 -0
  16. package/dist/components/Input.d.ts +10 -0
  17. package/dist/components/Input.js +31 -3
  18. package/dist/components/Kbd.d.ts +7 -0
  19. package/dist/components/Kbd.js +34 -0
  20. package/dist/components/Markdown.js +7 -0
  21. package/dist/components/Menu.js +10 -2
  22. package/dist/components/Modal.d.ts +0 -33
  23. package/dist/components/Modal.js +7 -0
  24. package/dist/components/Pagination.d.ts +0 -33
  25. package/dist/components/Pagination.js +7 -0
  26. package/dist/components/Popover.js +7 -0
  27. package/dist/components/Rows.js +7 -0
  28. package/dist/components/ScrollArea.js +7 -0
  29. package/dist/components/Select.d.ts +8 -1
  30. package/dist/components/Select.js +34 -5
  31. package/dist/components/Shell.d.ts +35 -0
  32. package/dist/components/Shell.js +26 -0
  33. package/dist/components/SizeGrid.js +7 -0
  34. package/dist/components/Slider.js +7 -0
  35. package/dist/components/SplitPane.js +7 -0
  36. package/dist/components/Stat.d.ts +45 -0
  37. package/dist/components/Stat.js +31 -0
  38. package/dist/components/Tabs.d.ts +21 -0
  39. package/dist/components/Tabs.js +8 -1
  40. package/dist/components/Textarea.js +14 -4
  41. package/dist/components/ThemeSwitch.d.ts +19 -0
  42. package/dist/components/ThemeSwitch.js +65 -0
  43. package/dist/components/Toast.d.ts +24 -18
  44. package/dist/components/Toast.js +44 -3
  45. package/dist/components/Toggle.d.ts +0 -30
  46. package/dist/components/Toggle.js +7 -0
  47. package/dist/components/Tooltip.js +7 -0
  48. package/dist/components/Tour.d.ts +0 -23
  49. package/dist/components/Tour.js +7 -0
  50. package/dist/components/fieldWiring.d.ts +44 -0
  51. package/dist/components/fieldWiring.js +30 -0
  52. package/dist/index.d.ts +23 -3
  53. package/dist/index.js +17 -1
  54. package/dist/styles/index.css +259 -13
  55. package/dist/tf.css +259 -13
  56. package/dist/themes/choice.d.ts +71 -0
  57. package/dist/themes/choice.js +81 -0
  58. package/dist/valet.css +259 -13
  59. package/package.json +2 -1
@@ -93,7 +93,28 @@ body::before {
93
93
  where the flat grey reads as recessed rather than as something to use. It is
94
94
  derived rather than declared -- a new token would have to be added to every
95
95
  theme in `themes.ts`, and a theme that missed it would lose its controls. */
96
+ /* An anchor drawn as a button. Three things the element brings that a
97
+ `<button>` does not, and all three have to go: the underline, the link
98
+ colour, and the text alignment a block anchor inherits. `cursor: pointer`
99
+ it already has. Disabled is `aria-disabled` here -- an anchor ignores the
100
+ native attribute -- so every `:disabled` rule above has an
101
+ `[aria-disabled='true']` twin rather than a rewrite, which leaves each
102
+ `button` selector at the specificity it had and every committed baseline
103
+ untouched. */
104
+ a.btn {
105
+ text-decoration: none;
106
+ color: var(--text);
107
+ text-align: center;
108
+ }
109
+
110
+ a.btn[aria-disabled='true'] {
111
+ /* A disabled anchor still takes the pointer, so say so. The click itself is
112
+ blocked in the capture phase by the component. */
113
+ cursor: not-allowed;
114
+ }
115
+
96
116
  button,
117
+ a.btn,
97
118
  input,
98
119
  textarea,
99
120
  select {
@@ -117,7 +138,8 @@ select {
117
138
  the icon; the button simply had no opinion about how to arrange two things.
118
139
  `inline-flex` and a gap gives it one, and the gap replaces the space
119
140
  character people were putting between an icon and its label. */
120
- button {
141
+ button,
142
+ a.btn {
121
143
  display: inline-flex;
122
144
  align-items: center;
123
145
  justify-content: center;
@@ -143,7 +165,8 @@ button[aria-pressed='true']:hover:not(:disabled) { border-color: var(--accent);
143
165
 
144
166
  /* The glyph never shrinks: in a narrow row flexbox would take it from the icon
145
167
  before the text, and half an icon is not a smaller icon. */
146
- button > svg { flex: none; }
168
+ button > svg,
169
+ a.btn > svg { flex: none; }
147
170
 
148
171
  .size-sm { font-size: var(--text-xs); padding: var(--space-1) var(--space-2); }
149
172
 
@@ -169,6 +192,7 @@ input, textarea, select { width: 100%; }
169
192
  textarea { resize: none; }
170
193
 
171
194
  button:hover:not(:disabled),
195
+ a.btn:hover:not([aria-disabled='true']),
172
196
  input:hover:not(:disabled),
173
197
  textarea:hover:not(:disabled),
174
198
  select:hover:not(:disabled) { border-color: var(--accent); }
@@ -177,6 +201,7 @@ select:hover:not(:disabled) { border-color: var(--accent); }
177
201
  `:focus` on the fields because a focused field is a place you are typing and
178
202
  should say so however you got there. */
179
203
  button:focus-visible,
204
+ a.btn:focus-visible,
180
205
  input:focus,
181
206
  textarea:focus,
182
207
  select:focus { outline: none; border-color: var(--accent); }
@@ -270,11 +295,13 @@ input[type='checkbox']:disabled,
270
295
  input[type='radio']:disabled { cursor: not-allowed; background: var(--panel-2); }
271
296
 
272
297
  button:disabled,
298
+ a.btn[aria-disabled='true'],
273
299
  input:disabled,
274
300
  textarea:disabled,
275
301
  select:disabled { opacity: 0.45; cursor: not-allowed; }
276
302
 
277
- button.primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
303
+ button.primary,
304
+ a.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
278
305
 
279
306
  /* A disabled primary is not a faded primary.
280
307
  `opacity: .45` was the whole of it, and a coloured fill at 45% is still a
@@ -282,7 +309,8 @@ button.primary { background: var(--accent); border-color: var(--accent); color:
282
309
  thing on the panel. That got worse when onboarding started holding people at
283
310
  a disabled Next. So it gives up the fill entirely and becomes the shape of a
284
311
  disabled control -- the opacity then has nothing left to do. */
285
- button.primary:disabled {
312
+ button.primary:disabled,
313
+ a.btn.primary[aria-disabled='true'] {
286
314
  background: var(--panel-2);
287
315
  border-color: var(--border);
288
316
  color: var(--muted);
@@ -352,13 +380,15 @@ button[aria-busy='true']::after {
352
380
 
353
381
  /* Quiet: no border, no fill. For the action beside the action -- Cancel next to
354
382
  Save -- where a bordered box competes with the thing it is next to. */
355
- button.ghost {
383
+ button.ghost,
384
+ a.btn.ghost {
356
385
  background: transparent;
357
386
  border-color: transparent;
358
387
  color: var(--muted);
359
388
  }
360
389
 
361
- button.ghost:hover:not(:disabled) {
390
+ button.ghost:hover:not(:disabled),
391
+ a.btn.ghost:hover:not([aria-disabled='true']) {
362
392
  background: var(--panel-2);
363
393
  border-color: transparent;
364
394
  color: var(--text);
@@ -565,19 +595,22 @@ button.ghost:hover:not(:disabled) {
565
595
  /* `:where(button)` so this carries no specificity of its own: a component's
566
596
  press rule below -- a card, a tab, a rail item, all of them buttons --
567
597
  wins by coming later, instead of every one having to restate `button`. */
568
- :where(button):active:not(:disabled) {
598
+ :where(button):active:not(:disabled),
599
+ :where(a.btn):active:not([aria-disabled='true']) {
569
600
  background: color-mix(in srgb, var(--text) var(--press-ink), var(--control));
570
601
  border-color: var(--accent);
571
602
  transform: scale(var(--press-scale));
572
603
  transition: none;
573
604
  }
574
605
 
575
- button.primary:active:not(:disabled) {
606
+ button.primary:active:not(:disabled),
607
+ a.btn.primary:active:not([aria-disabled='true']) {
576
608
  background: color-mix(in srgb, var(--text) var(--press-ink), var(--accent));
577
609
  border-color: color-mix(in srgb, var(--text) var(--press-ink), var(--accent));
578
610
  }
579
611
 
580
- button.ghost:active:not(:disabled) {
612
+ button.ghost:active:not(:disabled),
613
+ a.btn.ghost:active:not([aria-disabled='true']) {
581
614
  background: color-mix(in srgb, var(--text) var(--press-ink), var(--panel-2));
582
615
  border-color: transparent;
583
616
  }
@@ -800,7 +833,8 @@ input:active, textarea:active, select:active { transform: none; }
800
833
  /* Full width, for the one action a card or a step is about. Not a size: a
801
834
  `block` button is the same height as any other, it just stops being a thing
802
835
  you have to aim at. */
803
- button.block { display: flex; width: 100%; }
836
+ button.block,
837
+ a.btn.block { display: flex; width: 100%; }
804
838
 
805
839
  /* The default is `size-md`, spelled once for every control.
806
840
  The paragraph above promised one scale for buttons, inputs and selects, and
@@ -809,6 +843,7 @@ button.block { display: flex; width: 100%; }
809
843
  could line up. The download modal's search box against its Search button was
810
844
  the version you could see from across the room. */
811
845
  button,
846
+ a.btn,
812
847
  input,
813
848
  textarea,
814
849
  select,
@@ -959,18 +994,21 @@ select,
959
994
 
960
995
  /* ---- ./dangerzone.css ---- */
961
996
  /* Destructive, and legible as such standing still. */
962
- button.danger {
997
+ button.danger,
998
+ a.btn.danger {
963
999
  color: var(--bad);
964
1000
  border-color: color-mix(in srgb, var(--bad) 45%, var(--border-strong));
965
1001
  }
966
1002
 
967
- button.danger:hover:not(:disabled) {
1003
+ button.danger:hover:not(:disabled),
1004
+ a.btn.danger:hover:not([aria-disabled='true']) {
968
1005
  background: color-mix(in srgb, var(--bad) 12%, var(--panel));
969
1006
  border-color: var(--bad);
970
1007
  color: var(--bad);
971
1008
  }
972
1009
 
973
- button.danger:active:not(:disabled) {
1010
+ button.danger:active:not(:disabled),
1011
+ a.btn.danger:active:not([aria-disabled='true']) {
974
1012
  background: color-mix(in srgb, var(--bad) 20%, var(--panel));
975
1013
  border-color: var(--bad);
976
1014
  }
@@ -3080,6 +3118,70 @@ input[type='range'].slider:disabled::-moz-range-thumb { opacity: 0; }
3080
3118
 
3081
3119
  .secret-lg > .secret-eye { padding: var(--space-2); }
3082
3120
 
3121
+ /* An input with something inside its edges: a glyph on the left, a hint or a
3122
+ clear button on the right. Same arrangement as `.secret` above, and for the
3123
+ same reason -- one grid cell with everything stacked in it, and the room
3124
+ reserved in the input's own padding, so nothing is positioned against a
3125
+ height that changes with `size`. */
3126
+ .adorned { display: grid; align-items: center; }
3127
+
3128
+ .adorned > input,
3129
+ .adorned > .adorned-mark,
3130
+ .adorned > .adorned-end { grid-area: 1 / 1; }
3131
+
3132
+ .adorned.block { width: 100%; }
3133
+
3134
+ .adorned > .adorned-mark {
3135
+ justify-self: start;
3136
+ margin-left: var(--space-2);
3137
+ color: var(--muted);
3138
+ /* The glyph is decoration over the box; clicks belong to the input under
3139
+ it, which is otherwise unreachable in its own left edge. */
3140
+ pointer-events: none;
3141
+ /* Over the input, not under it. Both share one grid cell, the input has an
3142
+ opaque `--control` background, and it comes later in the DOM -- so
3143
+ without this the glyph is painted and then covered, which looks exactly
3144
+ like an icon that failed to render. `.secret` never hit this because its
3145
+ button is written after the input. */
3146
+ z-index: 1;
3147
+ }
3148
+
3149
+ /* Room for the glyph, on the scale, so a density change moves the text with
3150
+ the mark rather than leaving one behind. `--space-8` is what `.secret`
3151
+ reserves on the right for the same reason; the scale has no 7, and an
3152
+ undefined custom property does not fall back -- the declaration computes
3153
+ to 0 and the text sits under the glyph. */
3154
+ .adorned-icon > input { padding-left: var(--space-8); }
3155
+
3156
+ /* No `--space-12` on the scale; two steps that are, added. Same arithmetic
3157
+ as `.secret-lg`. */
3158
+ .adorned-icon.adorned-lg > input { padding-left: calc(var(--space-10) + var(--space-1)); }
3159
+
3160
+ .adorned > .adorned-end {
3161
+ justify-self: end;
3162
+ display: flex;
3163
+ align-items: center;
3164
+ gap: var(--space-1);
3165
+ margin-right: var(--space-1);
3166
+ }
3167
+
3168
+ /* `:has`, not a sibling combinator: the input comes *before* the end cap in
3169
+ the DOM, so `~` reads the wrong way and matches nothing. */
3170
+ .adorned:has(> .adorned-end) > input { padding-right: var(--space-8); }
3171
+ .adorned-lg:has(> .adorned-end) > input {
3172
+ padding-right: calc(var(--space-10) + var(--space-1));
3173
+ }
3174
+
3175
+ /* Sized from its glyph, not `.icon-btn`'s 34px tile, which would be taller
3176
+ than a small input -- the note above `.secret-eye` in full. */
3177
+ .adorned-clear {
3178
+ width: auto; height: auto;
3179
+ padding: var(--space-1);
3180
+ opacity: 1;
3181
+ }
3182
+
3183
+ .adorned-lg .adorned-clear { padding: var(--space-2); }
3184
+
3083
3185
  /* ---- ./tour.css ---- */
3084
3186
  /* tour ----------------------------------------------------------------- */
3085
3187
 
@@ -3147,6 +3249,150 @@ input[type='range'].slider:disabled::-moz-range-thumb { opacity: 0; }
3147
3249
  anchored surfaces (`.pop`, `.menu-sheet`, `.cmd`) sit over everything and a
3148
3250
  rule of equal specificity that came earlier would lose to whatever the
3149
3251
  sheets they cover happen to set. */
3252
+ /* ---- ./kbd.css ---- */
3253
+ /* One rule where there were two identical ones, in `menu.css` and
3254
+ `command.css`. Both keep their class as well, so `Menu` and `Command` are
3255
+ `.kbd .menu-key` and `.kbd .cmd-key` now: the declarations are the same, so
3256
+ nothing moves, and the context classes stay available for a rule that ever
3257
+ needs to tell the two apart. */
3258
+ .kbd {
3259
+ flex: none;
3260
+ font-family: var(--font-mono);
3261
+ font-size: var(--text-2xs);
3262
+ color: var(--muted);
3263
+ letter-spacing: var(--tracking);
3264
+ }
3265
+
3266
+ /* ---- ./stat.css ---- */
3267
+ /* A figure with furniture. `--space-1` between the three lines because they
3268
+ are one object read top to bottom, not three paragraphs. */
3269
+ .stat {
3270
+ display: grid;
3271
+ gap: var(--space-1);
3272
+ padding: var(--space-4);
3273
+ border: var(--border-width) solid var(--border);
3274
+ border-radius: var(--radius-md);
3275
+ background: var(--panel);
3276
+ }
3277
+
3278
+ /* Inside something already bordered. A bordered tile in a bordered row is two
3279
+ boxes saying one thing. */
3280
+ .stat-bare {
3281
+ padding: 0;
3282
+ border: 0;
3283
+ border-radius: 0;
3284
+ background: none;
3285
+ gap: 0;
3286
+ line-height: 1.25;
3287
+ }
3288
+
3289
+ .stat > p { margin: 0; }
3290
+
3291
+ .stat-label {
3292
+ color: var(--muted);
3293
+ font-size: var(--text-xs);
3294
+ }
3295
+
3296
+ /* Tabular figures are the point: a column of numbers is compared by its
3297
+ digits' positions, and proportional ones move them per row. */
3298
+ .stat-value {
3299
+ font-size: var(--text-lg);
3300
+ font-weight: var(--weight-strong);
3301
+ font-variant-numeric: tabular-nums;
3302
+ }
3303
+
3304
+ .stat-bare .stat-value { font-size: var(--text-sm); }
3305
+
3306
+ .stat-note {
3307
+ color: var(--muted);
3308
+ font-size: var(--text-xs);
3309
+ }
3310
+
3311
+ /* Only the figure takes the colour. Colouring the label too makes the tile a
3312
+ status box, and a tone here is a remark about the number. */
3313
+ .stat[data-tone='info'] .stat-value { color: var(--info); }
3314
+ .stat[data-tone='good'] .stat-value { color: var(--good); }
3315
+ .stat[data-tone='warn'] .stat-value { color: var(--warn); }
3316
+ .stat[data-tone='bad'] .stat-value { color: var(--bad); }
3317
+
3318
+ /* ---- ./shell.css ---- */
3319
+ /* The frame. Layout only: every colour and every step is a token, and the
3320
+ page inside it owns its own spacing. */
3321
+ .shell {
3322
+ display: flex;
3323
+ flex-direction: column;
3324
+ min-height: 100vh;
3325
+ background: var(--bg);
3326
+ color: var(--text);
3327
+ }
3328
+
3329
+ .shell-head {
3330
+ border-bottom: var(--border-width) solid var(--border);
3331
+ background: var(--panel);
3332
+ /* `flex: none` so a long page does not squeeze the header. */
3333
+ flex: none;
3334
+ }
3335
+
3336
+ /* The measure, said once. `--shell-measure` rather than a literal so an app
3337
+ with a different one sets a variable instead of overriding three rules and
3338
+ missing the fourth -- which is how manage ended up with `head-inner-wide`
3339
+ and `main-wide` as separate classes. */
3340
+ .shell {
3341
+ --shell-measure: 64rem;
3342
+ --shell-measure-wide: 90rem;
3343
+ }
3344
+
3345
+ .shell-head-inner,
3346
+ .shell-nav,
3347
+ .shell-main {
3348
+ width: 100%;
3349
+ max-width: var(--shell-measure);
3350
+ margin-inline: auto;
3351
+ padding-inline: var(--space-4);
3352
+ }
3353
+
3354
+ .shell-wide { max-width: var(--shell-measure-wide); }
3355
+
3356
+ .shell-head-inner {
3357
+ display: flex;
3358
+ align-items: center;
3359
+ justify-content: space-between;
3360
+ gap: var(--space-4);
3361
+ padding-block: var(--space-3);
3362
+ min-width: 0;
3363
+ }
3364
+
3365
+ .shell-who {
3366
+ display: flex;
3367
+ align-items: center;
3368
+ gap: var(--space-3);
3369
+ min-width: 0;
3370
+ }
3371
+
3372
+ .shell-nav { padding-bottom: var(--space-2); }
3373
+
3374
+ .shell-main {
3375
+ flex: 1;
3376
+ padding-block: var(--space-6);
3377
+ /* `min-width: 0` because a flex child will not shrink below its content,
3378
+ and one wide table inside then pushes the whole page sideways. */
3379
+ min-width: 0;
3380
+ }
3381
+
3382
+ /* The label beside the theme control, when it is drawn. */
3383
+ .theme-switch {
3384
+ display: flex;
3385
+ align-items: center;
3386
+ gap: var(--space-2);
3387
+ min-width: 0;
3388
+ }
3389
+
3390
+ .theme-switch-label {
3391
+ color: var(--muted);
3392
+ font-size: var(--text-sm);
3393
+ white-space: nowrap;
3394
+ }
3395
+
3150
3396
  /* ---- ./scrollarea.css ---- */
3151
3397
  /* A box that scrolls, with its edges drawn.
3152
3398