@lumiose/ui 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +53 -2
- package/dist/accordion/accordion.css +20 -0
- package/dist/accordion/accordion.script.ts +66 -0
- package/dist/accordion/accordion.tsx +288 -0
- package/dist/animation/animation.ts +34 -0
- package/dist/autocomplete/autocomplete.tsx +239 -0
- package/dist/avatar/avatar.css +54 -0
- package/dist/avatar/avatar.script.ts +96 -0
- package/dist/avatar/avatar.tsx +110 -0
- package/dist/button/button.script.ts +35 -0
- package/dist/button/button.tsx +40 -0
- package/dist/checkbox/checkbox.script.ts +225 -0
- package/dist/checkbox/checkbox.tsx +156 -0
- package/dist/checkbox/context.ts +17 -0
- package/dist/checkbox-group/checkbox-group.tsx +111 -0
- package/dist/collapsible/collapsible.css +12 -0
- package/dist/collapsible/collapsible.script.ts +7 -0
- package/dist/collapsible/collapsible.tsx +153 -0
- package/dist/collapsible/panel.ts +115 -0
- package/dist/combobox/combobox.css +37 -0
- package/dist/combobox/combobox.script.ts +974 -0
- package/dist/combobox/combobox.tsx +556 -0
- package/dist/combobox/context.ts +46 -0
- package/dist/context-menu/context-menu.css +16 -0
- package/dist/context-menu/context-menu.script.ts +172 -0
- package/dist/context-menu/context-menu.tsx +178 -0
- package/dist/dialog/dialog.css +35 -0
- package/dist/dialog/dialog.script.ts +269 -0
- package/dist/dialog/dialog.tsx +214 -0
- package/dist/drawer/drawer.css +38 -0
- package/dist/drawer/drawer.script.ts +207 -0
- package/dist/drawer/drawer.tsx +113 -0
- package/dist/field/context.ts +156 -0
- package/dist/field/field.css +16 -0
- package/dist/field/field.script.ts +422 -0
- package/dist/field/field.tsx +465 -0
- package/dist/fieldset/fieldset.tsx +41 -0
- package/dist/form/form.tsx +50 -0
- package/dist/form/native.ts +21 -0
- package/dist/form/reset.ts +53 -0
- package/dist/index.ts +261 -0
- package/dist/input/input.tsx +67 -0
- package/dist/layer/layer.ts +191 -0
- package/dist/locale/digits.ts +21 -0
- package/dist/menu/menu.css +22 -0
- package/dist/menu/menu.script.ts +591 -0
- package/dist/menu/menu.tsx +520 -0
- package/dist/menubar/menubar.css +16 -0
- package/dist/menubar/menubar.script.ts +195 -0
- package/dist/menubar/menubar.tsx +67 -0
- package/dist/meter/meter.tsx +134 -0
- package/dist/navigation-menu/navigation-menu.css +14 -0
- package/dist/navigation-menu/navigation-menu.script.ts +466 -0
- package/dist/navigation-menu/navigation-menu.tsx +266 -0
- package/dist/number-field/format.ts +70 -0
- package/dist/number-field/number-field.css +38 -0
- package/dist/number-field/number-field.script.ts +490 -0
- package/dist/number-field/number-field.tsx +403 -0
- package/dist/otp-field/otp-field.css +45 -0
- package/dist/otp-field/otp-field.script.ts +330 -0
- package/dist/otp-field/otp-field.tsx +320 -0
- package/dist/placement/place.ts +148 -0
- package/dist/placement/placement.css +155 -0
- package/dist/placement/placement.ts +60 -0
- package/dist/popover/hover.ts +178 -0
- package/dist/popover/popover.css +20 -0
- package/dist/popover/popover.script.ts +256 -0
- package/dist/popover/popover.tsx +317 -0
- package/dist/preview-card/preview-card.css +7 -0
- package/dist/preview-card/preview-card.script.ts +136 -0
- package/dist/preview-card/preview-card.tsx +128 -0
- package/dist/progress/progress.tsx +155 -0
- package/dist/progress/value.ts +23 -0
- package/dist/radio-group/radio-group.tsx +156 -0
- package/dist/scroll-area/scroll-area.css +72 -0
- package/dist/scroll-area/scroll-area.script.ts +281 -0
- package/dist/scroll-area/scroll-area.tsx +118 -0
- package/dist/select/select.css +25 -0
- package/dist/select/select.tsx +171 -0
- package/dist/separator/separator.css +12 -0
- package/dist/separator/separator.tsx +38 -0
- package/dist/slider/format.ts +33 -0
- package/dist/slider/slider.css +93 -0
- package/dist/slider/slider.script.ts +355 -0
- package/dist/slider/slider.tsx +374 -0
- package/dist/styles.css +30 -0
- package/dist/switch/switch.tsx +92 -0
- package/dist/tabs/tabs.css +54 -0
- package/dist/tabs/tabs.script.ts +225 -0
- package/dist/tabs/tabs.tsx +272 -0
- package/dist/themes/default.css +2111 -0
- package/dist/toast/command.ts +3 -0
- package/dist/toast/toast.css +68 -0
- package/dist/toast/toast.script.ts +516 -0
- package/dist/toast/toast.tsx +432 -0
- package/dist/toggle/context.ts +16 -0
- package/dist/toggle/toggle.tsx +99 -0
- package/dist/toggle-group/toggle-group.script.ts +166 -0
- package/dist/toggle-group/toggle-group.tsx +117 -0
- package/dist/toolbar/context.ts +18 -0
- package/dist/toolbar/toolbar.css +12 -0
- package/dist/toolbar/toolbar.script.ts +123 -0
- package/dist/toolbar/toolbar.tsx +180 -0
- package/dist/tooltip/tooltip.css +8 -0
- package/dist/tooltip/tooltip.script.ts +222 -0
- package/dist/tooltip/tooltip.tsx +149 -0
- package/docs/README.md +174 -0
- package/docs/accordion.md +208 -0
- package/docs/autocomplete.md +161 -0
- package/docs/avatar.md +198 -0
- package/docs/button.md +98 -0
- package/docs/checkbox-group.md +112 -0
- package/docs/checkbox.md +133 -0
- package/docs/collapsible.md +165 -0
- package/docs/combobox.md +369 -0
- package/docs/context-menu.md +197 -0
- package/docs/dialog.md +256 -0
- package/docs/drawer.md +204 -0
- package/docs/field.md +295 -0
- package/docs/fieldset.md +92 -0
- package/docs/form.md +131 -0
- package/docs/input.md +87 -0
- package/docs/installation.md +65 -0
- package/docs/llms-full.txt +6923 -0
- package/docs/llms.txt +53 -0
- package/docs/menu.md +365 -0
- package/docs/menubar.md +186 -0
- package/docs/meter.md +103 -0
- package/docs/navigation-menu.md +257 -0
- package/docs/number-field.md +181 -0
- package/docs/otp-field.md +160 -0
- package/docs/popover.md +271 -0
- package/docs/preview-card.md +130 -0
- package/docs/progress.md +115 -0
- package/docs/radio-group.md +127 -0
- package/docs/scroll-area.md +147 -0
- package/docs/select.md +236 -0
- package/docs/separator.md +116 -0
- package/docs/slider.md +184 -0
- package/docs/switch.md +98 -0
- package/docs/tabs.md +254 -0
- package/docs/theming.md +132 -0
- package/docs/toast.md +311 -0
- package/docs/toggle-group.md +119 -0
- package/docs/toggle.md +94 -0
- package/docs/toolbar.md +138 -0
- package/docs/tooltip.md +128 -0
- package/package.json +102 -3
package/docs/toggle.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Toggle
|
|
2
|
+
|
|
3
|
+
> A two-state button (bold, mute, pin), after Base UI's Toggle: a native `<button type="button" aria-pressed>`. Its label stays the same; `aria-pressed` tells pressed from not. On its own the component presses it, in an island, and `bind:pressed` keeps a signal in sync; inside a [Toggle Group](toggle-group.md) the group presses it, on server pages too.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { Toggle } from "@lumiose/ui"; // or "@lumiose/ui/toggle"
|
|
7
|
+
import "@lumiose/ui/themes/default.css"; // optional: the default theme
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Anatomy
|
|
11
|
+
|
|
12
|
+
```tsx
|
|
13
|
+
<Toggle aria-label="Bold">B</Toggle>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
| Part | Renders |
|
|
17
|
+
| -------- | ----------------------------------------------------------- |
|
|
18
|
+
| `Toggle` | `<button type="button" aria-pressed="true">` (or `"false"`) |
|
|
19
|
+
|
|
20
|
+
## API
|
|
21
|
+
|
|
22
|
+
### Toggle
|
|
23
|
+
|
|
24
|
+
| Prop | Type | Default | |
|
|
25
|
+
| ----------------- | ---------------------------- | ------- | ----------------------------------------------------------------------- |
|
|
26
|
+
| `pressed` | `boolean` | | Presses or releases it when it changes. Pair it with `onPressedChange`. |
|
|
27
|
+
| `defaultPressed` | `boolean` | `false` | Whether it starts pressed. |
|
|
28
|
+
| `onPressedChange` | `(pressed: boolean) => void` | | Called after the user presses or releases it. |
|
|
29
|
+
| `bind:pressed` | `Signal<boolean>` | | Stands in for `pressed` and `onPressedChange`. |
|
|
30
|
+
| `value` | `string` | | Its identity in a Toggle Group's `value`. |
|
|
31
|
+
|
|
32
|
+
Every other native button prop passes through: `disabled`, `id`, `class`, `aria-label`, `aria-*`,
|
|
33
|
+
`onClick` (`type` is always `"button"`).
|
|
34
|
+
|
|
35
|
+
**Differences from Base UI's Toggle.** No `nativeButton` or `render`. `onPressedChange` gets the new
|
|
36
|
+
state only; in a group, a `pressedchange` listener can cancel the press with
|
|
37
|
+
`event.preventDefault()`.
|
|
38
|
+
|
|
39
|
+
## Example
|
|
40
|
+
|
|
41
|
+
```tsx check
|
|
42
|
+
import { Toggle } from "@lumiose/ui";
|
|
43
|
+
import { signal } from "lumiose";
|
|
44
|
+
|
|
45
|
+
export function PinToggle() {
|
|
46
|
+
const pinned = signal(false);
|
|
47
|
+
return (
|
|
48
|
+
<p>
|
|
49
|
+
<Toggle bind:pressed={pinned} class="lu-toggle">
|
|
50
|
+
Pin
|
|
51
|
+
</Toggle>{" "}
|
|
52
|
+
{pinned() ? "Pinned to the top." : "Not pinned."}
|
|
53
|
+
</p>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Give an icon-only toggle an `aria-label` that names the action ("Bold"), not its state.
|
|
59
|
+
|
|
60
|
+
## Styling
|
|
61
|
+
|
|
62
|
+
**With the default theme**, put `lu-toggle` on it: a bordered 36px button that, pressed, takes a
|
|
63
|
+
filled surface and a bar along its bottom edge, so the state shows by shape and not by colour alone.
|
|
64
|
+
|
|
65
|
+
**With your own CSS**, style the button and its state: `[aria-pressed="true"]`, `:hover`,
|
|
66
|
+
`:focus-visible`, `:disabled`.
|
|
67
|
+
|
|
68
|
+
Toggle ships no stylesheet: it needs none to work.
|
|
69
|
+
|
|
70
|
+
## Keyboard and focus
|
|
71
|
+
|
|
72
|
+
| Key | Does |
|
|
73
|
+
| ------------- | ---------------------------- |
|
|
74
|
+
| Enter / Space | Presses or releases it. |
|
|
75
|
+
| Tab | Moves on: one tab stop each. |
|
|
76
|
+
|
|
77
|
+
In a Toggle Group, the group's arrow keys move between the toggles instead.
|
|
78
|
+
|
|
79
|
+
## Without JavaScript
|
|
80
|
+
|
|
81
|
+
A native button with its state in `aria-pressed` from the server. On its own, a Toggle is pressed
|
|
82
|
+
by the component, so it needs an island (its state is for your code anyway); in a Toggle Group the
|
|
83
|
+
group's script presses it on server pages too. For a value a form submits, use a
|
|
84
|
+
[Checkbox](checkbox.md) or a [Switch](switch.md).
|
|
85
|
+
|
|
86
|
+
## Browser support
|
|
87
|
+
|
|
88
|
+
`aria-pressed` on a native button: every browser and screen reader.
|
|
89
|
+
|
|
90
|
+
## Related
|
|
91
|
+
|
|
92
|
+
- [Toggle Group](toggle-group.md): toggles that share one value, one or several pressed.
|
|
93
|
+
- [Switch](switch.md): an on/off setting submitted with a form.
|
|
94
|
+
- [Button](button.md): a plain button.
|
package/docs/toolbar.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Toolbar
|
|
2
|
+
|
|
3
|
+
> A set of controls with one tab stop, after Base UI's Toolbar and the APG toolbar pattern: `<lu-toolbar role="toolbar">` moves focus between its items with the arrow keys, Home and End, following the orientation and the text direction. Its items are native: buttons that stay focusable when disabled, links, inputs, and the toggles of a [Toggle Group](toggle-group.md) inside it. In the server HTML every item is a tab stop, so each is reachable without JavaScript; a 1.1 kB script makes them one tab stop and moves it.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { Toolbar } from "@lumiose/ui"; // or "@lumiose/ui/toolbar"
|
|
7
|
+
import "@lumiose/ui/toolbar.css"; // or "@lumiose/ui/styles.css" for every component
|
|
8
|
+
import "@lumiose/ui/themes/default.css"; // optional: the default theme
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Anatomy
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
<Toolbar.Root>
|
|
15
|
+
<Toolbar.Button />
|
|
16
|
+
<Toolbar.Link />
|
|
17
|
+
<Toolbar.Separator />
|
|
18
|
+
<Toolbar.Group>
|
|
19
|
+
<Toolbar.Button />
|
|
20
|
+
</Toolbar.Group>
|
|
21
|
+
<Toolbar.Input />
|
|
22
|
+
</Toolbar.Root>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
| Part | Renders |
|
|
26
|
+
| ----------- | -------------------------------------------------------------------------------------------------- |
|
|
27
|
+
| `Root` | `<lu-toolbar role="toolbar" aria-orientation data-orientation>` |
|
|
28
|
+
| `Button` | A [Button](button.md): `<button type="button">`, `aria-disabled` when disabled (still focusable) |
|
|
29
|
+
| `Link` | `<a>` |
|
|
30
|
+
| `Input` | `<input>`, `aria-disabled` and read-only when disabled (still focusable) |
|
|
31
|
+
| `Group` | `<div role="group">` |
|
|
32
|
+
| `Separator` | A [Separator](separator.md), `<hr>`, across the toolbar: vertical in a row, horizontal in a column |
|
|
33
|
+
|
|
34
|
+
Every item gets `data-orientation`; the script gives the tab stop `tabindex="0"` and the other
|
|
35
|
+
items `"-1"`. Name the toolbar with `aria-label` or `aria-labelledby`.
|
|
36
|
+
|
|
37
|
+
## API
|
|
38
|
+
|
|
39
|
+
### Toolbar.Root
|
|
40
|
+
|
|
41
|
+
| Prop | Type | Default | |
|
|
42
|
+
| ------------- | ---------------------------- | -------------- | ---------------------------------------------------------------------- |
|
|
43
|
+
| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | A row moved through with Left and Right, or a column with Up and Down. |
|
|
44
|
+
| `loopFocus` | `boolean` | `true` | The arrow keys go from the last item to the first, and back. |
|
|
45
|
+
| `disabled` | `boolean` | `false` | Disables every item but links; they stay focusable. |
|
|
46
|
+
|
|
47
|
+
### Toolbar.Button, Toolbar.Input and Toolbar.Group
|
|
48
|
+
|
|
49
|
+
| Prop | Type | Default | |
|
|
50
|
+
| ----------------------- | --------- | ------- | ------------------------------------------------------------------ |
|
|
51
|
+
| `disabled` | `boolean` | `false` | Disables the item (a Group: its items). |
|
|
52
|
+
| `focusableWhenDisabled` | `boolean` | `true` | Button and Input: disabled, they stay focusable (`aria-disabled`). |
|
|
53
|
+
|
|
54
|
+
Every other native prop passes through. A `ToggleGroup` inside a toolbar keeps pressing its
|
|
55
|
+
toggles but leaves the arrow keys and the tab stop to the toolbar; a Menu or Popover trigger inside
|
|
56
|
+
it is an item too. A disabled Root or Group reaches them: its toggles and triggers render
|
|
57
|
+
`aria-disabled="true"` and stay focusable, but press and open nothing, without JavaScript too (the
|
|
58
|
+
triggers lose `popovertarget`). A Menu in a vertical toolbar opens beside it, at its inline end, as
|
|
59
|
+
in a vertical [Menubar](menubar.md). What a Menu's or a Popover's popup holds is not the toolbar's:
|
|
60
|
+
its toggles and triggers keep their own tab stops.
|
|
61
|
+
|
|
62
|
+
**Differences from Base UI's Toolbar.** No `render` or `nativeButton`: put other triggers
|
|
63
|
+
(`Menu.Trigger`, `Popover.Trigger`) in the toolbar directly. Disabled items are `aria-disabled`
|
|
64
|
+
rather than `data-disabled` and `data-focusable`.
|
|
65
|
+
|
|
66
|
+
## Example
|
|
67
|
+
|
|
68
|
+
```tsx check
|
|
69
|
+
import { Toggle, ToggleGroup, Toolbar } from "@lumiose/ui";
|
|
70
|
+
|
|
71
|
+
export function FormattingToolbar() {
|
|
72
|
+
return (
|
|
73
|
+
<Toolbar.Root aria-label="Formatting" class="lu-toolbar">
|
|
74
|
+
<ToggleGroup aria-label="Text style" multiple class="lu-toggle-group">
|
|
75
|
+
<Toggle value="bold" class="lu-toggle">
|
|
76
|
+
Bold
|
|
77
|
+
</Toggle>
|
|
78
|
+
<Toggle value="italic" class="lu-toggle">
|
|
79
|
+
Italic
|
|
80
|
+
</Toggle>
|
|
81
|
+
</ToggleGroup>
|
|
82
|
+
<Toolbar.Separator />
|
|
83
|
+
<Toolbar.Button class="lu-button">Undo</Toolbar.Button>
|
|
84
|
+
<Toolbar.Button class="lu-button" disabled>
|
|
85
|
+
Redo
|
|
86
|
+
</Toolbar.Button>
|
|
87
|
+
<Toolbar.Link href="/help">Help</Toolbar.Link>
|
|
88
|
+
</Toolbar.Root>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Styling
|
|
94
|
+
|
|
95
|
+
**With the default theme**, put `lu-toolbar` on `Root`: a bordered row (a column when vertical)
|
|
96
|
+
that wraps on narrow screens, groups in rows, separators as 1px lines across it, links as tall as
|
|
97
|
+
the buttons. Style the items with their own classes (`lu-button`, `lu-toggle`, `lu-input`).
|
|
98
|
+
|
|
99
|
+
**With your own CSS**, style the parts through `class`, `[data-orientation]`, `[role="group"]`,
|
|
100
|
+
`hr` and the items' `[aria-disabled="true"]` and `:focus-visible`.
|
|
101
|
+
|
|
102
|
+
`@lumiose/ui/toolbar.css` makes `<lu-toolbar>` a block box, as Base UI's `<div>`.
|
|
103
|
+
|
|
104
|
+
## Keyboard and focus
|
|
105
|
+
|
|
106
|
+
| Key | Does |
|
|
107
|
+
| ------------------------ | -------------------------------------------------------------------------------- |
|
|
108
|
+
| Tab | Into the toolbar on the last focused item (at first, the first), and out of it. |
|
|
109
|
+
| Arrow Right / Arrow Left | In a row: the next or previous item (swapped right to left), looping. |
|
|
110
|
+
| Arrow Down / Arrow Up | In a column: the next or previous item, looping. |
|
|
111
|
+
| Home / End | The first or last item. |
|
|
112
|
+
| Enter / Space | Acts on the item: presses a button or a toggle, follows a link. |
|
|
113
|
+
| Arrow Right | In a column, on a Menu's trigger: opens the menu beside it (Left right to left). |
|
|
114
|
+
|
|
115
|
+
Natively disabled controls are skipped. When the item with the tab stop is removed, disabled or
|
|
116
|
+
hidden, the first item takes it. A key an item handles itself (a Menu trigger's) moves nothing. A
|
|
117
|
+
control keeps the keys it needs: a text field its
|
|
118
|
+
caret's Left and Right (focus moves on at its start or end; with Shift they select) and Home and
|
|
119
|
+
End, a number field or a slider its arrows, a select Up and Down. A text field the arrows reach has
|
|
120
|
+
its text selected, as in Base UI. Base UI advises at most one input, last.
|
|
121
|
+
|
|
122
|
+
## Without JavaScript, and the element's script
|
|
123
|
+
|
|
124
|
+
The server HTML is a labelled toolbar whose items are all tab stops, so Tab reaches each one
|
|
125
|
+
without JavaScript or before the script runs. `<lu-toolbar>`'s script (1.1 kB brotli with Button's)
|
|
126
|
+
makes the toolbar one tab stop, moves focus with the keys and keeps the tab stop on the last focused
|
|
127
|
+
item.
|
|
128
|
+
|
|
129
|
+
## Browser support
|
|
130
|
+
|
|
131
|
+
Custom elements and `role="toolbar"`: every supported browser and screen reader.
|
|
132
|
+
|
|
133
|
+
## Related
|
|
134
|
+
|
|
135
|
+
- [Toggle Group](toggle-group.md): toggles that share a value, in or out of a toolbar.
|
|
136
|
+
- [Button](button.md): the items' button, focusable when disabled.
|
|
137
|
+
- [Separator](separator.md): the line between groups.
|
|
138
|
+
- [Menu](menu.md): a menu opened from a toolbar button.
|
package/docs/tooltip.md
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Tooltip
|
|
2
|
+
|
|
3
|
+
> A short tip next to its trigger, on hover or keyboard focus: a native `popover="hint"` with `role="tooltip"`, placed with CSS anchor positioning. The trigger is described by the tip in server HTML, so screen readers announce it without hovering; a 2.0 kB script opens and closes it as Base UI's tooltip does. `bind:open` (or `open` and `onOpenChange`) keeps a signal in sync in islands.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { Tooltip } from "@lumiose/ui"; // or "@lumiose/ui/tooltip"
|
|
7
|
+
import "@lumiose/ui/tooltip.css"; // places the tip next to its trigger
|
|
8
|
+
import "@lumiose/ui/themes/default.css"; // optional: the default theme
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Anatomy
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
<Tooltip.Root>
|
|
15
|
+
<Tooltip.Trigger />
|
|
16
|
+
<Tooltip.Popup />
|
|
17
|
+
</Tooltip.Root>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
| Part | Renders |
|
|
21
|
+
| --------- | ----------------------------------------------------------------------------------------------------------- |
|
|
22
|
+
| `Root` | No element. Holds the ids the parts share. |
|
|
23
|
+
| `Trigger` | `<button type="button" aria-describedby="…">`, with an `anchor-name` in its `style`. |
|
|
24
|
+
| `Popup` | `<span popover="hint" role="tooltip" data-side="…" data-align="…">`: a `<span>`, so it fits in a paragraph. |
|
|
25
|
+
|
|
26
|
+
**Accessibility model.** The tooltip follows the
|
|
27
|
+
[WAI-ARIA tooltip pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tooltip/): the tip is the
|
|
28
|
+
trigger's description, always, not only while it shows. It is not its name: give an icon-only
|
|
29
|
+
trigger an `aria-label` (Base UI asks the same).
|
|
30
|
+
|
|
31
|
+
## API
|
|
32
|
+
|
|
33
|
+
### Tooltip.Root
|
|
34
|
+
|
|
35
|
+
| Prop | Type | Default | |
|
|
36
|
+
| ----------------------- | ------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
37
|
+
| `open` | `boolean` | | Opens or closes the tip when it changes, in the browser. |
|
|
38
|
+
| `onOpenChange` | `(open: boolean) => void` | | Called after the tip opens or closes, whatever did it: hover, focus, Escape, `open`. |
|
|
39
|
+
| `bind:open` | `Signal<boolean>` | | Stands in for `open` and `onOpenChange`. |
|
|
40
|
+
| `disabled` | `boolean` | `false` | Hover and focus do not open the tip; `open` still does. |
|
|
41
|
+
| `disableHoverablePopup` | `boolean` | `false` | Closes the tip as soon as the pointer leaves the trigger, instead of letting it move onto the tip. WCAG 1.4.13 asks for hoverable content: use it only for tips that cover something the user needs. |
|
|
42
|
+
|
|
43
|
+
### Tooltip.Trigger
|
|
44
|
+
|
|
45
|
+
| Prop | Type | Default | |
|
|
46
|
+
| -------------- | --------- | ------- | ---------------------------------------------------------------------- |
|
|
47
|
+
| `delay` | `number` | `600` | How long the pointer rests on the trigger before the tip opens, in ms. |
|
|
48
|
+
| `closeDelay` | `number` | `0` | How long the tip stays after the pointer leaves, in ms. |
|
|
49
|
+
| `closeOnClick` | `boolean` | `true` | A click (or Enter, or Space) on the trigger closes the tip. |
|
|
50
|
+
|
|
51
|
+
The trigger takes a `<button>`'s props too; an `aria-describedby` you pass is kept, after the tip.
|
|
52
|
+
These props reach the client script as attributes of the trigger: `data-delay`,
|
|
53
|
+
`data-close-delay`, `data-close-on-click="false"`, and `data-trigger-disabled` from `Root`'s
|
|
54
|
+
`disabled`.
|
|
55
|
+
|
|
56
|
+
### Tooltip.Popup
|
|
57
|
+
|
|
58
|
+
The placement props of [Popover.Popup](popover.md#popoverpopup): `side` (default `"top"`),
|
|
59
|
+
`align`, `sideOffset` and `alignOffset`.
|
|
60
|
+
|
|
61
|
+
## Example
|
|
62
|
+
|
|
63
|
+
```tsx check
|
|
64
|
+
import { Tooltip } from "@lumiose/ui";
|
|
65
|
+
|
|
66
|
+
export function BoldButton() {
|
|
67
|
+
return (
|
|
68
|
+
<Tooltip.Root>
|
|
69
|
+
<Tooltip.Trigger class="lu-button" aria-label="Bold">
|
|
70
|
+
B
|
|
71
|
+
</Tooltip.Trigger>
|
|
72
|
+
<Tooltip.Popup class="lu-tooltip">Bold (Ctrl+B)</Tooltip.Popup>
|
|
73
|
+
</Tooltip.Root>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Styling
|
|
79
|
+
|
|
80
|
+
**With the default theme**, put `lu-tooltip` on `Popup`: inverted colours, a 6px gap and a fade.
|
|
81
|
+
|
|
82
|
+
**With your own CSS**, style the tip through `class`, `:popover-open`, `[data-side]` and
|
|
83
|
+
`[data-align]`, and the offsets through `--lu-side-offset` and `--lu-align-offset`, as for
|
|
84
|
+
[Popover](popover.md#styling). `@lumiose/ui/tooltip.css` holds the placement both share.
|
|
85
|
+
|
|
86
|
+
## Keyboard and focus
|
|
87
|
+
|
|
88
|
+
The [WAI-ARIA tooltip pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tooltip/), with Base UI's
|
|
89
|
+
and Zag's opening rules:
|
|
90
|
+
|
|
91
|
+
| Key | Does |
|
|
92
|
+
| ------------ | -------------------------------------------------------------------------------- |
|
|
93
|
+
| Tab | Focusing the trigger from the keyboard opens the tip at once; leaving closes it. |
|
|
94
|
+
| Escape | Closes the tip, and only the tip: a dialog or popover around it stays open. |
|
|
95
|
+
| Enter, Space | Activate the trigger and close the tip, unless `closeOnClick={false}`. |
|
|
96
|
+
|
|
97
|
+
- **Opens** after `delay` of hover with a mouse or a pen, or at once on keyboard focus
|
|
98
|
+
(`:focus-visible`). A touch never opens it, and neither does the focus a mouse click gives.
|
|
99
|
+
- **One at a time.** Within 400 ms of another tooltip closing, or while one is open, the next one
|
|
100
|
+
opens without delay, so moving along a toolbar reads each tip.
|
|
101
|
+
- **A press before the delay** cancels the opening.
|
|
102
|
+
- **Closes** on Escape (only the tip: a dialog or popover around it stays open), on a press, on a
|
|
103
|
+
click of the trigger (unless `closeOnClick={false}`), on blur, and when something around the
|
|
104
|
+
trigger scrolls.
|
|
105
|
+
- **Hoverable** (WCAG 1.4.13): the pointer can move from the trigger onto the tip, across the gap,
|
|
106
|
+
and the tip stays; it closes `closeDelay` after the pointer leaves both.
|
|
107
|
+
|
|
108
|
+
A tooltip inside a popover keeps the popover open, and one inside a dialog belongs inside its
|
|
109
|
+
`Popup` (everything outside a modal dialog is inert).
|
|
110
|
+
|
|
111
|
+
## Without JavaScript, and the client script
|
|
112
|
+
|
|
113
|
+
Without JavaScript the tip never shows, but the trigger keeps its description: screen readers
|
|
114
|
+
announce it. A page that renders a `Tooltip` loads a 2.0 kB (brotli) module once, without the
|
|
115
|
+
Lumiose runtime; islands bundle it. It opens and closes every tooltip on the page, and places them
|
|
116
|
+
in browsers without CSS anchor positioning.
|
|
117
|
+
|
|
118
|
+
## Browser support
|
|
119
|
+
|
|
120
|
+
- **`popover="hint"`**: Chrome and Edge from 133 (complete in 151), Firefox from 149 (complete in
|
|
121
|
+
153). Safari reads it as `popover="manual"`: the client script does the same work there.
|
|
122
|
+
- **CSS anchor positioning**: Chrome and Edge 125, Firefox 147, Safari 26. Elsewhere the client
|
|
123
|
+
script places the tip.
|
|
124
|
+
|
|
125
|
+
## Related
|
|
126
|
+
|
|
127
|
+
- [UI components](README.md): the approach, styling, the default theme and the other components.
|
|
128
|
+
- [Popover](popover.md): a popup with interactive content, opened by a click.
|
package/package.json
CHANGED
|
@@ -1,6 +1,105 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lumiose/ui",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Accessible, unstyled UI primitives for Lumiose: native elements first (dialog, popover, invoker commands, anchor positioning), light-DOM custom elements only where the platform falls short.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/itsjavi/lumiose.git",
|
|
9
|
+
"directory": "packages/ui"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"docs"
|
|
14
|
+
],
|
|
15
|
+
"type": "module",
|
|
16
|
+
"sideEffects": [
|
|
17
|
+
"**/*.css",
|
|
18
|
+
"**/*.script.ts"
|
|
19
|
+
],
|
|
20
|
+
"exports": {
|
|
21
|
+
".": "./dist/index.ts",
|
|
22
|
+
"./dialog": "./dist/dialog/dialog.tsx",
|
|
23
|
+
"./styles.css": "./dist/styles.css",
|
|
24
|
+
"./dialog.css": "./dist/dialog/dialog.css",
|
|
25
|
+
"./popover": "./dist/popover/popover.tsx",
|
|
26
|
+
"./popover.css": "./dist/popover/popover.css",
|
|
27
|
+
"./tooltip": "./dist/tooltip/tooltip.tsx",
|
|
28
|
+
"./tooltip.css": "./dist/tooltip/tooltip.css",
|
|
29
|
+
"./tabs": "./dist/tabs/tabs.tsx",
|
|
30
|
+
"./tabs.css": "./dist/tabs/tabs.css",
|
|
31
|
+
"./menu": "./dist/menu/menu.tsx",
|
|
32
|
+
"./menu.css": "./dist/menu/menu.css",
|
|
33
|
+
"./combobox": "./dist/combobox/combobox.tsx",
|
|
34
|
+
"./combobox.css": "./dist/combobox/combobox.css",
|
|
35
|
+
"./select": "./dist/select/select.tsx",
|
|
36
|
+
"./select.css": "./dist/select/select.css",
|
|
37
|
+
"./field": "./dist/field/field.tsx",
|
|
38
|
+
"./field.css": "./dist/field/field.css",
|
|
39
|
+
"./fieldset": "./dist/fieldset/fieldset.tsx",
|
|
40
|
+
"./form": "./dist/form/form.tsx",
|
|
41
|
+
"./input": "./dist/input/input.tsx",
|
|
42
|
+
"./checkbox": "./dist/checkbox/checkbox.tsx",
|
|
43
|
+
"./checkbox-group": "./dist/checkbox-group/checkbox-group.tsx",
|
|
44
|
+
"./radio-group": "./dist/radio-group/radio-group.tsx",
|
|
45
|
+
"./switch": "./dist/switch/switch.tsx",
|
|
46
|
+
"./collapsible": "./dist/collapsible/collapsible.tsx",
|
|
47
|
+
"./collapsible.css": "./dist/collapsible/collapsible.css",
|
|
48
|
+
"./accordion": "./dist/accordion/accordion.tsx",
|
|
49
|
+
"./accordion.css": "./dist/accordion/accordion.css",
|
|
50
|
+
"./toast": "./dist/toast/toast.tsx",
|
|
51
|
+
"./toast.css": "./dist/toast/toast.css",
|
|
52
|
+
"./button": "./dist/button/button.tsx",
|
|
53
|
+
"./toggle": "./dist/toggle/toggle.tsx",
|
|
54
|
+
"./toggle-group": "./dist/toggle-group/toggle-group.tsx",
|
|
55
|
+
"./progress": "./dist/progress/progress.tsx",
|
|
56
|
+
"./meter": "./dist/meter/meter.tsx",
|
|
57
|
+
"./slider": "./dist/slider/slider.tsx",
|
|
58
|
+
"./slider.css": "./dist/slider/slider.css",
|
|
59
|
+
"./context-menu": "./dist/context-menu/context-menu.tsx",
|
|
60
|
+
"./context-menu.css": "./dist/context-menu/context-menu.css",
|
|
61
|
+
"./drawer": "./dist/drawer/drawer.tsx",
|
|
62
|
+
"./drawer.css": "./dist/drawer/drawer.css",
|
|
63
|
+
"./number-field": "./dist/number-field/number-field.tsx",
|
|
64
|
+
"./number-field.css": "./dist/number-field/number-field.css",
|
|
65
|
+
"./autocomplete": "./dist/autocomplete/autocomplete.tsx",
|
|
66
|
+
"./autocomplete.css": "./dist/combobox/combobox.css",
|
|
67
|
+
"./separator": "./dist/separator/separator.tsx",
|
|
68
|
+
"./separator.css": "./dist/separator/separator.css",
|
|
69
|
+
"./avatar": "./dist/avatar/avatar.tsx",
|
|
70
|
+
"./avatar.css": "./dist/avatar/avatar.css",
|
|
71
|
+
"./otp-field": "./dist/otp-field/otp-field.tsx",
|
|
72
|
+
"./otp-field.css": "./dist/otp-field/otp-field.css",
|
|
73
|
+
"./preview-card": "./dist/preview-card/preview-card.tsx",
|
|
74
|
+
"./preview-card.css": "./dist/preview-card/preview-card.css",
|
|
75
|
+
"./toolbar": "./dist/toolbar/toolbar.tsx",
|
|
76
|
+
"./toolbar.css": "./dist/toolbar/toolbar.css",
|
|
77
|
+
"./scroll-area": "./dist/scroll-area/scroll-area.tsx",
|
|
78
|
+
"./scroll-area.css": "./dist/scroll-area/scroll-area.css",
|
|
79
|
+
"./menubar": "./dist/menubar/menubar.tsx",
|
|
80
|
+
"./menubar.css": "./dist/menubar/menubar.css",
|
|
81
|
+
"./navigation-menu": "./dist/navigation-menu/navigation-menu.tsx",
|
|
82
|
+
"./navigation-menu.css": "./dist/navigation-menu/navigation-menu.css",
|
|
83
|
+
"./themes/default.css": "./dist/themes/default.css",
|
|
84
|
+
"./package.json": "./package.json"
|
|
85
|
+
},
|
|
86
|
+
"devDependencies": {
|
|
87
|
+
"@lumiose/compiler": "0.6.0",
|
|
88
|
+
"@lumiose/vite": "0.6.0",
|
|
89
|
+
"@types/node": "^24.0.0",
|
|
90
|
+
"happy-dom": "^20.14.5",
|
|
91
|
+
"lumiose": "0.6.0",
|
|
92
|
+
"vite": "^8.3.4"
|
|
93
|
+
},
|
|
94
|
+
"peerDependencies": {
|
|
95
|
+
"lumiose": "^0.6.0"
|
|
96
|
+
},
|
|
97
|
+
"scripts": {
|
|
98
|
+
"typecheck": "tsc -p tsconfig.json",
|
|
99
|
+
"test": "vitest run",
|
|
100
|
+
"size": "node scripts/size.ts",
|
|
101
|
+
"docs:gen": "node scripts/gen-llms.ts",
|
|
102
|
+
"docs:check": "node scripts/gen-llms.ts --check",
|
|
103
|
+
"build": "node scripts/build.ts"
|
|
104
|
+
}
|
|
6
105
|
}
|