@lyeve-labs/ui-kit 0.31.0 → 0.32.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 +9 -9
- package/dist/components/AccordionItem.svelte +1 -1
- package/dist/components/AccountMenu.svelte +2 -2
- package/dist/components/AccountMenu.svelte.d.ts +1 -1
- package/dist/components/Alert.svelte +2 -2
- package/dist/components/Alert.svelte.d.ts +1 -1
- package/dist/components/AppShell.svelte +7 -7
- package/dist/components/AppShell.svelte.d.ts +2 -2
- package/dist/components/AuthShell.svelte +1 -1
- package/dist/components/AuthShell.svelte.d.ts +1 -1
- package/dist/components/Autocomplete.svelte +1 -1
- package/dist/components/Badge.svelte +2 -2
- package/dist/components/Banner.svelte +1 -1
- package/dist/components/Banner.svelte.d.ts +1 -1
- package/dist/components/Button.svelte +5 -5
- package/dist/components/Button.svelte.d.ts +1 -1
- package/dist/components/Card.svelte +2 -2
- package/dist/components/Checkbox.svelte +3 -3
- package/dist/components/CheckoutSteps.svelte +54 -0
- package/dist/components/CheckoutSteps.svelte.d.ts +12 -0
- package/dist/components/Collapsible.svelte +1 -1
- package/dist/components/Collapsible.svelte.d.ts +1 -1
- package/dist/components/CopyField.svelte +2 -2
- package/dist/components/Field.svelte +1 -1
- package/dist/components/Field.svelte.d.ts +1 -1
- package/dist/components/FileInput.svelte +1 -1
- package/dist/components/FormMessage.svelte +4 -4
- package/dist/components/Logo.svelte +1 -1
- package/dist/components/Logo.svelte.d.ts +1 -1
- package/dist/components/MultiSelect.svelte +1 -1
- package/dist/components/PageHeader.svelte +2 -2
- package/dist/components/PageShell.svelte +4 -4
- package/dist/components/PageShell.svelte.d.ts +4 -4
- package/dist/components/Pagination.svelte +2 -2
- package/dist/components/Panel.svelte +1 -1
- package/dist/components/Panel.svelte.d.ts +1 -1
- package/dist/components/PasswordInput.svelte +5 -2
- package/dist/components/PluginCart.svelte +339 -0
- package/dist/components/PluginCart.svelte.d.ts +45 -0
- package/dist/components/Radio.svelte +2 -2
- package/dist/components/SearchInput.svelte +5 -4
- package/dist/components/SegmentedControl.svelte +4 -4
- package/dist/components/SegmentedControl.svelte.d.ts +2 -2
- package/dist/components/Select.svelte +5 -5
- package/dist/components/Select.svelte.d.ts +4 -4
- package/dist/components/SidebarNav.svelte +5 -5
- package/dist/components/Stat.svelte +1 -1
- package/dist/components/Stat.svelte.d.ts +1 -1
- package/dist/components/Table.svelte +4 -4
- package/dist/components/Tabs.svelte +2 -2
- package/dist/components/ThemeToggle.svelte +1 -1
- package/dist/components/TimePicker.svelte +2 -2
- package/dist/components/Toggle.svelte +3 -3
- package/dist/components/Toolbar.svelte +1 -1
- package/dist/components/Toolbar.svelte.d.ts +1 -1
- package/dist/components/TreeView.svelte +1 -1
- package/dist/components/dialog/Dialog.svelte +1 -1
- package/dist/components/dialog/DialogContainer.svelte +1 -1
- package/dist/components/dialog/dialog-manager.svelte.d.ts +3 -3
- package/dist/components/dialog/dialog-manager.svelte.js +7 -7
- package/dist/components/dialog/types.d.ts +3 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.js +8 -5
- package/dist/internal/calendar.d.ts +1 -1
- package/dist/internal/calendar.js +2 -2
- package/dist/internal/choice.d.ts +6 -6
- package/dist/internal/choice.js +6 -6
- package/dist/internal/copy.svelte.d.ts +2 -2
- package/dist/internal/copy.svelte.js +2 -2
- package/dist/internal/field.d.ts +11 -11
- package/dist/internal/field.js +12 -12
- package/dist/internal/filter.d.ts +3 -3
- package/dist/internal/filter.js +3 -3
- package/dist/internal/href.d.ts +1 -1
- package/dist/internal/href.js +1 -1
- package/dist/internal/layout.d.ts +7 -7
- package/dist/internal/layout.js +7 -7
- package/dist/internal/listbox.svelte.d.ts +1 -1
- package/dist/internal/listbox.svelte.js +3 -3
- package/dist/internal/nav-expansion.svelte.d.ts +1 -1
- package/dist/internal/nav-expansion.svelte.js +2 -2
- package/dist/internal/nav-tree.d.ts +3 -3
- package/dist/internal/nav-tree.js +3 -3
- package/dist/internal/number.d.ts +1 -1
- package/dist/internal/number.js +1 -1
- package/dist/internal/overlay.d.ts +3 -3
- package/dist/internal/overlay.js +3 -3
- package/dist/internal/panel.d.ts +5 -5
- package/dist/internal/panel.js +5 -5
- package/dist/internal/pricing.d.ts +110 -0
- package/dist/internal/pricing.js +77 -0
- package/dist/internal/rollup.d.ts +3 -3
- package/dist/internal/rollup.js +3 -3
- package/dist/internal/scroll-edges.d.ts +4 -4
- package/dist/internal/scroll-edges.js +5 -5
- package/dist/internal/time.d.ts +3 -3
- package/dist/internal/time.js +3 -3
- package/dist/internal/tone.d.ts +3 -3
- package/dist/internal/tone.js +3 -3
- package/dist/internal/touch.d.ts +1 -1
- package/dist/internal/touch.js +1 -1
- package/dist/internal/tree.d.ts +2 -2
- package/dist/internal/tree.js +2 -2
- package/dist/motion.d.ts +3 -3
- package/dist/motion.js +4 -4
- package/dist/stores/toast.svelte.d.ts +1 -1
- package/dist/styles/theme.css +45 -45
- package/dist/utils/cn.d.ts +1 -1
- package/dist/utils/theme.d.ts +6 -6
- package/dist/utils/theme.js +10 -10
- package/package.json +1 -1
- package/src/lib/styles/theme.css +45 -45
package/README.md
CHANGED
|
@@ -28,16 +28,16 @@ No config file, no theme provider, no setup ceremony.
|
|
|
28
28
|
|
|
29
29
|
## What's in the box
|
|
30
30
|
|
|
31
|
-
- **
|
|
31
|
+
- **71 components:** buttons, inputs, modals, drawers, tabs, tables, toasts, the works.
|
|
32
32
|
- **Two themes:** Soft Dark (default) and Soft Light, switched by a single `data-theme` attribute on `<html>`.
|
|
33
|
-
- **One CSS file:** `@lyeve-labs/ui-kit/styles.css` declares every token
|
|
33
|
+
- **One CSS file:** `@lyeve-labs/ui-kit/styles.css` declares every token. The rest is just Tailwind.
|
|
34
34
|
- **Svelte 5 native:** built on runes and snippets, fully typed end-to-end.
|
|
35
35
|
- **No surprises:** a `<Button />` is a `<button>`, an `<Input />` is an `<input>`. Markup matches the preview.
|
|
36
36
|
|
|
37
37
|
## Component list
|
|
38
38
|
|
|
39
39
|
<details>
|
|
40
|
-
<summary>
|
|
40
|
+
<summary>71 components, organized by purpose</summary>
|
|
41
41
|
|
|
42
42
|
**Layout and structure**
|
|
43
43
|
Card, Panel, AppShell, AuthShell, PageShell, PageHeader, SectionHeading, Divider, Accordion, AccordionItem, Collapsible, Table, DescriptionList, Toolbar, TreeView
|
|
@@ -55,7 +55,7 @@ Modal, Drawer, Tooltip
|
|
|
55
55
|
Dialog, DialogContainer, ConfirmDialog (plus the `openDialog` and `confirm` services)
|
|
56
56
|
|
|
57
57
|
**Feedback and status**
|
|
58
|
-
Alert, Banner, Badge, Tag, Indicator, Progress, Spinner, Skeleton, EmptyState, Stat, Kbd, CopyButton
|
|
58
|
+
Alert, Banner, Badge, Tag, Indicator, Progress, Spinner, Skeleton, CheckoutSteps, EmptyState, PluginCart, Stat, Kbd, CopyButton
|
|
59
59
|
|
|
60
60
|
**Media**
|
|
61
61
|
Avatar, AvatarGroup, Logo
|
|
@@ -175,7 +175,7 @@ in `styles.css` beside the palette:
|
|
|
175
175
|
|
|
176
176
|
| Token | Value | For |
|
|
177
177
|
| --------------------- | -------------------------------- | -------------------------------------------------------------------------------------------------------------- |
|
|
178
|
-
| `--duration-fast` | 120ms | a state change in place:
|
|
178
|
+
| `--duration-fast` | 120ms | a state change in place: color, opacity, border, shadow, focus |
|
|
179
179
|
| `--duration-base` | 200ms | movement in place (a knob, a chevron, a collapse) and a small surface arriving (menu, popover, tooltip, toast) |
|
|
180
180
|
| `--duration-slow` | 320ms | a large surface arriving (dialog, drawer) |
|
|
181
181
|
| `--duration-progress` | 500ms | a value moving (a progress bar's width) |
|
|
@@ -185,7 +185,7 @@ in `styles.css` beside the palette:
|
|
|
185
185
|
|
|
186
186
|
An exit runs one rung faster than its entrance, on the exit curve. The
|
|
187
187
|
`transition-*` utilities default to the fast rung and the move curve, so
|
|
188
|
-
`transition-colors` on its own is complete
|
|
188
|
+
`transition-colors` on its own is complete. `duration-base`, `duration-slow`,
|
|
189
189
|
`duration-progress`, `ease-enter`, `ease-exit` and `ease-move` are utilities
|
|
190
190
|
for the rest. A surface of your own that mounts and unmounts enters and
|
|
191
191
|
leaves through the same presets the kit's overlays use:
|
|
@@ -225,7 +225,7 @@ A page picks a role and `PageShell` picks the cap:
|
|
|
225
225
|
| `wide` | 1536px | a data page whose table needs the room |
|
|
226
226
|
| `full` | none | a canvas or a split pane |
|
|
227
227
|
|
|
228
|
-
A surface lifted off the page
|
|
228
|
+
A surface lifted off the page (`Modal`, `Drawer`, a dialog) takes a rung of
|
|
229
229
|
one shared ladder, so the same form is the same size whichever of the three a
|
|
230
230
|
page opens it in: `sm` 448px, `md` 576px, `lg` 704px, `xl` 896px, and `full`
|
|
231
231
|
1088px for a dialog holding a table.
|
|
@@ -253,7 +253,7 @@ This repo is a single-purpose component library. Nothing but `src/lib/`.
|
|
|
253
253
|
```
|
|
254
254
|
src/
|
|
255
255
|
└── lib/ # → published as @lyeve-labs/ui-kit
|
|
256
|
-
├── components/ #
|
|
256
|
+
├── components/ # 71 .svelte files
|
|
257
257
|
├── stores/ # toast.svelte.ts
|
|
258
258
|
├── styles/ # theme.css (the one stylesheet)
|
|
259
259
|
├── utils/ # cn.ts, theme.ts
|
|
@@ -263,7 +263,7 @@ src/
|
|
|
263
263
|
## Versioning
|
|
264
264
|
|
|
265
265
|
`@lyeve-labs/ui-kit` follows [SemVer](https://semver.org). While under `1.0`,
|
|
266
|
-
breaking changes bump the **minor** version
|
|
266
|
+
breaking changes bump the **minor** version. Additive changes bump the **patch**.
|
|
267
267
|
Every release is logged in [`CHANGELOG.md`](CHANGELOG.md) and on the docs site.
|
|
268
268
|
|
|
269
269
|
Maintainers: the release workflow is [`CONTRIBUTING.md`](CONTRIBUTING.md#releases).
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
<!--
|
|
60
60
|
A panel opens to the height of its own content, which no fixed value knows.
|
|
61
61
|
Animating grid-template-rows from 0fr to 1fr resolves to that height in CSS
|
|
62
|
-
alone
|
|
62
|
+
alone: no measuring, no reflow on every frame, and nothing to get wrong when
|
|
63
63
|
the content changes. The inner element carries the overflow so the text is
|
|
64
64
|
clipped rather than spilling while the row grows.
|
|
65
65
|
-->
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
import type { AccentTone } from '../internal/tone.js';
|
|
23
23
|
|
|
24
24
|
interface Props {
|
|
25
|
-
/** The line people
|
|
25
|
+
/** The line people recognize themselves by: a display name, or the email. */
|
|
26
26
|
name: string;
|
|
27
27
|
/** Under it. The email when the name is a name, otherwise a role or a plan. */
|
|
28
28
|
secondary?: string;
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
/**
|
|
75
75
|
* A `details` closes on its own summary and on nothing else, so without this
|
|
76
76
|
* the panel stays open behind whatever the reader does next. Escape and an
|
|
77
|
-
* outside click are both what a menu owes
|
|
77
|
+
* outside click are both what a menu owes. They are enhancement, and the
|
|
78
78
|
* disclosure still works without either.
|
|
79
79
|
*/
|
|
80
80
|
$effect(() => {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import type { Snippet } from 'svelte';
|
|
19
19
|
import type { AccentTone } from '../internal/tone.js';
|
|
20
20
|
interface Props {
|
|
21
|
-
/** The line people
|
|
21
|
+
/** The line people recognize themselves by: a display name, or the email. */
|
|
22
22
|
name: string;
|
|
23
23
|
/** Under it. The email when the name is a name, otherwise a role or a plan. */
|
|
24
24
|
secondary?: string;
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
*
|
|
29
29
|
* A confirmation clears itself. A warning and a failure stay until
|
|
30
30
|
* dismissed, so this is off by default and a danger alert never takes it.
|
|
31
|
-
* Pass it wherever the alert reports the outcome of a submit
|
|
31
|
+
* Pass it wherever the alert reports the outcome of a submit. Leave it off
|
|
32
32
|
* where a success tone states a standing condition, which does not stop
|
|
33
33
|
* being true after five seconds.
|
|
34
34
|
*/
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
62
|
* A failure and a warning interrupt, because the reader has to act. Anything
|
|
63
|
-
* else waits for a pause. FormMessage already splits them this way
|
|
63
|
+
* else waits for a pause. FormMessage already splits them this way. Alert
|
|
64
64
|
* announced every tone assertively, so a green confirmation cut across
|
|
65
65
|
* whatever was being read. An alert that also clears itself must not be
|
|
66
66
|
* assertive: it would interrupt to say something and then take it away.
|
|
@@ -9,7 +9,7 @@ interface Props {
|
|
|
9
9
|
*
|
|
10
10
|
* A confirmation clears itself. A warning and a failure stay until
|
|
11
11
|
* dismissed, so this is off by default and a danger alert never takes it.
|
|
12
|
-
* Pass it wherever the alert reports the outcome of a submit
|
|
12
|
+
* Pass it wherever the alert reports the outcome of a submit. Leave it off
|
|
13
13
|
* where a success tone states a standing condition, which does not stop
|
|
14
14
|
* being true after five seconds.
|
|
15
15
|
*/
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* while the sidebar is the 56px icon rail, so a SidebarNav takes it as
|
|
5
5
|
* `collapsed`, a brand row renders its mark without the wordmark, and a
|
|
6
6
|
* footer band renders what fits. A snippet that ignores the argument still
|
|
7
|
-
* renders
|
|
7
|
+
* renders. It is simply drawn at 56px and clipped.
|
|
8
8
|
*/
|
|
9
9
|
export interface SidebarState {
|
|
10
10
|
rail: boolean;
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
*
|
|
84
84
|
* The rail is automatic between md: and lg:, where the 224px column left a
|
|
85
85
|
* 768px window 496px for the page. A page that owns the viewport - a
|
|
86
|
-
* canvas, a split pane - asks for it at every width with this
|
|
86
|
+
* canvas, a split pane - asks for it at every width with this. The labels
|
|
87
87
|
* still come back under a pointer or the keyboard. Below md: the sidebar
|
|
88
88
|
* is the drawer whatever this says.
|
|
89
89
|
*/
|
|
@@ -136,7 +136,7 @@
|
|
|
136
136
|
const drawerOpen = $derived(isMobile && navOpen);
|
|
137
137
|
/**
|
|
138
138
|
* Collapsing is a desktop gesture. Below md: the same aside is the drawer,
|
|
139
|
-
* so
|
|
139
|
+
* so honoring `collapsed` there would leave the hamburger opening nothing.
|
|
140
140
|
*/
|
|
141
141
|
const railHidden = $derived(collapsible && collapsed && !isMobile);
|
|
142
142
|
/** Same rule for the header: a desktop gesture, and the drawer's button stays. */
|
|
@@ -148,7 +148,7 @@
|
|
|
148
148
|
* While the sidebar is the rail it is 56px wide and the nav is told so. A
|
|
149
149
|
* pointer over it, or focus inside it, opens it to the full column over the
|
|
150
150
|
* page rather than beside it, so the page keeps its width and the reader
|
|
151
|
-
* still gets every label
|
|
151
|
+
* still gets every label. It closes when the pointer or the focus leaves.
|
|
152
152
|
* State rather than a hover rule, because the nav snippet has to be told
|
|
153
153
|
* the labels are wanted, and a stylesheet cannot tell it.
|
|
154
154
|
*/
|
|
@@ -174,7 +174,7 @@
|
|
|
174
174
|
};
|
|
175
175
|
});
|
|
176
176
|
|
|
177
|
-
/** Focus moving from one rail link to the next stays inside
|
|
177
|
+
/** Focus moving from one rail link to the next stays inside. Only leaving the rail closes it. */
|
|
178
178
|
function onRailFocusOut(event: FocusEvent) {
|
|
179
179
|
const next = event.relatedTarget;
|
|
180
180
|
if (
|
|
@@ -218,7 +218,7 @@
|
|
|
218
218
|
{/if}
|
|
219
219
|
|
|
220
220
|
<!--
|
|
221
|
-
One aside at every width. Below md: it sits inside a positioned dialog
|
|
221
|
+
One aside at every width. Below md: it sits inside a positioned dialog. Above
|
|
222
222
|
it, that wrapper is the column it has always been. Rendering a second copy
|
|
223
223
|
for the drawer would put every nav link in the page twice, which is what a
|
|
224
224
|
strict-mode locator trips on and what a screen reader reads out.
|
|
@@ -226,7 +226,7 @@
|
|
|
226
226
|
The dialog role is on the wrapper and not on the aside: an aside is a
|
|
227
227
|
complementary landmark, and the a11y gate rejects a non-interactive element
|
|
228
228
|
taking an interactive role. use:overlay is what makes aria-modal true rather
|
|
229
|
-
than merely claimed
|
|
229
|
+
than merely claimed. It moves focus in, keeps Tab inside, and hands focus
|
|
230
230
|
back to the hamburger on close.
|
|
231
231
|
-->
|
|
232
232
|
{#if drawerOpen}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* while the sidebar is the 56px icon rail, so a SidebarNav takes it as
|
|
4
4
|
* `collapsed`, a brand row renders its mark without the wordmark, and a
|
|
5
5
|
* footer band renders what fits. A snippet that ignores the argument still
|
|
6
|
-
* renders
|
|
6
|
+
* renders. It is simply drawn at 56px and clipped.
|
|
7
7
|
*/
|
|
8
8
|
export interface SidebarState {
|
|
9
9
|
rail: boolean;
|
|
@@ -68,7 +68,7 @@ interface Props {
|
|
|
68
68
|
*
|
|
69
69
|
* The rail is automatic between md: and lg:, where the 224px column left a
|
|
70
70
|
* 768px window 496px for the page. A page that owns the viewport - a
|
|
71
|
-
* canvas, a split pane - asks for it at every width with this
|
|
71
|
+
* canvas, a split pane - asks for it at every width with this. The labels
|
|
72
72
|
* still come back under a pointer or the keyboard. Below md: the sidebar
|
|
73
73
|
* is the drawer whatever this says.
|
|
74
74
|
*/
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
description?: string;
|
|
12
12
|
/** Where the lockup links. Left unset, it is a mark and not a link. */
|
|
13
13
|
href?: string;
|
|
14
|
-
/** `md` is a form
|
|
14
|
+
/** `md` is a form. `lg` is a walkthrough with more than one column. */
|
|
15
15
|
width?: Width;
|
|
16
16
|
/** Controls at the top end of the column: a theme toggle, a language switch. */
|
|
17
17
|
actions?: Snippet;
|
|
@@ -6,7 +6,7 @@ interface Props {
|
|
|
6
6
|
description?: string;
|
|
7
7
|
/** Where the lockup links. Left unset, it is a mark and not a link. */
|
|
8
8
|
href?: string;
|
|
9
|
-
/** `md` is a form
|
|
9
|
+
/** `md` is a form. `lg` is a walkthrough with more than one column. */
|
|
10
10
|
width?: Width;
|
|
11
11
|
/** Controls at the top end of the column: a theme toggle, a language switch. */
|
|
12
12
|
actions?: Snippet;
|
|
@@ -210,7 +210,7 @@
|
|
|
210
210
|
the field. The padding is taken back off the horizontal axis only: the
|
|
211
211
|
box is positioned from its right edge, so a negative right margin holds
|
|
212
212
|
the glyph exactly where it was, while a negative top margin would fight
|
|
213
|
-
the translate that
|
|
213
|
+
the translate that centers it and lift it 6px off the middle of the
|
|
214
214
|
field.
|
|
215
215
|
-->
|
|
216
216
|
<button
|
|
@@ -42,14 +42,14 @@
|
|
|
42
42
|
Under a Table's default `overflow-wrap: anywhere` a status rendered as
|
|
43
43
|
`dra ft` and a role as `sup er_a dmi n`. The label sits in a truncating
|
|
44
44
|
span so a badge whose caller caps its width ends in an ellipsis instead of
|
|
45
|
-
painting past its own border
|
|
45
|
+
painting past its own border. With no cap the span never shrinks.
|
|
46
46
|
|
|
47
47
|
The svg rules are what let a caller put an icon in the label. Preflight
|
|
48
48
|
makes every svg a block, and a block inside the label takes a line of its
|
|
49
49
|
own, so an icon and its text stacked and the badge rendered two rows tall
|
|
50
50
|
inside a pill. A flex row here would fix the stacking and lose the
|
|
51
51
|
ellipsis, which is the thing the span exists for, so the icon goes back to
|
|
52
|
-
being inline and sits on the text's optical
|
|
52
|
+
being inline and sits on the text's optical center. -->
|
|
53
53
|
<span
|
|
54
54
|
class="inline-flex max-w-full items-center font-medium whitespace-nowrap rounded-full border {tones[
|
|
55
55
|
tone
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
*
|
|
19
19
|
* A confirmation clears itself. A warning and a failure stay until
|
|
20
20
|
* dismissed, so this is off by default. Pass it wherever the banner
|
|
21
|
-
* reports the outcome of a submit
|
|
21
|
+
* reports the outcome of a submit. Leave it off where it states a standing
|
|
22
22
|
* condition, which does not stop being true after five seconds.
|
|
23
23
|
*/
|
|
24
24
|
autoDismiss?: number | boolean;
|
|
@@ -8,7 +8,7 @@ interface Props {
|
|
|
8
8
|
*
|
|
9
9
|
* A confirmation clears itself. A warning and a failure stay until
|
|
10
10
|
* dismissed, so this is off by default. Pass it wherever the banner
|
|
11
|
-
* reports the outcome of a submit
|
|
11
|
+
* reports the outcome of a submit. Leave it off where it states a standing
|
|
12
12
|
* condition, which does not stop being true after five seconds.
|
|
13
13
|
*/
|
|
14
14
|
autoDismiss?: number | boolean;
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
/**
|
|
31
31
|
* The words shown on hover and focus. Defaults to the button's own
|
|
32
32
|
* aria-label, so an icon-only button names itself to a sighted reader the
|
|
33
|
-
* way it already does to a screen reader
|
|
33
|
+
* way it already does to a screen reader. A string replaces those words,
|
|
34
34
|
* and `false` keeps the button silent.
|
|
35
35
|
*
|
|
36
36
|
* Thirty-five icon-only actions across one console had a name and no
|
|
@@ -65,14 +65,14 @@
|
|
|
65
65
|
);
|
|
66
66
|
|
|
67
67
|
/**
|
|
68
|
-
*
|
|
68
|
+
* Color per variant, in three states: at rest, under a pointer, and held.
|
|
69
69
|
*
|
|
70
70
|
* The pressed step is the one a touch screen depends on. `hover:` compiles
|
|
71
71
|
* inside `@media (hover: hover)`, which is false on a finger, so a tap ran
|
|
72
72
|
* rest to rest with the action already fired and the control never
|
|
73
73
|
* acknowledged the press. `active:` matches under a finger as well as under a
|
|
74
74
|
* mouse, and Tailwind emits it after `hover:` at equal specificity, so the
|
|
75
|
-
* held
|
|
75
|
+
* held color wins on a device that has both.
|
|
76
76
|
*
|
|
77
77
|
* primary and violet had a pressed state already and neither one showed:
|
|
78
78
|
* `active:bg-brand` on a `bg-brand` button and `active:brightness-100` on an
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
* whitespace-nowrap: a label never breaks inside itself. Under a Table's
|
|
108
108
|
* `overflow-wrap: anywhere` a two-word ghost button in a flex row rendered
|
|
109
109
|
* on two lines, and a status badge beside it as `dra ft`. A caller that
|
|
110
|
-
* caps the width wraps its label in a `truncate` span
|
|
110
|
+
* caps the width wraps its label in a `truncate` span. The button cannot
|
|
111
111
|
* do that for it, because the label and any icon before it are flex items
|
|
112
112
|
* and text-overflow does not reach into one.
|
|
113
113
|
*
|
|
@@ -164,7 +164,7 @@
|
|
|
164
164
|
{/snippet}
|
|
165
165
|
|
|
166
166
|
<!-- The hint repeats the name, so it does not describe the control a second
|
|
167
|
-
time
|
|
167
|
+
time. The aria-label already announces it. The wrapper takes the full
|
|
168
168
|
width along with the button, or a full button would shrink to its text. -->
|
|
169
169
|
{#if hintText}
|
|
170
170
|
<Tooltip text={hintText} describe={false} class={full ? 'w-full' : ''}>
|
|
@@ -19,7 +19,7 @@ interface Props extends Forwarded {
|
|
|
19
19
|
/**
|
|
20
20
|
* The words shown on hover and focus. Defaults to the button's own
|
|
21
21
|
* aria-label, so an icon-only button names itself to a sighted reader the
|
|
22
|
-
* way it already does to a screen reader
|
|
22
|
+
* way it already does to a screen reader. A string replaces those words,
|
|
23
23
|
* and `false` keeps the button silent.
|
|
24
24
|
*
|
|
25
25
|
* Thirty-five icon-only actions across one console had a name and no
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
* you pressed one. It took `role="button"` and `tabindex="0"` from `onclick`
|
|
59
59
|
* alone, so it entered the tab order and then ignored Enter and Space unless
|
|
60
60
|
* the page happened to pass its own `onkeydown`. Native button semantics are
|
|
61
|
-
* the contract here, so the component
|
|
61
|
+
* the contract here, so the component honors them itself and still forwards
|
|
62
62
|
* whatever the consumer supplied.
|
|
63
63
|
*/
|
|
64
64
|
function activate(e: KeyboardEvent) {
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
</script>
|
|
80
80
|
|
|
81
81
|
<!-- tabindex is only set alongside role="button" (when onclick is provided),
|
|
82
|
-
so the element is interactive
|
|
82
|
+
so the element is interactive. The compiler can't narrow the dynamic role. -->
|
|
83
83
|
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
84
84
|
<div
|
|
85
85
|
data-print="keep"
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
: undefined,
|
|
80
80
|
);
|
|
81
81
|
|
|
82
|
-
// A part-checked box is on, not a third
|
|
82
|
+
// A part-checked box is on, not a third color, so it paints the filled box
|
|
83
83
|
// and tints the card exactly as a checked one does.
|
|
84
84
|
const on = $derived(checked || indeterminate);
|
|
85
85
|
|
|
@@ -91,8 +91,8 @@
|
|
|
91
91
|
function handleChange(e: Event & { currentTarget: HTMLInputElement }) {
|
|
92
92
|
// Disabled is enforced here as well as on the input. The native attribute
|
|
93
93
|
// is what stops a real click, and it is the only thing that does, so a
|
|
94
|
-
// change arriving any other way
|
|
95
|
-
// node
|
|
94
|
+
// change arriving any other way (a synthetic event, a script driving the
|
|
95
|
+
// node) would still move the bound value with nothing on screen to say so.
|
|
96
96
|
if (disabled) return;
|
|
97
97
|
// The DOM clears indeterminate on the first click. Leaving the prop set
|
|
98
98
|
// would repaint the mixed bar over a box the user has just ticked.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Where the reader is in a purchase, on every surface the purchase crosses.
|
|
3
|
+
|
|
4
|
+
Buying here spans two sites: the price page configures, the portal takes the
|
|
5
|
+
account and the payment. The hop between them read as arriving somewhere
|
|
6
|
+
else, so the same four labels are rendered on both, from one component, and
|
|
7
|
+
the reader can see that step three follows step two.
|
|
8
|
+
|
|
9
|
+
It is an ordered list, and the step in progress carries aria-current, so the
|
|
10
|
+
same information reaches a reader who cannot see the styling.
|
|
11
|
+
-->
|
|
12
|
+
<script lang="ts">
|
|
13
|
+
import { cn } from '../utils/cn.js';
|
|
14
|
+
|
|
15
|
+
interface Props {
|
|
16
|
+
/** In order. Four is the most that stays readable on a phone. */
|
|
17
|
+
steps: string[];
|
|
18
|
+
/** Zero-based index of the step in progress. */
|
|
19
|
+
current: number;
|
|
20
|
+
/** Names the list for assistive technology. */
|
|
21
|
+
label?: string;
|
|
22
|
+
class?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let { steps, current, label = 'Checkout progress', class: klass = '' }: Props = $props();
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<nav aria-label={label} class={cn('w-full', klass)}>
|
|
29
|
+
<ol class="flex flex-wrap items-center gap-x-2 gap-y-1 text-xs sm:text-sm">
|
|
30
|
+
{#each steps as step, i (step)}
|
|
31
|
+
{@const done = i < current}
|
|
32
|
+
{@const here = i === current}
|
|
33
|
+
<li class="flex items-center gap-2" aria-current={here ? 'step' : undefined}>
|
|
34
|
+
<span
|
|
35
|
+
class={cn(
|
|
36
|
+
'flex h-5 w-5 shrink-0 items-center justify-center rounded-full text-xs font-semibold transition-colors',
|
|
37
|
+
done && 'bg-brand/20 text-brand',
|
|
38
|
+
here && 'bg-brand text-ink',
|
|
39
|
+
!done && !here && 'bg-surface-2 text-faint',
|
|
40
|
+
)}
|
|
41
|
+
aria-hidden="true"
|
|
42
|
+
>
|
|
43
|
+
{i + 1}
|
|
44
|
+
</span>
|
|
45
|
+
<span class={cn(here ? 'font-medium text-fg' : done ? 'text-muted' : 'text-faint')}>
|
|
46
|
+
{step}
|
|
47
|
+
</span>
|
|
48
|
+
{#if i < steps.length - 1}
|
|
49
|
+
<span class="mx-1 text-faint" aria-hidden="true">/</span>
|
|
50
|
+
{/if}
|
|
51
|
+
</li>
|
|
52
|
+
{/each}
|
|
53
|
+
</ol>
|
|
54
|
+
</nav>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
/** In order. Four is the most that stays readable on a phone. */
|
|
3
|
+
steps: string[];
|
|
4
|
+
/** Zero-based index of the step in progress. */
|
|
5
|
+
current: number;
|
|
6
|
+
/** Names the list for assistive technology. */
|
|
7
|
+
label?: string;
|
|
8
|
+
class?: string;
|
|
9
|
+
}
|
|
10
|
+
declare const CheckoutSteps: import("svelte").Component<Props, {}, "">;
|
|
11
|
+
type CheckoutSteps = ReturnType<typeof CheckoutSteps>;
|
|
12
|
+
export default CheckoutSteps;
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* share one value.
|
|
11
11
|
*
|
|
12
12
|
* Accordion is the component for a set of these. It decides which of its
|
|
13
|
-
* items is open, and an item asks it
|
|
13
|
+
* items is open, and an item asks it. This decides nothing beyond itself.
|
|
14
14
|
*/
|
|
15
15
|
import type { Component, Snippet } from 'svelte';
|
|
16
16
|
import { TOUCH_GROW } from '../internal/touch.js';
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* share one value.
|
|
10
10
|
*
|
|
11
11
|
* Accordion is the component for a set of these. It decides which of its
|
|
12
|
-
* items is open, and an item asks it
|
|
12
|
+
* items is open, and an item asks it. This decides nothing beyond itself.
|
|
13
13
|
*/
|
|
14
14
|
import type { Component, Snippet } from 'svelte';
|
|
15
15
|
interface Props {
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
inside it, so the caller's actions, the reveal and the copy all fit
|
|
88
88
|
without the value running under them and without measuring anything.
|
|
89
89
|
The frame takes the focus ring and the brand border from the input
|
|
90
|
-
inside it
|
|
90
|
+
inside it. A focused button draws its own ring and not the frame's. -->
|
|
91
91
|
<div
|
|
92
92
|
class="flex h-control w-full items-center gap-0.5 rounded-lg border bg-surface-2 pe-1 transition-colors border-line-strong has-[input:focus]:border-brand has-[input:focus-visible]:ring-2 has-[input:focus-visible]:ring-inset has-[input:focus-visible]:ring-brand"
|
|
93
93
|
>
|
|
@@ -130,7 +130,7 @@
|
|
|
130
130
|
|
|
131
131
|
<!-- Mounted empty for the life of the field, so a copy is a change a screen
|
|
132
132
|
reader announces rather than a region that arrives already full. The
|
|
133
|
-
check is the sighted confirmation
|
|
133
|
+
check is the sighted confirmation. Only a failure is shown in words. -->
|
|
134
134
|
<p
|
|
135
135
|
role="status"
|
|
136
136
|
aria-live="polite"
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* What Field hands the control it wraps.
|
|
4
4
|
*
|
|
5
5
|
* The wiring is the whole point of the component. A consumer building a
|
|
6
|
-
* control the kit does not ship - a
|
|
6
|
+
* control the kit does not ship - a color well, a code editor, a third
|
|
7
7
|
* party map picker - cannot reach internal/field.ts, so it hand-copies the
|
|
8
8
|
* class strings and stops there: a form in a consuming app spelled
|
|
9
9
|
* `class="flex flex-col gap-1.5"` inline, which is the literal value of
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* What Field hands the control it wraps.
|
|
3
3
|
*
|
|
4
4
|
* The wiring is the whole point of the component. A consumer building a
|
|
5
|
-
* control the kit does not ship - a
|
|
5
|
+
* control the kit does not ship - a color well, a code editor, a third
|
|
6
6
|
* party map picker - cannot reach internal/field.ts, so it hand-copies the
|
|
7
7
|
* class strings and stops there: a form in a consuming app spelled
|
|
8
8
|
* `class="flex flex-col gap-1.5"` inline, which is the literal value of
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
|
|
54
54
|
<!-- The dashed border is the only thing marking this region as a control, so
|
|
55
55
|
it rests on line-strong: line reads 1.25:1 and failed SC 1.4.11. The
|
|
56
|
-
hover is full-strength brand, because a focus-adjacent
|
|
56
|
+
hover is full-strength brand, because a focus-adjacent color dropped to
|
|
57
57
|
half alpha reads as a weaker affordance for no reason a user can infer. -->
|
|
58
58
|
<label
|
|
59
59
|
class="flex w-full cursor-pointer flex-col items-center justify-center gap-2 rounded-xl
|
|
@@ -14,10 +14,10 @@
|
|
|
14
14
|
let { tone = 'danger', class: klass = '', children }: Props = $props();
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* The message
|
|
17
|
+
* The message color, stated once.
|
|
18
18
|
*
|
|
19
19
|
* Eleven pages rendered a submit outcome into a bare paragraph and picked
|
|
20
|
-
* their own size and
|
|
20
|
+
* their own size and color for it, so the same failure read as three
|
|
21
21
|
* different things depending on which settings page the user was on.
|
|
22
22
|
*/
|
|
23
23
|
const tones: Record<Tone, string> = {
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
</script>
|
|
51
51
|
|
|
52
52
|
<!--
|
|
53
|
-
The tones differ in more than
|
|
53
|
+
The tones differ in more than color. A failure takes role="alert", which
|
|
54
54
|
interrupts whatever the reader is doing, because the submit did not go
|
|
55
55
|
through and the user has to act on it. A confirmation takes aria-live
|
|
56
56
|
"polite" and waits for a pause, because the work is done and nothing is being
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
{#if filled}
|
|
65
65
|
<!-- Stroked SVG, not a literal cross or tick: a font glyph lands at
|
|
66
66
|
whatever weight the reader's font gives it, which sat visibly lighter
|
|
67
|
-
than every other icon in the library.
|
|
67
|
+
than every other icon in the library. Color alone would also be the
|
|
68
68
|
only thing telling a failure from a confirmation. -->
|
|
69
69
|
<svg
|
|
70
70
|
class="mt-0.5 shrink-0"
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* for that reason, and none belongs here.
|
|
13
13
|
*
|
|
14
14
|
* Every face and the wordmark paint from the mark-* tokens, which carry the
|
|
15
|
-
* brand book's
|
|
15
|
+
* brand book's colors per theme: the same lockup the documentation site
|
|
16
16
|
* ships as a static file. They are separate from the brand ramp because that
|
|
17
17
|
* ramp darkens for text contrast in the light theme and inverts the mark's
|
|
18
18
|
* lighting there.
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* for that reason, and none belongs here.
|
|
12
12
|
*
|
|
13
13
|
* Every face and the wordmark paint from the mark-* tokens, which carry the
|
|
14
|
-
* brand book's
|
|
14
|
+
* brand book's colors per theme: the same lockup the documentation site
|
|
15
15
|
* ships as a static file. They are separate from the brand ramp because that
|
|
16
16
|
* ramp darkens for text contrast in the light theme and inverts the mark's
|
|
17
17
|
* lighting there.
|
|
@@ -215,7 +215,7 @@
|
|
|
215
215
|
exception, and this one sits inside the trigger. The padding is
|
|
216
216
|
taken straight back off as margin, so the chip is laid out
|
|
217
217
|
exactly as it was and only the reachable area grew. The glyph
|
|
218
|
-
is the same 12px cross Autocomplete clears its value with
|
|
218
|
+
is the same 12px cross Autocomplete clears its value with. It
|
|
219
219
|
was drawn a pixel smaller here for no reason anyone recorded.
|
|
220
220
|
-->
|
|
221
221
|
<svg
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
one of them, while this rendered at 24px, a size the ramp does not
|
|
50
50
|
contain.
|
|
51
51
|
|
|
52
|
-
text-h2 and not text-h1. The brand ramp is a marketing ramp
|
|
52
|
+
text-h2 and not text-h1. The brand ramp is a marketing ramp: it runs
|
|
53
53
|
64 / 44 / 32 / 22 / 16 and holds nothing between 16 and 22, which is
|
|
54
54
|
where a console's own type lives. A 44px page title over a 14px table on
|
|
55
55
|
a 390px screen is the ramp applied rather than adopted. 32px is the step
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
{#if description && !compact}<p class="mt-1 text-sm text-muted">{description}</p>{/if}
|
|
75
75
|
</div>
|
|
76
76
|
<!-- The slot wraps in two steps. It sits beside the title while both fit
|
|
77
|
-
and drops under it, at the end edge, when they do not
|
|
77
|
+
and drops under it, at the end edge, when they do not. Wider than the
|
|
78
78
|
row on its own, it wraps its controls inside itself, each line ending at
|
|
79
79
|
the end edge, in the order they were given. It used to be `shrink-0`,
|
|
80
80
|
which sizes a flex item to its content and so cannot wrap even when the
|