@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.
Files changed (149) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +53 -2
  3. package/dist/accordion/accordion.css +20 -0
  4. package/dist/accordion/accordion.script.ts +66 -0
  5. package/dist/accordion/accordion.tsx +288 -0
  6. package/dist/animation/animation.ts +34 -0
  7. package/dist/autocomplete/autocomplete.tsx +239 -0
  8. package/dist/avatar/avatar.css +54 -0
  9. package/dist/avatar/avatar.script.ts +96 -0
  10. package/dist/avatar/avatar.tsx +110 -0
  11. package/dist/button/button.script.ts +35 -0
  12. package/dist/button/button.tsx +40 -0
  13. package/dist/checkbox/checkbox.script.ts +225 -0
  14. package/dist/checkbox/checkbox.tsx +156 -0
  15. package/dist/checkbox/context.ts +17 -0
  16. package/dist/checkbox-group/checkbox-group.tsx +111 -0
  17. package/dist/collapsible/collapsible.css +12 -0
  18. package/dist/collapsible/collapsible.script.ts +7 -0
  19. package/dist/collapsible/collapsible.tsx +153 -0
  20. package/dist/collapsible/panel.ts +115 -0
  21. package/dist/combobox/combobox.css +37 -0
  22. package/dist/combobox/combobox.script.ts +974 -0
  23. package/dist/combobox/combobox.tsx +556 -0
  24. package/dist/combobox/context.ts +46 -0
  25. package/dist/context-menu/context-menu.css +16 -0
  26. package/dist/context-menu/context-menu.script.ts +172 -0
  27. package/dist/context-menu/context-menu.tsx +178 -0
  28. package/dist/dialog/dialog.css +35 -0
  29. package/dist/dialog/dialog.script.ts +269 -0
  30. package/dist/dialog/dialog.tsx +214 -0
  31. package/dist/drawer/drawer.css +38 -0
  32. package/dist/drawer/drawer.script.ts +207 -0
  33. package/dist/drawer/drawer.tsx +113 -0
  34. package/dist/field/context.ts +156 -0
  35. package/dist/field/field.css +16 -0
  36. package/dist/field/field.script.ts +422 -0
  37. package/dist/field/field.tsx +465 -0
  38. package/dist/fieldset/fieldset.tsx +41 -0
  39. package/dist/form/form.tsx +50 -0
  40. package/dist/form/native.ts +21 -0
  41. package/dist/form/reset.ts +53 -0
  42. package/dist/index.ts +261 -0
  43. package/dist/input/input.tsx +67 -0
  44. package/dist/layer/layer.ts +191 -0
  45. package/dist/locale/digits.ts +21 -0
  46. package/dist/menu/menu.css +22 -0
  47. package/dist/menu/menu.script.ts +591 -0
  48. package/dist/menu/menu.tsx +520 -0
  49. package/dist/menubar/menubar.css +16 -0
  50. package/dist/menubar/menubar.script.ts +195 -0
  51. package/dist/menubar/menubar.tsx +67 -0
  52. package/dist/meter/meter.tsx +134 -0
  53. package/dist/navigation-menu/navigation-menu.css +14 -0
  54. package/dist/navigation-menu/navigation-menu.script.ts +466 -0
  55. package/dist/navigation-menu/navigation-menu.tsx +266 -0
  56. package/dist/number-field/format.ts +70 -0
  57. package/dist/number-field/number-field.css +38 -0
  58. package/dist/number-field/number-field.script.ts +490 -0
  59. package/dist/number-field/number-field.tsx +403 -0
  60. package/dist/otp-field/otp-field.css +45 -0
  61. package/dist/otp-field/otp-field.script.ts +330 -0
  62. package/dist/otp-field/otp-field.tsx +320 -0
  63. package/dist/placement/place.ts +148 -0
  64. package/dist/placement/placement.css +155 -0
  65. package/dist/placement/placement.ts +60 -0
  66. package/dist/popover/hover.ts +178 -0
  67. package/dist/popover/popover.css +20 -0
  68. package/dist/popover/popover.script.ts +256 -0
  69. package/dist/popover/popover.tsx +317 -0
  70. package/dist/preview-card/preview-card.css +7 -0
  71. package/dist/preview-card/preview-card.script.ts +136 -0
  72. package/dist/preview-card/preview-card.tsx +128 -0
  73. package/dist/progress/progress.tsx +155 -0
  74. package/dist/progress/value.ts +23 -0
  75. package/dist/radio-group/radio-group.tsx +156 -0
  76. package/dist/scroll-area/scroll-area.css +72 -0
  77. package/dist/scroll-area/scroll-area.script.ts +281 -0
  78. package/dist/scroll-area/scroll-area.tsx +118 -0
  79. package/dist/select/select.css +25 -0
  80. package/dist/select/select.tsx +171 -0
  81. package/dist/separator/separator.css +12 -0
  82. package/dist/separator/separator.tsx +38 -0
  83. package/dist/slider/format.ts +33 -0
  84. package/dist/slider/slider.css +93 -0
  85. package/dist/slider/slider.script.ts +355 -0
  86. package/dist/slider/slider.tsx +374 -0
  87. package/dist/styles.css +30 -0
  88. package/dist/switch/switch.tsx +92 -0
  89. package/dist/tabs/tabs.css +54 -0
  90. package/dist/tabs/tabs.script.ts +225 -0
  91. package/dist/tabs/tabs.tsx +272 -0
  92. package/dist/themes/default.css +2111 -0
  93. package/dist/toast/command.ts +3 -0
  94. package/dist/toast/toast.css +68 -0
  95. package/dist/toast/toast.script.ts +516 -0
  96. package/dist/toast/toast.tsx +432 -0
  97. package/dist/toggle/context.ts +16 -0
  98. package/dist/toggle/toggle.tsx +99 -0
  99. package/dist/toggle-group/toggle-group.script.ts +166 -0
  100. package/dist/toggle-group/toggle-group.tsx +117 -0
  101. package/dist/toolbar/context.ts +18 -0
  102. package/dist/toolbar/toolbar.css +12 -0
  103. package/dist/toolbar/toolbar.script.ts +123 -0
  104. package/dist/toolbar/toolbar.tsx +180 -0
  105. package/dist/tooltip/tooltip.css +8 -0
  106. package/dist/tooltip/tooltip.script.ts +222 -0
  107. package/dist/tooltip/tooltip.tsx +149 -0
  108. package/docs/README.md +174 -0
  109. package/docs/accordion.md +208 -0
  110. package/docs/autocomplete.md +161 -0
  111. package/docs/avatar.md +198 -0
  112. package/docs/button.md +98 -0
  113. package/docs/checkbox-group.md +112 -0
  114. package/docs/checkbox.md +133 -0
  115. package/docs/collapsible.md +165 -0
  116. package/docs/combobox.md +369 -0
  117. package/docs/context-menu.md +197 -0
  118. package/docs/dialog.md +256 -0
  119. package/docs/drawer.md +204 -0
  120. package/docs/field.md +295 -0
  121. package/docs/fieldset.md +92 -0
  122. package/docs/form.md +131 -0
  123. package/docs/input.md +87 -0
  124. package/docs/installation.md +65 -0
  125. package/docs/llms-full.txt +6923 -0
  126. package/docs/llms.txt +53 -0
  127. package/docs/menu.md +365 -0
  128. package/docs/menubar.md +186 -0
  129. package/docs/meter.md +103 -0
  130. package/docs/navigation-menu.md +257 -0
  131. package/docs/number-field.md +181 -0
  132. package/docs/otp-field.md +160 -0
  133. package/docs/popover.md +271 -0
  134. package/docs/preview-card.md +130 -0
  135. package/docs/progress.md +115 -0
  136. package/docs/radio-group.md +127 -0
  137. package/docs/scroll-area.md +147 -0
  138. package/docs/select.md +236 -0
  139. package/docs/separator.md +116 -0
  140. package/docs/slider.md +184 -0
  141. package/docs/switch.md +98 -0
  142. package/docs/tabs.md +254 -0
  143. package/docs/theming.md +132 -0
  144. package/docs/toast.md +311 -0
  145. package/docs/toggle-group.md +119 -0
  146. package/docs/toggle.md +94 -0
  147. package/docs/toolbar.md +138 -0
  148. package/docs/tooltip.md +128 -0
  149. 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.
@@ -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.
@@ -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.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
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
  }