abaabil 1.1.0 → 1.3.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 (46) hide show
  1. package/CHANGELOG.md +177 -0
  2. package/README.md +329 -36
  3. package/dist/alert/a11y.js +1 -0
  4. package/dist/alert/alert.css +1 -0
  5. package/dist/alert/index.js +1 -0
  6. package/dist/alert/styled.js +1 -0
  7. package/dist/breadcrumb/a11y.js +1 -0
  8. package/dist/breadcrumb/breadcrumb.css +1 -0
  9. package/dist/breadcrumb/index.js +1 -0
  10. package/dist/breadcrumb/styled.js +1 -0
  11. package/dist/menu/a11y.js +2 -0
  12. package/dist/menu/index.js +1 -0
  13. package/dist/menu/menu.css +1 -0
  14. package/dist/menu/styled.js +1 -0
  15. package/dist/popover/a11y.js +1 -0
  16. package/dist/popover/index.js +1 -0
  17. package/dist/popover/popover.css +1 -0
  18. package/dist/popover/styled.js +1 -0
  19. package/dist/progress/a11y.js +2 -0
  20. package/dist/progress/index.js +1 -0
  21. package/dist/progress/progress.css +1 -0
  22. package/dist/progress/styled.js +1 -0
  23. package/dist/slider/a11y.js +2 -0
  24. package/dist/slider/index.js +1 -0
  25. package/dist/slider/slider.css +1 -0
  26. package/dist/slider/styled.js +1 -0
  27. package/dist/styles.css +1 -1
  28. package/dist/switch/a11y.js +2 -0
  29. package/dist/switch/index.js +1 -0
  30. package/dist/switch/styled.js +1 -0
  31. package/dist/switch/switch.css +1 -0
  32. package/dist/tabs/a11y.js +2 -0
  33. package/dist/tabs/index.js +2 -0
  34. package/dist/tabs/styled.js +1 -0
  35. package/dist/tabs/tabs.css +1 -0
  36. package/dist/textarea/a11y.js +2 -0
  37. package/dist/textarea/index.js +1 -0
  38. package/dist/textarea/styled.js +1 -0
  39. package/dist/textarea/textarea.css +1 -0
  40. package/dist/theme.css +1 -1
  41. package/dist/tooltip/a11y.js +2 -0
  42. package/dist/tooltip/index.js +1 -0
  43. package/dist/tooltip/styled.js +1 -0
  44. package/dist/tooltip/tooltip.css +1 -0
  45. package/llms.txt +122 -0
  46. package/package.json +45 -4
package/CHANGELOG.md CHANGED
@@ -4,6 +4,183 @@ All notable changes to this project are documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
6
6
 
7
+ ## [1.3.0] - 2026-09-21
8
+
9
+ ### Added
10
+
11
+ Five components, taking the library from thirteen to eighteen. Two of
12
+ them are the things people ask for most and are hard; three are nearly
13
+ free because the platform already does the work.
14
+
15
+ - **`abaabil/menu`** - the W3C APG menu button pattern on top of the
16
+ native Popover API. The browser supplies the top layer, the
17
+ outside-click dismissal and Escape; this supplies `role="menu"`, a
18
+ roving tabindex, Up/Down with wrapping, Home/End, multi-character
19
+ typeahead, Tab to close, and focus moving into the menu on open and
20
+ back to the trigger on close. `aria-expanded` is driven by the panel's
21
+ own `toggle` event rather than a second copy of the open state, since
22
+ the browser can close the panel without telling the component. Its
23
+ docs say when a menu is the wrong widget: a button revealing
24
+ navigation links is a popover, not a menu.
25
+ - **`abaabil/tooltip`** - shows on `:hover` and `:focus-within`, which
26
+ are selectors, so the `normal` and `styled` tiers need no JavaScript
27
+ at all. The `a11y` tier adds the two things CSS cannot: the
28
+ `aria-describedby` that makes a screen reader read it, and Escape to
29
+ dismiss, which WCAG 1.4.13 requires. The docs lead with when not to
30
+ use one.
31
+ - **`abaabil/progress`** - native `<progress>`. `value` has no default,
32
+ so omitting it gives a genuinely indeterminate bar rather than one
33
+ claiming to be 0% done. `valueText` sets `aria-valuetext`, because a
34
+ bar whose number is not a percentage is otherwise announced as one.
35
+ - **`abaabil/slider`** - native `<input type="range">`, so the keyboard
36
+ support and the min/max/step arithmetic are the platform's.
37
+ `formatValue` sets `aria-valuetext` and the optional visible
38
+ `<output>`, which is marked `aria-hidden` so the value is not
39
+ announced twice.
40
+ - **`abaabil/breadcrumb`** - a `<nav>` around an ordered list, because
41
+ the order is the information. Server-renderable at every tier. The
42
+ separator is drawn in CSS so it is never read aloud, the `<nav>` is
43
+ named, and the last item renders as text with `aria-current="page"`.
44
+
45
+ Thirty-six of the fifty-four entry points now carry no client directive.
46
+ Six components are server-renderable at every tier including `a11y`, up
47
+ from four.
48
+
49
+ `role="tooltip"` is deliberately **not** in tooltip's `normal` tier,
50
+ unlike `role="switch"` in switch's. The difference: a switch's role
51
+ changes how a reachable interactive control is announced, so it does
52
+ something on its own. A tooltip is only ever reached through the
53
+ `aria-describedby` on its trigger, so the role without that association
54
+ announces nothing, and it belongs with the wiring that gives it meaning.
55
+
56
+ Three bugs were found by driving these in a real browser, after 511
57
+ jsdom tests had passed. All three are the same shape: jsdom does not run
58
+ the thing that was broken.
59
+
60
+ - Menu focused its first item inside `requestAnimationFrame`, which does
61
+ not fire in a background tab, so focus silently never moved there. The
62
+ `toggle` event already fires with the panel in the top layer and
63
+ focusable, so the focus call is synchronous now.
64
+ - Menu read the active index from state, so two keydowns arriving in the
65
+ same tick both moved one step from the same origin: holding ArrowDown
66
+ advanced one item and stopped. The index is a ref now. Covered by a
67
+ test that dispatches two raw events inside one `act`, which is the
68
+ only way to reproduce it, since `fireEvent` flushes between calls.
69
+ - Tooltip put its fade on the shown state, so whenever the transition
70
+ did not advance the bubble stayed at opacity 0 while every selector
71
+ said it should be visible: a tooltip that silently never appears. The
72
+ fade is on the hidden state now and showing is instant, which is also
73
+ the better behaviour for a tooltip.
74
+
75
+ Two more came out of a full accessibility audit of all eighteen
76
+ components, run with axe against a real browser. Both were silent: the
77
+ component looked right, the tests passed, and the thing it claimed to add
78
+ was being discarded.
79
+
80
+ - **`popover/a11y` was not naming its panel at all.** A bare
81
+ `<div popover>` has no role, and `aria-label` is prohibited on a
82
+ generic element, so the label was dropped: the panel computed to role
83
+ `null` with no accessible name, and axe flags it as
84
+ `aria-prohibited-attr`. The panel is `role="group"` now, overridable
85
+ with `role`. Naming the panel was this tier's entire contribution, so
86
+ until now it added 484 bytes and achieved nothing.
87
+ - **`progress/a11y` was not naming its bar.** `<progress>` is a labelable
88
+ element, so `<label for>` is not wrong, but no accessible name is
89
+ computed from it: axe names an `<input>` from identical markup and
90
+ names neither `<progress>` nor `<meter>`. `aria-labelledby` now points
91
+ at the label, making the name explicit instead of leaving it to differ
92
+ between implementations.
93
+
94
+ The audit itself had to be redone once. The first pass rendered the
95
+ dialog open, and a modal dialog makes everything behind it inert, so the
96
+ sweep that reported zero violations had not actually examined the other
97
+ seventeen components.
98
+
99
+ ### Fixed
100
+
101
+ - `dist/combobox/index.js` and `dist/combobox/styled.js` had no size
102
+ budget. Combobox was the first component built and only its `a11y`
103
+ tier was ever listed, so two entry points had been unwatched since
104
+ 1.0.0. Found by the new registration check, not by noticing.
105
+
106
+ ### Added (tooling)
107
+
108
+ - `llms.txt`, shipped inside the package. It is the short version of the
109
+ README written for coding agents: the rules that are easy to get wrong
110
+ (no root export, import `theme.css` once, prefer the `a11y` tier) and
111
+ the per-component traps (popover needs an explicit `id`, alert has to
112
+ exist before it has a message, combobox and tabs are uncontrolled). An
113
+ agent with `node_modules` can read it without the network. It reaches
114
+ npm with the next release.
115
+ - `AGENTS.md` at the repository root, for agents changing the library
116
+ rather than using it: the enforced architecture rules, the conventions,
117
+ and the seven steps for adding a component.
118
+
119
+ ## [1.2.0] - 2026-09-21
120
+
121
+ ### Added
122
+
123
+ Five components, taking the library from eight to thirteen. Each ships the
124
+ same three tiers.
125
+
126
+ - **`abaabil/textarea`** - a native `<textarea>` with the same label,
127
+ description, error and `aria-describedby` wiring as `input`. The most
128
+ obvious gap in a library that already had five form controls.
129
+ - **`abaabil/switch`** - a native checkbox carrying `role="switch"`. The
130
+ role sits in the `normal` tier, not the `a11y` tier: it is the
131
+ component's identity, the way `<dialog>` and `<details>` are for the
132
+ components built on them, and without it the styled tier would look like
133
+ a switch while announcing itself as a checkbox. No `aria-checked`; the
134
+ native `checked` property already exposes the state.
135
+ - **`abaabil/popover`** - the native Popover API. The trigger, the top
136
+ layer, light-dismiss and Escape all come from the browser, so all three
137
+ tiers ship **zero JavaScript** and all three are server-renderable. It
138
+ needs an explicit `id` rather than generating one, because generating
139
+ one would mean `useId`, which would mean a client boundary, which would
140
+ cost the component the only property that makes it interesting. It is
141
+ the one component with a floor of its own: Chrome 114, Firefox 125,
142
+ Safari 17.
143
+ - **`abaabil/tabs`** - the W3C APG tabs pattern, with roving tabindex,
144
+ arrow keys, Home/End, both orientations and both activation modes. The
145
+ only new component whose `a11y` tier does substantial work, because tabs
146
+ have no native element behind them. Client at every tier for the same
147
+ reason combobox is.
148
+ - **`abaabil/alert`** - a live region. The role follows the variant, since
149
+ the right answer is the same every time and getting it wrong is the
150
+ common failure: `info` and `success` announce politely, `warning` and
151
+ `danger` interrupt. Read the caveat in the README about when a live
152
+ region actually announces; it decides whether this works at all.
153
+
154
+ Also added: `--color-success` and `--color-warning` semantic tokens, which
155
+ alert needs and nothing else previously did.
156
+
157
+ Twenty-six of the thirty-nine entry points now carry no client directive.
158
+ Four components are server-renderable at every tier including `a11y`
159
+ (button, accordion, popover, alert), up from two.
160
+
161
+ ### Fixed
162
+
163
+ Documentation errors from 1.1.0, all of the same kind: figures typed in by
164
+ hand that had stopped being true.
165
+
166
+
167
+
168
+ - Corrected the headline size comparison in `README.md`. It claimed
169
+ `dialog/a11y` at 674 B against `@radix-ui/react-dialog` at 3,422 B. The
170
+ Radix figure was measured by importing only `Dialog.Root`, which renders
171
+ nothing; a dialog you can actually open needs `Root`, `Trigger`, `Portal`,
172
+ `Overlay`, `Content`, `Title` and `Close`, and costs 13,493 B. Both sides
173
+ are now measured the same way, at 580 B against 13,493 B. The correction
174
+ is less favourable to Radix, not more.
175
+ - Regenerated the delivered-size table in `README.md`. It was still printing
176
+ 1.0.0's figures in the 1.1.0 release, several of them out by more than
177
+ 10% (`checkbox/a11y` read 600 B against an actual 534 B, `radio/a11y`
178
+ 542 B against 444 B). The table is now generated from the published
179
+ package rather than copied by hand.
180
+ - Noted in `README.md` that a `styled` entry can measure a byte or two below
181
+ its `normal` entry. That is compression noise: `styled` is `normal` plus a
182
+ CSS side-effect import, so the JavaScript is identical.
183
+
7
184
  ## [1.1.0] - 2026-09-21
8
185
 
9
186
  ### Added
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Minimal, themeable, accessible React components. Zero dependencies.
4
4
 
5
- Eight components: button, dialog, combobox, input, checkbox, radio, select, accordion.
5
+ Eighteen components: button, dialog, popover, menu, tooltip, combobox, input, textarea, checkbox, radio, switch, select, slider, accordion, tabs, breadcrumb, progress, alert.
6
6
 
7
7
  ```js
8
8
  import Button from 'abaabil/button/a11y'
@@ -15,7 +15,9 @@ function Example() {
15
15
 
16
16
  Each component ships three tiers, `normal`, `styled`, `a11y`, so you only pay for what you use. Two facts worth knowing up front:
17
17
 
18
- - A fully accessible `dialog/a11y` is **674 B gzipped**, against `@radix-ui/react-dialog` at **3,422 B**, both measured the same way: the component imported alone, bundled with esbuild, `react`/`react-dom` external, minified, then gzipped. Both numbers are JS only; Radix ships no stylesheet of its own, and it also bundles its own positioning and portal logic and predates a usable native `<dialog>`, so it isn't attempting exactly what abaabil does. The difference is mostly what the platform now gives you for free, not a claim of doing more with less.
18
+ - A fully accessible `dialog/a11y` is **580 B gzipped**, against `@radix-ui/react-dialog` at **13,493 B**. Both are measured the same way: a dialog you can actually open, imported with every part it needs, bundled with esbuild, `react`/`react-dom` external, minified, then gzipped. Both numbers are JS only; Radix ships no stylesheet of its own, and it also bundles its own positioning and portal logic and predates a usable native `<dialog>`, so it isn't attempting exactly what abaabil does. The difference is mostly what the platform now gives you for free, not a claim of doing more with less.
19
+
20
+ Earlier releases of this README compared 674 B against Radix at 3,422 B. That Radix figure was wrong: it was what you get importing only `Dialog.Root`, which renders nothing. The corrected comparison is less favourable to Radix, not more, which is why it is stated here rather than quietly changed.
19
21
  - `accordion/a11y` needs **no JavaScript at all**. Every tier of accordion, including `a11y`, is server-renderable, because it's built on native `<details>`/`<summary>` rather than a scripted widget.
20
22
 
21
23
  ## Install
@@ -38,34 +40,53 @@ The gzipped cost of importing each entry point on its own, React treated as exte
38
40
 
39
41
  | Entry | JS | CSS |
40
42
  |---|---:|---:|
41
- | `abaabil/button` | 189 B | - |
42
- | `abaabil/button/styled` | 200 B | 555 B |
43
- | `abaabil/button/a11y` | 559 B | 555 B |
44
- | `abaabil/dialog` | 161 B | - |
45
- | `abaabil/dialog/styled` | 171 B | 373 B |
46
- | `abaabil/dialog/a11y` | 572 B | 373 B |
47
- | `abaabil/combobox` | 454 B | - |
48
- | `abaabil/combobox/styled` | 456 B | 626 B |
49
- | `abaabil/combobox/a11y` | 1090 B | 626 B |
50
- | `abaabil/input` | 160 B | - |
51
- | `abaabil/input/styled` | 171 B | 500 B |
52
- | `abaabil/input/a11y` | 485 B | 500 B |
53
- | `abaabil/checkbox` | 161 B | - |
54
- | `abaabil/checkbox/styled` | 171 B | 636 B |
55
- | `abaabil/checkbox/a11y` | 600 B | 636 B |
56
- | `abaabil/radio` | 157 B | - |
57
- | `abaabil/radio/styled` | 168 B | 545 B |
58
- | `abaabil/radio/a11y` | 542 B | 545 B |
59
- | `abaabil/select` | 202 B | - |
60
- | `abaabil/select/styled` | 212 B | 769 B |
61
- | `abaabil/select/a11y` | 488 B | 769 B |
62
- | `abaabil/accordion` | 323 B | - |
63
- | `abaabil/accordion/styled` | 334 B | 533 B |
64
- | `abaabil/accordion/a11y` | 383 B | 533 B |
65
-
66
- Each entry point is bundled on its own with its full module graph (so, for example, `button/styled`'s cost already includes the `button` markup it imports) using Rollup (the same plugins as the real build: `@rollup/plugin-node-resolve` and `esbuild` in minify mode), `react`/`react-dom`/`*.css` external, then gzipped. The `styled` and `a11y` tiers also pull in their component's stylesheet as a side-effect import; the CSS column is that stylesheet's own gzipped size, separate from the JS number. `normal` tier entries have no CSS column because they import none.
67
-
68
- This is different from the per-file numbers in `SIZES.md`, which are produced with `preserveModules` and so under-count any tier that imports another module, such as `styled.js`, which shows only the bytes of its own file, not the `index.js` it re-exports. Re-run this yourself with the same setup (each entry bundled alone, React external, gzip the output) to reproduce these numbers.
43
+ | `abaabil/button` | 199 B | - |
44
+ | `abaabil/button/styled` | 198 B | 551 B |
45
+ | `abaabil/button/a11y` | 557 B | 551 B |
46
+ | `abaabil/dialog` | 168 B | - |
47
+ | `abaabil/dialog/styled` | 169 B | 371 B |
48
+ | `abaabil/dialog/a11y` | 580 B | 371 B |
49
+ | `abaabil/combobox` | 455 B | - |
50
+ | `abaabil/combobox/styled` | 455 B | 615 B |
51
+ | `abaabil/combobox/a11y` | 1,097 B | 615 B |
52
+ | `abaabil/input` | 166 B | - |
53
+ | `abaabil/input/styled` | 166 B | 497 B |
54
+ | `abaabil/input/a11y` | 501 B | 497 B |
55
+ | `abaabil/checkbox` | 168 B | - |
56
+ | `abaabil/checkbox/styled` | 168 B | 651 B |
57
+ | `abaabil/checkbox/a11y` | 534 B | 651 B |
58
+ | `abaabil/radio` | 165 B | - |
59
+ | `abaabil/radio/styled` | 166 B | 542 B |
60
+ | `abaabil/radio/a11y` | 444 B | 542 B |
61
+ | `abaabil/select` | 209 B | - |
62
+ | `abaabil/select/styled` | 210 B | 771 B |
63
+ | `abaabil/select/a11y` | 530 B | 771 B |
64
+ | `abaabil/accordion` | 320 B | - |
65
+ | `abaabil/accordion/styled` | 320 B | 528 B |
66
+ | `abaabil/accordion/a11y` | 364 B | 528 B |
67
+ | `abaabil/textarea` | 166 B | - |
68
+ | `abaabil/textarea/styled` | 166 B | 476 B |
69
+ | `abaabil/textarea/a11y` | 506 B | 476 B |
70
+ | `abaabil/switch` | 177 B | - |
71
+ | `abaabil/switch/styled` | 177 B | 585 B |
72
+ | `abaabil/switch/a11y` | 423 B | 585 B |
73
+ | `abaabil/popover` | 387 B | - |
74
+ | `abaabil/popover/styled` | 388 B | 436 B |
75
+ | `abaabil/popover/a11y` | 484 B | 436 B |
76
+ | `abaabil/tabs` | 349 B | - |
77
+ | `abaabil/tabs/styled` | 349 B | 529 B |
78
+ | `abaabil/tabs/a11y` | 786 B | 529 B |
79
+ | `abaabil/alert` | 190 B | - |
80
+ | `abaabil/alert/styled` | 190 B | 345 B |
81
+ | `abaabil/alert/a11y` | 341 B | 345 B |
82
+
83
+ Measured against `abaabil@1.2.0`. Each entry point is bundled on its own with its full module graph (so `button/styled`'s cost already includes the `button` markup it imports) using esbuild, `react`/`react-dom` external, minified, then gzipped. The `styled` and `a11y` tiers also pull in their component's stylesheet as a side-effect import; the CSS column is that stylesheet's own gzipped size, separate from the JS number. `normal` tier entries have no CSS column because they import none.
84
+
85
+ A `styled` entry sometimes measures a byte or two *below* its `normal` entry. That is compression noise, not a real difference: `styled` is `normal` plus a CSS side-effect import, so its JavaScript is the same code, and a byte of difference either way is gzip responding to a different module path. The CSS column is where the `styled` tier's actual cost is.
86
+
87
+ This is different from the per-file numbers in `SIZES.md`, which are produced with `preserveModules` and so under-count any tier that imports another module: `styled.js` there shows only the bytes of its own file, not the `index.js` it re-exports.
88
+
89
+ These figures are generated, not typed. The script lives in the docs site repo at `scripts/compare/delivered.mjs`, alongside the harness that measures abaabil against nine other libraries with one shared method. Earlier releases of this table were copied by hand and went stale: the 1.1.0 package shipped with 1.0.0's numbers still printed here, some of them out by more than 10%.
69
90
 
70
91
  Import the tier you need directly:
71
92
 
@@ -82,7 +103,7 @@ import Accordion from 'abaabil/accordion/a11y'
82
103
 
83
104
  ### Bundler required for `styled` and `a11y` tiers
84
105
 
85
- The `styled` and `a11y` entry points (all sixteen across the eight components) import their
106
+ The `styled` and `a11y` entry points (all thirty-six across the eighteen components) import their
86
107
  component's CSS as a JS side effect (`import 'abaabil/button/button.css'` style, resolved
87
108
  relative to the package). That works in any bundler that understands CSS imports, which
88
109
  covers Next.js, Vite, and webpack. It does **not** work if you run the built file directly in
@@ -178,10 +199,55 @@ Only the entry points that need interactivity are marked `"use client"`. The res
178
199
  | `abaabil/accordion` | no |
179
200
  | `abaabil/accordion/styled` | no |
180
201
  | `abaabil/accordion/a11y` | no |
181
-
182
- Button (all tiers), accordion (all tiers), and every `normal`/`styled` tier of dialog, input, checkbox, radio and select carry no client directive and can be rendered from a Server Component with zero client JS. Accordion is the only component whose `a11y` tier is also server-renderable: it has no hooks and needs no JavaScript at all, so all three of its tiers stay server components. Dialog's `a11y` tier, every combobox tier, and the `a11y` tier of input, checkbox, radio and select are client components.
183
-
184
- `abaabil/combobox/styled` has no literal `"use client"` directive of its own; it only re-exports `abaabil/combobox`, which does carry one, so the client boundary is already established there and the directive isn't duplicated. It still behaves as a client module once bundled, which is why it's marked "yes" above.
202
+ | `abaabil/textarea` | no |
203
+ | `abaabil/textarea/styled` | no |
204
+ | `abaabil/textarea/a11y` | yes |
205
+ | `abaabil/switch` | no |
206
+ | `abaabil/switch/styled` | no |
207
+ | `abaabil/switch/a11y` | yes |
208
+ | `abaabil/popover` | no |
209
+ | `abaabil/popover/styled` | no |
210
+ | `abaabil/popover/a11y` | no |
211
+ | `abaabil/tabs` | yes |
212
+ | `abaabil/tabs/styled` | yes (see note) |
213
+ | `abaabil/tabs/a11y` | yes |
214
+ | `abaabil/alert` | no |
215
+ | `abaabil/alert/styled` | no |
216
+ | `abaabil/alert/a11y` | no |
217
+ | `abaabil/progress` | no |
218
+ | `abaabil/progress/styled` | no |
219
+ | `abaabil/progress/a11y` | yes |
220
+ | `abaabil/slider` | no |
221
+ | `abaabil/slider/styled` | no |
222
+ | `abaabil/slider/a11y` | yes |
223
+ | `abaabil/breadcrumb` | no |
224
+ | `abaabil/breadcrumb/styled` | no |
225
+ | `abaabil/breadcrumb/a11y` | no |
226
+ | `abaabil/tooltip` | no |
227
+ | `abaabil/tooltip/styled` | no |
228
+ | `abaabil/tooltip/a11y` | yes |
229
+ | `abaabil/menu` | no |
230
+ | `abaabil/menu/styled` | no |
231
+ | `abaabil/menu/a11y` | yes |
232
+
233
+ Thirty-six of the fifty-four entry points carry no client directive and render from a Server Component with zero client JS.
234
+
235
+ Six components are server-renderable at **every** tier, `a11y` included, because they need no JavaScript at all:
236
+
237
+ - **button**, which attaches handlers only when you actually pass one (see the note at the end of this section).
238
+ - **accordion**, where exclusive open/close comes from the native `name` attribute on `<details>`.
239
+ - **popover**, where the trigger, the top layer, light-dismiss and Escape all come from the native Popover API.
240
+ - **alert**, which is a live region and a border.
241
+ - **breadcrumb**, which is a `<nav>` and an ordered list.
242
+ - **progress** and **slider** at `normal` and `styled`; their `a11y` tiers need `useId`.
243
+
244
+ **tooltip** is the interesting case: it shows and hides on `:hover` and `:focus-within`, which are selectors, so its `normal` and `styled` tiers need no JavaScript to work at all. Only the tier that adds `aria-describedby` and Escape is a client component.
245
+
246
+ Two components are client modules at every tier, because neither has a native element to build on and both need state to show one thing at a time: **combobox** and **tabs**.
247
+
248
+ The rest (dialog, input, textarea, checkbox, radio, switch, select) are server-renderable at `normal` and `styled`, and client at `a11y`, where `useId` mints the ids that wire labels and descriptions together.
249
+
250
+ `abaabil/combobox/styled` and `abaabil/tabs/styled` have no literal `"use client"` directive of their own; they only re-export their `normal` tier, which does carry one, so the client boundary is already established there and the directive isn't duplicated. They still behave as client modules once bundled, which is why they're marked "yes" above.
185
251
 
186
252
  This split is enforced at build time by `scripts/check-directives.js`, which runs as part of `npm run build` and fails the build in both directions: a required `"use client"` that got stripped, or an accidental one on a component that's supposed to stay server-only. That gate is checked against the built output, not just the source, so this table can't silently drift from what actually ships.
187
253
 
@@ -367,7 +433,221 @@ Accordion is the only component whose every tier, including `a11y`, is server-re
367
433
 
368
434
  **Deliberate limitation:** `Accordion_a11y` offers no `headingLevel` prop, and this is intentional, not an oversight. `<summary>` has an implicit ARIA role of `button` in some browsers, and a heading nested inside a button is not reliably exposed to assistive technology (VoiceOver, for one, does not expose a heading nested inside `<summary>` as a heading). The reverse, wrapping `<summary>` in a heading element, isn't an option either: `<summary>` must be the literal first child of `<details>` for the browser to recognize it as the disclosure trigger. Consumers who need reliable heading navigation across sections need the button-in-heading accordion pattern instead (an explicit heading wrapping a button, with `aria-expanded` and `aria-controls`), a different, ARIA-driven widget that this component does not attempt to be.
369
435
 
370
- ## Combobox: uncontrolled in 1.0.0
436
+ ### Textarea
437
+
438
+ `normal` / `styled` (`abaabil/textarea`, `abaabil/textarea/styled`):
439
+
440
+ | Prop | Type | Default | Description |
441
+ |---|---|---|---|
442
+ | `rows` | `number` | `3` | Visible line count. The browser default of 2 is too short to read back what you typed. |
443
+ | `className` | `string` | - | Merged with the base class. |
444
+
445
+ `a11y` (`abaabil/textarea/a11y`), the same wiring as Input on a `<textarea>`:
446
+
447
+ | Prop | Type | Default | Description |
448
+ |---|---|---|---|
449
+ | `label` | `string` | - | Rendered as a real `<label>`, associated via `htmlFor`/`id`. |
450
+ | `hideLabel` | `boolean` | `false` | Hide the label visually. It stays in the accessibility tree. |
451
+ | `description` | `string` | - | Help text, wired into `aria-describedby`. |
452
+ | `error` | `string` | - | Error message. Sets `aria-invalid` and joins `aria-describedby`. |
453
+ | `required` | `boolean` | `false` | |
454
+ | `id` | `string` | - | Overrides the generated id. |
455
+
456
+ ### Switch
457
+
458
+ `normal` / `styled` (`abaabil/switch`, `abaabil/switch/styled`) render `<input type="checkbox" role="switch">`.
459
+
460
+ The role is in the `normal` tier, not held back for `a11y`, because it is this component's identity rather than wiring, the same way `<dialog>` and `<details>` are for the components built on them. There is no native switch element, so the role is the only thing that makes a switch a switch; without it the component would be `abaabil/checkbox` with different CSS, and the styled tier would look like a switch while announcing itself as a checkbox.
461
+
462
+ There is no `aria-checked`. The native `checked` property already exposes the state, and a second copy of a piece of state is a second copy that can disagree with the first.
463
+
464
+ | Prop | Type | Default | Description |
465
+ |---|---|---|---|
466
+ | `className` | `string` | - | Merged with the base class. |
467
+
468
+ `a11y` (`abaabil/switch/a11y`):
469
+
470
+ | Prop | Type | Default | Description |
471
+ |---|---|---|---|
472
+ | `label` | `string` | - | Accessible name. The platform does not supply one. |
473
+ | `description` | `string` | - | Rendered text, wired via `aria-describedby`. |
474
+ | `id` | `string` | - | Overrides the generated id. |
475
+
476
+ Deliberately not included: visible "On"/"Off" text beside the control. A switch already announces its state from `checked`, so rendering the same state as adjacent text means a screen reader says it twice. Render your own and mark it `aria-hidden` if you want it visible.
477
+
478
+ ### Popover
479
+
480
+ Built on the native Popover API. The trigger, the top layer, light-dismiss and Escape all come from the browser, so **all three tiers ship zero JavaScript** and all three are server-renderable.
481
+
482
+ `id` is required rather than generated. Generating one would mean `useId`, which would mean a client boundary, which would cost this component the only property that makes it interesting.
483
+
484
+ `Popover` (all tiers), trigger and panel together, rendered as a fragment so nothing is added to your layout:
485
+
486
+ | Prop | Type | Default | Description |
487
+ |---|---|---|---|
488
+ | `id` | `string` | - | Panel id; also wires the trigger. Required. |
489
+ | `trigger` | `ReactNode` | - | Button content. |
490
+ | `triggerProps` | `object` | - | Spread onto the button. |
491
+ | `mode` | `'auto' \| 'manual'` | `'auto'` | `auto` light-dismisses and closes on Escape; `manual` does neither. |
492
+
493
+ `PopoverTrigger` and `PopoverPanel` are also exported from every tier, for placing the two apart.
494
+
495
+ `a11y` (`abaabil/popover/a11y`) adds a name for the panel, which is the one thing the platform does not supply:
496
+
497
+ | Prop | Type | Default | Description |
498
+ |---|---|---|---|
499
+ | `label` | `string` | - | Accessible name for the panel, as `aria-label`. |
500
+ | `labelledBy` | `string` | - | Id of an element naming the panel. Use instead of `label` when the panel renders a visible heading, so the name is not said twice. |
501
+
502
+ Deliberately not included: `aria-expanded` on the trigger. With no JavaScript it could only ever be a static value, and a static `aria-expanded` is worse than none, because it states a fact that stops being true the moment the popover opens. Browsers expose the `popovertarget` relationship natively.
503
+
504
+ ### Tabs
505
+
506
+ The W3C APG tabs pattern. Tabs have no native element behind them, so unlike most of this library every tier is a client module: showing one panel at a time needs state.
507
+
508
+ `normal` / `styled` (`abaabil/tabs`, `abaabil/tabs/styled`) give you the structure and the selection, with no ARIA and no keyboard handling:
509
+
510
+ | Prop | Type | Default | Description |
511
+ |---|---|---|---|
512
+ | `items` | `Array<{ key?, label, children? }>` | - | One entry per tab. |
513
+ | `defaultIndex` | `number` | `0` | Tab selected on first render. |
514
+ | `onChange` | `(index: number) => void` | - | Called with the newly selected index. |
515
+ | `className` | `string` | - | Merged with the base class. |
516
+
517
+ `a11y` (`abaabil/tabs/a11y`) adds the roles, the pairing, the roving tabindex and the arrow keys, plus:
518
+
519
+ | Prop | Type | Default | Description |
520
+ |---|---|---|---|
521
+ | `label` | `string` | - | Accessible name for the tablist. |
522
+ | `labelledBy` | `string` | - | Id of an element naming the tablist. |
523
+ | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Chooses the arrow pair and sets `aria-orientation`. |
524
+ | `activation` | `'automatic' \| 'manual'` | `'automatic'` | `automatic` selects as focus moves, which is the APG's recommendation when rendering a panel is cheap. `manual` moves focus without selecting; Enter or Space commits. |
525
+
526
+ Uncontrolled, like the combobox: observe the selection through `onChange`.
527
+
528
+ ### Alert
529
+
530
+ `normal` / `styled` (`abaabil/alert`, `abaabil/alert/styled`):
531
+
532
+ | Prop | Type | Default | Description |
533
+ |---|---|---|---|
534
+ | `variant` | `'info' \| 'success' \| 'warning' \| 'danger'` | `'info'` | Applied as `data-variant`. |
535
+ | `className` | `string` | - | Merged with the base class. |
536
+
537
+ `a11y` (`abaabil/alert/a11y`) adds the live-region semantics, which are the whole difference between a coloured box and a message anyone using a screen reader finds out about. The role follows the variant, because the right answer is the same every time and getting it wrong is the common failure: `info` and `success` get `role="status"` (polite), `warning` and `danger` get `role="alert"` (interrupting).
538
+
539
+ | Prop | Type | Default | Description |
540
+ |---|---|---|---|
541
+ | `variant` | `'info' \| 'success' \| 'warning' \| 'danger'` | `'info'` | Also selects the role. |
542
+ | `live` | `'polite' \| 'assertive' \| 'off'` | from `variant` | Overrides the politeness the variant implies. `off` drops the role entirely. |
543
+ | `title` | `string` | - | Rendered above the message as `<strong>`, not a heading: an alert is usually not a section of the document, and a stray heading damages the outline for anyone navigating by headings. |
544
+
545
+ **Read this before using it.** A live region is announced when its *contents change*, and assistive tech has to be watching the region before that happens. An alert that arrives in the DOM already carrying its message may not be announced at all, and behaviour differs between screen readers. If the message appears in response to something the user did, render the component with empty children from the start and fill it in, rather than mounting the whole alert at the moment you have something to say. Rendered empty it paints nothing.
546
+
547
+ ### Progress
548
+
549
+ Native `<progress>`. Omit `value` for an indeterminate bar; it has no default, because defaulting it to 0 would turn every unknown-duration operation into one claiming to be 0% done.
550
+
551
+ `normal` / `styled`:
552
+
553
+ | Prop | Type | Default | Description |
554
+ |---|---|---|---|
555
+ | `value` | `number` | - | Omit for indeterminate. |
556
+ | `max` | `number` | `100` | |
557
+ | `className` | `string` | - | Merged with the base class. |
558
+
559
+ `a11y` adds the label wiring and the one thing people get wrong about a progress bar, which is what it announces:
560
+
561
+ | Prop | Type | Default | Description |
562
+ |---|---|---|---|
563
+ | `label` | `string` | - | Rendered as a real `<label>`. |
564
+ | `hideLabel` | `boolean` | `false` | Hide it visually; it stays in the accessibility tree. |
565
+ | `description` | `string` | - | Help text, wired into `aria-describedby`. |
566
+ | `valueText` | `string` | - | Announced instead of the percentage. A bare `<progress value="3" max="8">` says "38 percent"; pass `"3 of 8 files"` when the number means something else. |
567
+
568
+ ### Slider
569
+
570
+ Native `<input type="range">`. The keyboard support, the min/max/step arithmetic and the announced role all come from the platform, which is why this is under 200 bytes at the lower tiers.
571
+
572
+ `normal` / `styled`:
573
+
574
+ | Prop | Type | Default | Description |
575
+ |---|---|---|---|
576
+ | `min` / `max` / `step` | `number` | `0` / `100` / `1` | |
577
+ | `className` | `string` | - | Merged with the base class. |
578
+
579
+ `a11y`:
580
+
581
+ | Prop | Type | Default | Description |
582
+ |---|---|---|---|
583
+ | `label` | `string` | - | Rendered as a real `<label>`. |
584
+ | `hideLabel` | `boolean` | `false` | |
585
+ | `description` | `string` | - | Wired into `aria-describedby`. |
586
+ | `showValue` | `boolean` | `false` | Renders the value in an `<output>`, marked `aria-hidden` because the input already announces it. |
587
+ | `formatValue` | `(value: number) => string` | - | Sets `aria-valuetext` and the visible output. A price slider announces "50" without it. |
588
+
589
+ Uncontrolled by default; pass `defaultValue` and read `onChange`. An uncontrolled slider starts at the midpoint, matching the native thumb.
590
+
591
+ ### Breadcrumb
592
+
593
+ A `<nav>` around an ordered list, because the order is the information. All three tiers are server-renderable.
594
+
595
+ The separator is drawn in CSS, never rendered as text, so a screen reader is not reading punctuation between the steps.
596
+
597
+ | Prop | Type | Default | Description |
598
+ |---|---|---|---|
599
+ | `items` | `Array<{ key?, label, href? }>` | - | Root first. An item with no `href` renders as text. |
600
+ | `className` | `string` | - | |
601
+
602
+ `a11y` adds the two things a hand-rolled breadcrumb almost always misses:
603
+
604
+ | Prop | Type | Default | Description |
605
+ |---|---|---|---|
606
+ | `label` | `string` | `'Breadcrumb'` | Names the `<nav>`. A page usually has several navigation landmarks, and unnamed ones are announced as "navigation, navigation, navigation". |
607
+ | `linkCurrent` | `boolean` | `false` | By default the last item renders as text even if given an `href`, because a link to the page you are on is a link that does nothing. Either way it gets `aria-current="page"`. |
608
+
609
+ ### Tooltip
610
+
611
+ **Read this before using one.** A tooltip is the control most often used for the wrong job.
612
+
613
+ - **Never put essential information in one.** There is no hover on touch, so it is simply unreachable there.
614
+ - **Never put interactive content in one.** Moving towards a link inside a tooltip dismisses the tooltip. If you need that, you want a popover.
615
+ - **The trigger must be focusable.** A tooltip on a plain `<span>` does not exist for keyboard users. The `a11y` tier warns when its child cannot receive props.
616
+
617
+ Showing and hiding is done in CSS, by `:hover` and `:focus-within`. Those are the two events that should reveal a tooltip and both are expressible as selectors, so the `normal` and `styled` tiers need no JavaScript.
618
+
619
+ | Prop | Type | Default | Description |
620
+ |---|---|---|---|
621
+ | `content` | `ReactNode` | - | The tooltip text. |
622
+ | `placement` | `'top' \| 'bottom'` | `'top'` | |
623
+ | `children` | `ReactElement` | - | The trigger. |
624
+
625
+ `a11y` adds the two things CSS cannot: `aria-describedby` from the trigger to the bubble, which is what makes a screen reader read it at all, and Escape to dismiss, which WCAG 1.4.13 requires for content revealed on hover. It clones the child to attach them, so the trigger stays your element rather than a wrapper of ours.
626
+
627
+ ### Menu
628
+
629
+ The W3C APG menu button pattern, on top of the native Popover API. The browser supplies the top layer, the outside-click dismissal and Escape; this component supplies the menu semantics and keyboard.
630
+
631
+ Like popover, `id` is required rather than generated.
632
+
633
+ **When not to use it.** `role="menu"` means a list of *actions*, in the application-menu sense. A button revealing a few navigation links is not a menu, and marking it up as one makes a screen reader announce a widget the user then cannot operate as one. Use `abaabil/popover` with links in it.
634
+
635
+ | Prop | Type | Default | Description |
636
+ |---|---|---|---|
637
+ | `id` | `string` | - | Panel id; also wires the trigger. Required. |
638
+ | `trigger` | `ReactNode` | - | Button content. |
639
+ | `items` | `Array<{ key?, label, href?, onSelect?, disabled? }>` | - | An item with `href` renders an `<a>`, otherwise a `<button>`. |
640
+ | `triggerProps` | `object` | - | Spread onto the trigger. |
641
+
642
+ `a11y` adds:
643
+
644
+ | Prop | Type | Default | Description |
645
+ |---|---|---|---|
646
+ | `label` | `string` | - | Names the menu itself. Without it the menu is named by its trigger. |
647
+
648
+ plus `aria-haspopup`, a live `aria-expanded` kept in step with the panel's own `toggle` event, `role="menu"`/`role="menuitem"`, a roving tabindex, Up/Down with wrapping, Home/End, multi-character typeahead, Tab to close, and focus moving to the first item on open and back to the trigger on close.
649
+
650
+ ## Combobox: uncontrolled in 1.x
371
651
 
372
652
  The combobox is uncontrolled in this release: it does not accept a `value` prop. Observe the selected value through the `onChange` callback instead of driving it from external state.
373
653
 
@@ -381,6 +661,10 @@ The floor is Chrome 99, Firefox 98, Safari 15.4, set by `@layer` (needed from Ch
381
661
  | `@starting-style` | Chrome 117, Firefox 129, Safari 17.5 | no entry animation |
382
662
  | CSS anchor positioning | Chrome 125, Firefox 147, Safari 26 | absolute positioning, behind `@supports` |
383
663
 
664
+ `abaabil/popover` is the one component with a floor of its own: the Popover API needs **Chrome 114, Firefox 125, Safari 17**. That is below this library's build target, so it raises nothing in practice, but it is a hard requirement rather than an enhancement. Below it, an unknown `popover` attribute is inert and the panel renders as an ordinary always-visible block under its trigger. Content stays reachable, which is why it is left that way rather than hidden, but it will not open and close. Import it only if that floor is acceptable; the other twelve components are unaffected.
665
+
666
+ Where the panel appears is a separate question from whether it works. Attaching it to its trigger needs CSS anchor positioning, which is not yet Baseline, so it is applied behind `@supports`. Without it the popover falls back to the UA default, centred in the viewport. Placing it next to the trigger any other way would mean measuring the DOM in JavaScript, which would cost this component the zero-JavaScript property that is the reason to use it.
667
+
384
668
  ## Accessibility
385
669
 
386
670
  - The combobox implements the [WAI-ARIA Authoring Practices Guide 1.2 combobox pattern](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/).
@@ -388,6 +672,15 @@ The floor is Chrome 99, Firefox 98, Safari 15.4, set by `@layer` (needed from Ch
388
672
  - Checkbox's `indeterminate` is mirrored to `aria-checked="mixed"`, because `indeterminate` itself is a DOM property with no attribute equivalent, so it's invisible to assistive tech unless something does this explicitly.
389
673
  - `RadioGroup` supplies the shared `name` every descendant `Radio` needs to behave as one native group; without it, arrow-key navigation and single-selection silently stop working.
390
674
  - Accordion's `a11y` tier needs no JavaScript at all: every tier, including `a11y`, is server-renderable, and exclusive open/close comes from the native `name` attribute on `<details>`.
675
+ - Popover leans entirely on the native Popover API for the top layer, light-dismiss, Escape and the trigger-to-panel relationship. Its `a11y` tier adds only an accessible name for the panel, which is the one thing the platform does not supply.
676
+ - Tabs implement the [WAI-ARIA Authoring Practices Guide tabs pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tabs/), including a roving tabindex so Tab steps past the tablist rather than through every tab in it.
677
+ - Switch carries `role="switch"` from its `normal` tier, so it never looks like a switch while announcing itself as a checkbox.
678
+ - Alert derives `role="status"` or `role="alert"` from its variant, so a confirmation does not interrupt and an error does. Read the note in its props section about when live regions actually announce.
679
+ - Menu implements the [WAI-ARIA Authoring Practices Guide menu button pattern](https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/), and its docs say when a menu is the wrong widget.
680
+ - Tooltip shows on both hover and focus, dismisses on Escape as WCAG 1.4.13 requires, and its bubble is never a pointer target so it cannot be hovered instead of its trigger.
681
+ - Progress accepts `valueText`, because a bar whose number is not a percentage is otherwise announced as one.
682
+ - Slider is a native range input, so its keyboard support is the platform's rather than a reimplementation; `formatValue` sets `aria-valuetext` for values that are not self-explanatory.
683
+ - Breadcrumb names its `<nav>` and marks the current page with `aria-current`, and draws its separators in CSS so they are never read aloud.
391
684
  - `a11y` tiers are the recommended default for production use; `normal` and `styled` tiers exist for cases where you want to compose your own accessibility behavior.
392
685
 
393
686
  ## License
@@ -0,0 +1 @@
1
+ import{jsxs as o,jsx as s}from"react/jsx-runtime";import"./alert.css";import v from"./index.js";function d({variant:t="info",live:e,title:a,children:l,...n}){const i=t==="warning"||t==="danger",r=e??(i?"assertive":"polite");return o(v,{variant:t,role:r==="off"?void 0:i?"alert":"status","aria-live":r!==(i?"assertive":"polite")?r:void 0,...n,children:[a?s("strong",{className:"abaabil-alert__title",children:a}):null,l]})}export{d as default};
@@ -0,0 +1 @@
1
+ @layer abaabil.components{.abaabil-alert{--alert-accent:var(--color-primary);gap:var(--space-1);padding-block:var(--space-2);padding-inline:var(--space-3);border:1px solid color-mix(in srgb, var(--alert-accent) 35%, var(--color-surface));border-inline-start:3px solid var(--alert-accent);border-radius:var(--radius-md);background:color-mix(in srgb, var(--alert-accent) 8%, var(--color-surface));color:var(--color-text);font-size:var(--font-size-sm);flex-direction:column;line-height:1.5;display:flex}.abaabil-alert[data-variant=success]{--alert-accent:var(--color-success)}.abaabil-alert[data-variant=warning]{--alert-accent:var(--color-warning)}.abaabil-alert[data-variant=danger]{--alert-accent:var(--color-danger)}.abaabil-alert__title{font-weight:var(--font-weight-semibold);color:var(--color-text)}.abaabil-alert:empty{display:none}}
@@ -0,0 +1 @@
1
+ import{jsx as e}from"react/jsx-runtime";function l({variant:r="info",className:a,children:t,...i}){const n=a?`abaabil-alert ${a}`:"abaabil-alert";return e("div",{className:n,"data-variant":r,...i,children:t})}export{l as default};
@@ -0,0 +1 @@
1
+ import"./alert.css";import{default as e}from"./index.js";export{e as default};
@@ -0,0 +1 @@
1
+ import{jsx as e}from"react/jsx-runtime";import"./breadcrumb.css";function u({items:l,label:c="Breadcrumb",linkCurrent:m=!1,className:b,...s}){const n=b?`abaabil-breadcrumb ${b}`:"abaabil-breadcrumb",t=l.length-1;return e("nav",{className:n,"aria-label":c,...s,children:e("ol",{className:"abaabil-breadcrumb__list",children:l.map((a,i)=>{const r=i===t,d=a.href&&(!r||m);return e("li",{className:"abaabil-breadcrumb__item",children:d?e("a",{href:a.href,className:"abaabil-breadcrumb__link","aria-current":r?"page":void 0,children:a.label}):e("span",{className:"abaabil-breadcrumb__current","aria-current":r?"page":void 0,children:a.label})},a.key??i)})})})}export{u as default};
@@ -0,0 +1 @@
1
+ @layer abaabil.components{.abaabil-breadcrumb__list{align-items:center;gap:var(--space-2);font-size:var(--font-size-sm);flex-wrap:wrap;margin:0;padding:0;list-style:none;display:flex}.abaabil-breadcrumb__item{align-items:center;gap:var(--space-2);color:var(--color-text-muted);display:flex}.abaabil-breadcrumb__item+.abaabil-breadcrumb__item:before{content:"";opacity:.6;border-block-start:1.5px solid;border-inline-end:1.5px solid;flex:none;block-size:.35rem;inline-size:.35rem;rotate:45deg}.abaabil-breadcrumb__list:is(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi)) .abaabil-breadcrumb__item+.abaabil-breadcrumb__item:before{rotate:-135deg}.abaabil-breadcrumb__link{color:var(--color-text-muted);text-underline-offset:.2em;border-radius:var(--radius-md);text-decoration:underline}.abaabil-breadcrumb__link:hover{color:var(--color-text)}.abaabil-breadcrumb__link:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:2px}.abaabil-breadcrumb__current{color:var(--color-text);font-weight:var(--font-weight-semibold)}}
@@ -0,0 +1 @@
1
+ import{jsx as e}from"react/jsx-runtime";function s({items:b,className:r,...l}){const c=r?`abaabil-breadcrumb ${r}`:"abaabil-breadcrumb";return e("nav",{className:c,...l,children:e("ol",{className:"abaabil-breadcrumb__list",children:b.map((a,i)=>e("li",{className:"abaabil-breadcrumb__item",children:a.href?e("a",{href:a.href,className:"abaabil-breadcrumb__link",children:a.label}):e("span",{className:"abaabil-breadcrumb__current",children:a.label})},a.key??i))})})}export{s as default};
@@ -0,0 +1 @@
1
+ import"./breadcrumb.css";import{default as e}from"./index.js";export{e as default};
@@ -0,0 +1,2 @@
1
+ "use client";
2
+ import{jsxs as P,Fragment as T,jsx as c}from"react/jsx-runtime";import{useRef as u,useState as D,useEffect as $}from"react";import"./menu.css";import{anchorNameFor as x}from"./index.js";function q({id:l,trigger:F,items:i,label:N,triggerProps:C,className:d,...E}){const f=u(null),h=u(null),m=u([]),[L,K]=D(!1),[S,_]=D(0),g=u(0),b=e=>{g.current=e,_(e)},v=u({buffer:"",at:0}),A=d?`abaabil-menu ${d}`:"abaabil-menu",t=i.map((e,r)=>e.disabled?null:r).filter(e=>e!==null);$(()=>{const e=f.current;if(!e)return;function r(a){const n=a.newState==="open";if(K(n),n){const s=t[0]??0;b(s),m.current[s]?.focus()}else v.current={buffer:"",at:0}}return e.addEventListener("toggle",r),()=>e.removeEventListener("toggle",r)},[]);function p({restoreFocus:e=!0}={}){f.current?.hidePopover(),e&&h.current?.focus()}function o(e){b(e),m.current[e]?.focus()}function y(e,r){if(!t.length)return e;const a=(t.indexOf(e)+r+t.length)%t.length;return t[a]}function O(e){const r=g.current;switch(e.key){case"ArrowDown":e.preventDefault(),o(y(r,1));return;case"ArrowUp":e.preventDefault(),o(y(r,-1));return;case"Home":e.preventDefault(),o(t[0]??0);return;case"End":e.preventDefault(),o(t[t.length-1]??0);return;case"Tab":p({restoreFocus:!1});return}if(e.key.length!==1||e.metaKey||e.ctrlKey||e.altKey)return;const a=Date.now(),n=v.current;n.buffer=a-n.at>500?e.key:n.buffer+e.key,n.at=a;const s=n.buffer.toLowerCase(),w=t.indexOf(r)+1,k=[...t.slice(w),...t.slice(0,w)].find(j=>String(i[j].label).toLowerCase().startsWith(s));k!==void 0&&(e.preventDefault(),o(k))}return P(T,{children:[c("button",{type:"button",ref:h,popoverTarget:l,"aria-haspopup":"menu","aria-expanded":L,className:"abaabil-menu__trigger",style:{anchorName:x(l)},...C,children:F}),c("div",{id:l,popover:"auto",ref:f,role:"menu","aria-label":N,className:A,style:{positionAnchor:x(l)},onKeyDown:O,...E,children:i.map((e,r)=>{const a={role:"menuitem",className:"abaabil-menu__item",tabIndex:r===S?0:-1,ref:n=>{m.current[r]=n},onFocus:()=>b(r)};return e.href?c("a",{href:e.href,...a,onClick:()=>p({restoreFocus:!1}),children:e.label},e.key??r):c("button",{type:"button",disabled:e.disabled,...a,onClick:()=>{e.onSelect?.(),p()},children:e.label},e.key??r)})})]})}export{q as default};
@@ -0,0 +1 @@
1
+ import{jsxs as b,Fragment as d,jsx as n}from"react/jsx-runtime";function o(e){return`--abaabil-menu-${String(e).replace(/[^\w-]/g,"-")}`}function p({id:e,trigger:r,items:l,triggerProps:s,className:a,...t}){const i=a?`abaabil-menu ${a}`:"abaabil-menu";return b(d,{children:[n("button",{type:"button",popoverTarget:e,className:"abaabil-menu__trigger",style:{anchorName:o(e)},...s,children:r}),n("div",{id:e,popover:"auto",className:i,style:{positionAnchor:o(e)},...t,children:l.map((m,u)=>n(c,{...m},m.key??u))})]})}function c({label:e,href:r,onSelect:l,disabled:s,className:a,...t}){const i=a?`abaabil-menu__item ${a}`:"abaabil-menu__item";return r?n("a",{href:r,className:i,...t,children:e}):n("button",{type:"button",className:i,onClick:l,disabled:s,...t,children:e})}export{c as MenuItem,o as anchorNameFor,p as default};
@@ -0,0 +1 @@
1
+ @layer abaabil.components{.abaabil-menu{inline-size:max-content;min-inline-size:10rem;max-inline-size:min(18rem,100vw - 2rem);padding:var(--space-1);border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface);color:var(--color-text)}.abaabil-menu::backdrop{background:0 0}.abaabil-menu__trigger{font:inherit;color:inherit}.abaabil-menu__item{appearance:none;inline-size:100%;padding-block:var(--space-2);padding-inline:var(--space-3);border-radius:var(--radius-md);color:var(--color-text);font:inherit;font-size:var(--font-size-sm);text-align:start;white-space:nowrap;cursor:pointer;background:0 0;border:0;text-decoration:none;display:block}.abaabil-menu__item:hover{background:var(--color-muted)}.abaabil-menu__item:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:-2px;background:var(--color-muted)}.abaabil-menu__item:disabled{opacity:.5;cursor:not-allowed}.abaabil-menu__item:disabled:hover{background:0 0}@supports (anchor-name:--x){.abaabil-menu{position-area:block-end span-inline-end;position-try-fallbacks:block-start span-inline-end, block-end span-inline-start;margin:0;margin-block-start:var(--space-1);position:fixed;inset:auto}}@starting-style{.abaabil-menu:popover-open{opacity:0}}.abaabil-menu:popover-open{opacity:1;transition:opacity var(--duration-fast) ease}@media (prefers-reduced-motion:reduce){.abaabil-menu:popover-open{transition:none}@starting-style{.abaabil-menu:popover-open{opacity:1}}}}
@@ -0,0 +1 @@
1
+ import"./menu.css";import{MenuItem as m,anchorNameFor as t,default as a}from"./index.js";export{m as MenuItem,t as anchorNameFor,a as default};
@@ -0,0 +1 @@
1
+ import{jsx as d}from"react/jsx-runtime";import"./popover.css";import{Popover as g}from"./index.js";import{PopoverPanel as P,PopoverTrigger as y}from"./index.js";function b({id:a,trigger:l,label:r,labelledBy:o,role:n="group",triggerProps:i,mode:p="auto",children:t,...e}){const s=!!(r||o||e["aria-label"]||e["aria-labelledby"]);return typeof process<"u"&&process.env.NODE_ENV!=="production"&&!s&&console.warn("abaabil/popover: no `label` or `labelledBy` given, so the panel has no accessible name and screen readers announce it as an unnamed group."),d(g,{id:a,trigger:l,triggerProps:i,mode:p,role:n,"aria-label":r,"aria-labelledby":o,...e,children:t})}export{P as PopoverPanel,y as PopoverTrigger,b as default};
@@ -0,0 +1 @@
1
+ import{jsx as n,jsxs as d,Fragment as u}from"react/jsx-runtime";function p(e){return`--abaabil-popover-${String(e).replace(/[^\w-]/g,"-")}`}function c({target:e,action:o="toggle",className:r,style:t,children:a,...i}){const l=r?`abaabil-popover__trigger ${r}`:"abaabil-popover__trigger";return n("button",{type:"button",popoverTarget:e,popoverTargetAction:o,className:l,style:{anchorName:p(e),...t},...i,children:a})}function s({id:e,mode:o="auto",className:r,style:t,children:a,...i}){const l=r?`abaabil-popover ${r}`:"abaabil-popover";return n("div",{id:e,popover:o,className:l,style:{positionAnchor:p(e),...t},...i,children:a})}function g({id:e,trigger:o,triggerProps:r,mode:t="auto",children:a,...i}){return d(u,{children:[n(c,{target:e,...r,children:o}),n(s,{id:e,mode:t,...i,children:a})]})}export{g as Popover,s as PopoverPanel,c as PopoverTrigger,g as default};
@@ -0,0 +1 @@
1
+ @layer abaabil.components{.abaabil-popover{inline-size:max-content;max-inline-size:min(22rem,100vw - 2rem);padding:var(--space-3);border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface);color:var(--color-text);font-size:var(--font-size-sm)}.abaabil-popover:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:2px}.abaabil-popover::backdrop{background:0 0}.abaabil-popover__trigger{font:inherit;color:inherit}@supports (anchor-name:--x){.abaabil-popover{position-area:block-end span-inline-end;position-try-fallbacks:block-start span-inline-end, block-end span-inline-start;margin:0;margin-block-start:var(--space-1);position:fixed;inset:auto}}@starting-style{.abaabil-popover:popover-open{opacity:0}}.abaabil-popover:popover-open{opacity:1;transition:opacity var(--duration-fast) ease}@media (prefers-reduced-motion:reduce){.abaabil-popover:popover-open{transition:none}@starting-style{.abaabil-popover:popover-open{opacity:1}}}}
@@ -0,0 +1 @@
1
+ import"./popover.css";import{Popover as p,PopoverPanel as P,PopoverTrigger as v,Popover as a}from"./index.js";export{p as Popover,P as PopoverPanel,v as PopoverTrigger,a as default};
@@ -0,0 +1,2 @@
1
+ "use client";
2
+ import{jsxs as g,jsx as i}from"react/jsx-runtime";import{useId as f}from"react";import"./progress.css";import h from"./index.js";function x({label:a,hideLabel:d=!1,description:l,valueText:n,value:t,max:c=100,id:p,"aria-describedby":u,...e}){const r=f(),s=p??`${r}-progress`,o=`${r}-description`,b=`${r}-label`,m=!!(a||e["aria-label"]||e["aria-labelledby"]);typeof process<"u"&&process.env.NODE_ENV!=="production"&&!m&&console.warn("abaabil/progress: no `label` given, so the progress bar has no accessible name. Pass `label`, `aria-label`, or `aria-labelledby`.");const v=[u,l?o:null].filter(Boolean).join(" ")||void 0;return g("div",{className:"abaabil-progress-group",children:[a?i("label",{id:b,htmlFor:s,className:d?"abaabil-progress__label abaabil-visually-hidden":"abaabil-progress__label",children:a}):null,i(h,{...e,id:s,value:t,max:c,"aria-valuetext":n,"aria-labelledby":a?b:void 0,"aria-describedby":v}),l?i("div",{id:o,className:"abaabil-progress__description",children:l}):null]})}export{x as default};
@@ -0,0 +1 @@
1
+ import{jsx as o}from"react/jsx-runtime";function m({value:s,max:e=100,className:a,...r}){const l=a?`abaabil-progress ${a}`:"abaabil-progress";return o("progress",{className:l,value:s,max:e,...r})}export{m as default};
@@ -0,0 +1 @@
1
+ @layer abaabil.components{.abaabil-progress{appearance:none;background:var(--color-muted);block-size:.5rem;inline-size:100%;color:var(--color-primary);border:0;border-radius:999px;display:block;overflow:hidden}.abaabil-progress::-webkit-progress-bar{background:var(--color-muted);border-radius:999px}.abaabil-progress::-webkit-progress-value{background:var(--color-primary);transition:inline-size var(--duration-fast) ease;border-radius:999px}.abaabil-progress::-moz-progress-bar{background:var(--color-primary);border-radius:999px}.abaabil-progress:indeterminate{background:linear-gradient(90deg, var(--color-primary) 0 35%, transparent 35% 100%) 0 0 / 250% 100% no-repeat var(--color-muted);animation:1.4s linear infinite abaabil-progress-slide}.abaabil-progress:indeterminate::-webkit-progress-bar{background:0 0}.abaabil-progress:indeterminate::-moz-progress-bar{background:0 0}@keyframes abaabil-progress-slide{0%{background-position:-60% 0}to{background-position:160% 0}}.abaabil-progress-group{gap:var(--space-1);flex-direction:column;display:flex}.abaabil-progress__label{font-size:var(--font-size-sm);color:var(--color-text)}.abaabil-progress__description{font-size:var(--font-size-sm);color:var(--color-text-muted)}@media (prefers-reduced-motion:reduce){.abaabil-progress::-webkit-progress-value{transition:none}.abaabil-progress:indeterminate{background-position:40% 0;animation:none}}}
@@ -0,0 +1 @@
1
+ import"./progress.css";import{default as e}from"./index.js";export{e as default};
@@ -0,0 +1,2 @@
1
+ "use client";
2
+ import{jsxs as N,jsx as e}from"react/jsx-runtime";import{useId as E,useState as F}from"react";import"./slider.css";import S from"./index.js";function B({label:l,hideLabel:v=!1,description:i,showValue:_=!1,formatValue:n,min:r=0,max:s=100,value:o,defaultValue:x,onChange:y,id:g,"aria-describedby":V,...d}){const u=E(),b=g??`${u}-slider`,m=`${u}-description`,t=o!==void 0,c=a=>Math.min(Math.max(Number(a),Number(r)),Number(s)),[h,M]=F(()=>c(x??Math.floor((Number(r)+Number(s))/2))),f=t?c(o):h,j=!!(l||d["aria-label"]||d["aria-labelledby"]);typeof process<"u"&&process.env.NODE_ENV!=="production"&&!j&&console.warn("abaabil/slider: no `label` given, so the slider has no accessible name. Pass `label`, `aria-label`, or `aria-labelledby`.");const w=[V,i?m:null].filter(Boolean).join(" ")||void 0,p=n?n(Number(f)):void 0;function C(a){t||M(a.target.value),y?.(a)}return N("div",{className:"abaabil-slider-group",children:[N("div",{className:"abaabil-slider__header",children:[l?e("label",{htmlFor:b,className:v?"abaabil-slider__label abaabil-visually-hidden":"abaabil-slider__label",children:l}):null,_?e("output",{htmlFor:b,className:"abaabil-slider__output","aria-hidden":"true",children:p??f}):null]}),e(S,{...d,id:b,min:r,max:s,...t?{value:o}:{defaultValue:h},onChange:C,"aria-valuetext":p,"aria-describedby":w}),i?e("div",{id:m,className:"abaabil-slider__description",children:i}):null]})}export{B as default};
@@ -0,0 +1 @@
1
+ import{jsx as m}from"react/jsx-runtime";function n({min:e=0,max:s=100,step:t=1,className:a,...i}){const r=a?`abaabil-slider ${a}`:"abaabil-slider";return m("input",{type:"range",className:r,min:e,max:s,step:t,...i})}export{n as default};
@@ -0,0 +1 @@
1
+ @layer abaabil.components{.abaabil-slider{--slider-track:.3rem;--slider-thumb:1.1rem;appearance:none;inline-size:100%;block-size:var(--slider-thumb);cursor:pointer;background:0 0;margin:0}.abaabil-slider::-webkit-slider-runnable-track{block-size:var(--slider-track);background:var(--color-muted);border-radius:999px}.abaabil-slider::-moz-range-track{block-size:var(--slider-track);background:var(--color-muted);border-radius:999px}.abaabil-slider::-webkit-slider-thumb{appearance:none;inline-size:var(--slider-thumb);block-size:var(--slider-thumb);border:2px solid var(--color-surface);background:var(--color-primary);border-radius:50%;margin-block-start:calc((var(--slider-track) - var(--slider-thumb)) / 2)}.abaabil-slider::-moz-range-thumb{inline-size:var(--slider-thumb);block-size:var(--slider-thumb);border:2px solid var(--color-surface);background:var(--color-primary);border-radius:50%}.abaabil-slider::-moz-range-progress{block-size:var(--slider-track);background:var(--color-primary);border-radius:999px}.abaabil-slider:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:4px;border-radius:999px}.abaabil-slider:disabled{opacity:.5;cursor:not-allowed}.abaabil-slider-group{gap:var(--space-1);flex-direction:column;display:flex}.abaabil-slider__header{justify-content:space-between;align-items:baseline;gap:var(--space-2);display:flex}.abaabil-slider__label{font-size:var(--font-size-sm);color:var(--color-text)}.abaabil-slider__output{font-size:var(--font-size-sm);font-weight:var(--font-weight-semibold);font-variant-numeric:tabular-nums;color:var(--color-text)}.abaabil-slider__description{font-size:var(--font-size-sm);color:var(--color-text-muted)}}
@@ -0,0 +1 @@
1
+ import"./slider.css";import{default as e}from"./index.js";export{e as default};
package/dist/styles.css CHANGED
@@ -1 +1 @@
1
- @layer abaabil.reset;@layer abaabil.tokens{:root{--abaabil-blue-500:#2563eb;--abaabil-blue-400:#3b82f6;--abaabil-gray-900:#111827;--abaabil-gray-300:#d1d5db;--abaabil-gray-100:#f3f4f6;--abaabil-white:#fff;--abaabil-red-600:#dc2626;--color-primary:var(--abaabil-blue-500);--color-primary-hover:var(--abaabil-blue-400);--color-primary-fg:var(--abaabil-white);--color-surface:var(--abaabil-white);--color-text:var(--abaabil-gray-900);--color-border:var(--abaabil-gray-300);--color-muted:var(--abaabil-gray-100);--color-danger:var(--abaabil-red-600);--color-focus-ring:var(--abaabil-blue-500);--color-overlay:#00000080;--color-text-muted:#6b7280;--color-placeholder:#9ca3af;--radius-md:.375rem;--space-1:.25rem;--space-2:.5rem;--space-3:.75rem;--space-4:1rem;--font-size-sm:.875rem;--font-size-lg:1.125rem;--font-weight-semibold:600;--control-height-md:2.5rem;--duration-fast:.15s}@media (prefers-color-scheme:dark){:root:not([data-theme=light]){--color-surface:#0b0f19;--color-text:#e5e7eb;--color-border:#374151;--color-muted:#1f2937;--color-text-muted:#9ca3af;--color-placeholder:#6b7280}}:root[data-theme=dark]{--color-surface:#0b0f19;--color-text:#e5e7eb;--color-border:#374151;--color-muted:#1f2937;--color-text-muted:#9ca3af;--color-placeholder:#6b7280}}@layer abaabil.base{.abaabil-visually-hidden{clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}}@layer abaabil.components{.abaabil-accordion-group{border:1px solid var(--color-border);border-radius:var(--radius-md);overflow:clip}.abaabil-accordion-group .abaabil-accordion+.abaabil-accordion{border-top:1px solid var(--color-border)}.abaabil-accordion{color:var(--color-text)}.abaabil-accordion__summary{list-style:none}.abaabil-accordion__summary::-webkit-details-marker{display:none}.abaabil-accordion__summary{justify-content:space-between;align-items:center;gap:var(--space-2);min-height:var(--control-height-md);padding:var(--space-3) var(--space-4);font-size:var(--font-size-sm);font-weight:var(--font-weight-semibold);cursor:pointer;-webkit-user-select:none;user-select:none;line-height:1.3;display:flex}.abaabil-accordion__summary:hover{background:var(--color-muted)}.abaabil-accordion__summary:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:-2px}.abaabil-accordion__summary:after{content:"";block-size:.5rem;inline-size:.5rem;transition:transform var(--duration-fast) ease;border-bottom:2px solid;border-right:2px solid;flex:none;transform:rotate(45deg)}.abaabil-accordion[open]>.abaabil-accordion__summary:after{transform:rotate(-135deg)}.abaabil-accordion__panel{padding:0 var(--space-4) var(--space-3)}@media (prefers-reduced-motion:reduce){.abaabil-accordion__summary:after{transition:none}}.abaabil-button{--btn-bg:var(--color-primary);--btn-fg:var(--color-primary-fg);--btn-border:var(--color-primary);--btn-height:var(--control-height-md);--btn-padding-x:max(.375rem, calc((var(--btn-height) - var(--font-size-sm)) / 2));--btn-radius:var(--radius-md);justify-content:center;align-items:center;gap:var(--space-2);height:var(--btn-height);padding-inline:var(--btn-padding-x);border:1px solid var(--btn-border);border-radius:var(--btn-radius);background:var(--btn-bg);color:var(--btn-fg);font-size:var(--font-size-sm);font-weight:var(--font-weight-semibold);white-space:nowrap;cursor:pointer;transition:background-color var(--duration-fast) ease, border-color var(--duration-fast) ease;line-height:1;text-decoration:none;display:inline-flex}.abaabil-button:hover{--btn-bg:var(--color-primary-hover);--btn-border:var(--color-primary-hover)}.abaabil-button:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:2px}.abaabil-button[data-variant=secondary]{--btn-bg:transparent;--btn-fg:var(--color-text);--btn-border:var(--color-border)}.abaabil-button[data-variant=ghost]{--btn-bg:transparent;--btn-fg:var(--color-text);--btn-border:transparent}.abaabil-button[data-size=sm]{--btn-height:2rem}.abaabil-button[data-size=lg]{--btn-height:3rem}.abaabil-button:disabled,.abaabil-button[aria-disabled=true]{opacity:.5;cursor:not-allowed}@media (prefers-reduced-motion:reduce){.abaabil-button{transition:none}}.abaabil-checkbox{--checkbox-radius:min(var(--radius-md), .25rem);appearance:none;border:1px solid var(--color-border);border-radius:var(--checkbox-radius);background:var(--color-surface);cursor:pointer;width:1.125rem;height:1.125rem;transition:background-color var(--duration-fast) ease, border-color var(--duration-fast) ease;flex-shrink:0;place-content:center;margin:0;display:inline-grid}.abaabil-checkbox:before{content:"";background:var(--color-primary-fg);clip-path:polygon(14% 44%,0 65%,50% 100%,100% 16%,80% 0%,43% 62%);width:.65rem;height:.65rem;transition:transform var(--duration-fast) ease;transform:scale(0)}.abaabil-checkbox:checked,.abaabil-checkbox:indeterminate{background:var(--color-primary);border-color:var(--color-primary)}.abaabil-checkbox:checked:before,.abaabil-checkbox:indeterminate:before{transform:scale(1)}.abaabil-checkbox:indeterminate:before{clip-path:none;border-radius:1px;width:.65rem;height:.125rem}.abaabil-checkbox:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:2px}.abaabil-checkbox:disabled{opacity:.5;cursor:not-allowed}.abaabil-checkbox__wrapper{align-items:flex-start;gap:var(--space-2);display:inline-flex}.abaabil-checkbox__label{font-size:var(--font-size-sm);color:var(--color-text);cursor:pointer}.abaabil-checkbox__description{font-size:var(--font-size-sm);color:var(--color-text);opacity:.75;display:block}.abaabil-checkbox-group{border:1px solid var(--color-border);border-radius:var(--radius-md);padding:var(--space-3)}.abaabil-checkbox-group__legend{padding-inline:var(--space-1);font-size:var(--font-size-sm);font-weight:var(--font-weight-semibold);color:var(--color-text)}@media (prefers-reduced-motion:reduce){.abaabil-checkbox,.abaabil-checkbox:before{transition:none}}.abaabil-combobox{display:inline-block;position:relative}.abaabil-combobox__label{clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.abaabil-combobox__label--visible{margin-bottom:var(--space-1);font-size:var(--font-size-sm);color:var(--color-text);display:block}.abaabil-combobox__input{height:var(--control-height-md);--input-padding-x:max(.375rem, calc((var(--control-height-md) - var(--font-size-sm)) / 2));padding-inline:var(--input-padding-x);border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface);color:var(--color-text);font-size:var(--font-size-sm);width:100%}.abaabil-combobox__input:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:2px}.abaabil-combobox__list{z-index:10;width:100%;max-height:15rem;padding:var(--space-1);border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface);margin:0;list-style:none;position:absolute;inset-block-start:calc(100% + var(--space-1));inset-inline-start:0;overflow-y:auto}.abaabil-combobox__option{padding:var(--space-2) var(--space-3);border-radius:var(--radius-md);font-size:var(--font-size-sm);color:var(--color-text);cursor:pointer}.abaabil-combobox__option:hover,.abaabil-combobox__option[data-active=true]{background:var(--color-muted)}@supports (anchor-name:--x){.abaabil-combobox__input{anchor-name:--abaabil-combobox-input}.abaabil-combobox__list{position-anchor:--abaabil-combobox-input;position-area:block-end span-inline-end;position-try-fallbacks:block-start span-inline-end;width:anchor-size(width);position:fixed;inset:auto}}.abaabil-dialog{--dialog-width:32rem;--dialog-padding:var(--space-4);width:min(var(--dialog-width), calc(100vw - 2rem));padding:var(--dialog-padding);border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface);color:var(--color-text)}.abaabil-dialog::backdrop{background:var(--color-overlay)}.abaabil-dialog__title{margin:0 0 var(--space-3);font-size:var(--font-size-lg);font-weight:var(--font-weight-semibold)}@starting-style{.abaabil-dialog[open]{opacity:0;transform:translateY(.5rem)}}.abaabil-dialog[open]{opacity:1;transition:opacity var(--duration-fast) ease, transform var(--duration-fast) ease;transform:none}@media (prefers-reduced-motion:reduce){.abaabil-dialog[open]{transition:none}@starting-style{.abaabil-dialog[open]{opacity:1;transform:none}}}.abaabil-input{--input-height:var(--control-height-md);--input-padding-x:max(.375rem, calc((var(--input-height) - var(--font-size-sm)) / 2));--input-border:var(--color-border);--input-placeholder-fg:var(--color-placeholder);width:100%;height:var(--input-height);padding-inline:var(--input-padding-x);border:1px solid var(--input-border);border-radius:var(--radius-md);background:var(--color-surface);color:var(--color-text);font-size:var(--font-size-sm);transition:border-color var(--duration-fast) ease;display:block}.abaabil-input::placeholder{color:var(--input-placeholder-fg)}.abaabil-input:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:2px}.abaabil-input:disabled{opacity:.5;cursor:not-allowed}.abaabil-input[aria-invalid=true]{--input-border:var(--color-danger)}.abaabil-input-group{gap:var(--space-1);flex-direction:column;display:flex}.abaabil-input__label{font-size:var(--font-size-sm);color:var(--color-text)}.abaabil-input__description{--input-description-fg:var(--color-text-muted);font-size:var(--font-size-sm);color:var(--input-description-fg)}.abaabil-input__error{font-size:var(--font-size-sm);color:var(--color-danger)}@media (prefers-reduced-motion:reduce){.abaabil-input{transition:none}}.abaabil-radio{appearance:none;border:1px solid var(--color-border);background:var(--color-surface);cursor:pointer;width:1.125rem;height:1.125rem;transition:background-color var(--duration-fast) ease, border-color var(--duration-fast) ease;border-radius:50%;flex-shrink:0;place-content:center;margin:0;display:inline-grid}.abaabil-radio:before{content:"";background:var(--color-primary-fg);width:.5rem;height:.5rem;transition:transform var(--duration-fast) ease;border-radius:50%;transform:scale(0)}.abaabil-radio:checked{background:var(--color-primary);border-color:var(--color-primary)}.abaabil-radio:checked:before{transform:scale(1)}.abaabil-radio:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:2px}.abaabil-radio:disabled{opacity:.5;cursor:not-allowed}.abaabil-radio__wrapper{align-items:flex-start;gap:var(--space-2);display:inline-flex}.abaabil-radio__label{font-size:var(--font-size-sm);color:var(--color-text);cursor:pointer}.abaabil-radio__description{font-size:var(--font-size-sm);color:var(--color-text);opacity:.75;display:block}.abaabil-radio-group{border:1px solid var(--color-border);border-radius:var(--radius-md);padding:var(--space-3)}.abaabil-radio-group__legend{padding-inline:var(--space-1);font-size:var(--font-size-sm);font-weight:var(--font-weight-semibold);color:var(--color-text)}@media (prefers-reduced-motion:reduce){.abaabil-radio,.abaabil-radio:before{transition:none}}.abaabil-select{--select-height:var(--control-height-md);--select-padding-x:max(.375rem, calc((var(--select-height) - var(--font-size-sm)) / 2));appearance:none;height:var(--select-height);padding-inline:var(--select-padding-x);border:1px solid var(--color-border);border-radius:var(--radius-md);background-color:var(--color-surface);background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M2.5 4.5L6 8l3.5-3.5' stroke='currentColor' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right var(--select-padding-x) center;color:var(--color-text);font-size:var(--font-size-sm);cursor:pointer;transition:border-color var(--duration-fast) ease;background-size:.75rem .75rem;padding-inline-end:calc(var(--select-padding-x) + 1.125rem);line-height:1}.abaabil-select:is(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi)){background-position:left var(--select-padding-x) center;padding-inline-start:calc(var(--select-padding-x) + 1.125rem);padding-inline-end:var(--select-padding-x)}.abaabil-select:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:2px}.abaabil-select:disabled{opacity:.5;cursor:not-allowed}.abaabil-select-group{gap:var(--space-1);flex-direction:column;display:flex}.abaabil-select__label{font-size:var(--font-size-sm);color:var(--color-text)}.abaabil-select__description{font-size:var(--font-size-sm);color:var(--color-text);opacity:.75;margin:0}.abaabil-select__error{font-size:var(--font-size-sm);color:var(--color-danger);margin:0}.abaabil-select[aria-invalid=true]{border-color:var(--color-danger)}@media (prefers-reduced-motion:reduce){.abaabil-select{transition:none}}}
1
+ @layer abaabil.reset;@layer abaabil.tokens{:root{--abaabil-blue-500:#2563eb;--abaabil-blue-400:#3b82f6;--abaabil-gray-900:#111827;--abaabil-gray-300:#d1d5db;--abaabil-gray-100:#f3f4f6;--abaabil-white:#fff;--abaabil-red-600:#dc2626;--abaabil-green-600:#16a34a;--abaabil-amber-600:#b45309;--color-primary:var(--abaabil-blue-500);--color-primary-hover:var(--abaabil-blue-400);--color-primary-fg:var(--abaabil-white);--color-surface:var(--abaabil-white);--color-text:var(--abaabil-gray-900);--color-border:var(--abaabil-gray-300);--color-muted:var(--abaabil-gray-100);--color-danger:var(--abaabil-red-600);--color-success:var(--abaabil-green-600);--color-warning:var(--abaabil-amber-600);--color-focus-ring:var(--abaabil-blue-500);--color-overlay:#00000080;--color-text-muted:#6b7280;--color-placeholder:#9ca3af;--radius-md:.375rem;--space-1:.25rem;--space-2:.5rem;--space-3:.75rem;--space-4:1rem;--font-size-sm:.875rem;--font-size-lg:1.125rem;--font-weight-semibold:600;--control-height-md:2.5rem;--duration-fast:.15s}@media (prefers-color-scheme:dark){:root:not([data-theme=light]){--color-surface:#0b0f19;--color-text:#e5e7eb;--color-border:#374151;--color-muted:#1f2937;--color-text-muted:#9ca3af;--color-placeholder:#6b7280}}:root[data-theme=dark]{--color-surface:#0b0f19;--color-text:#e5e7eb;--color-border:#374151;--color-muted:#1f2937;--color-text-muted:#9ca3af;--color-placeholder:#6b7280}}@layer abaabil.base{.abaabil-visually-hidden{clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}}@layer abaabil.components{.abaabil-accordion-group{border:1px solid var(--color-border);border-radius:var(--radius-md);overflow:clip}.abaabil-accordion-group .abaabil-accordion+.abaabil-accordion{border-top:1px solid var(--color-border)}.abaabil-accordion{color:var(--color-text)}.abaabil-accordion__summary{list-style:none}.abaabil-accordion__summary::-webkit-details-marker{display:none}.abaabil-accordion__summary{justify-content:space-between;align-items:center;gap:var(--space-2);min-height:var(--control-height-md);padding:var(--space-3) var(--space-4);font-size:var(--font-size-sm);font-weight:var(--font-weight-semibold);cursor:pointer;-webkit-user-select:none;user-select:none;line-height:1.3;display:flex}.abaabil-accordion__summary:hover{background:var(--color-muted)}.abaabil-accordion__summary:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:-2px}.abaabil-accordion__summary:after{content:"";block-size:.5rem;inline-size:.5rem;transition:transform var(--duration-fast) ease;border-bottom:2px solid;border-right:2px solid;flex:none;transform:rotate(45deg)}.abaabil-accordion[open]>.abaabil-accordion__summary:after{transform:rotate(-135deg)}.abaabil-accordion__panel{padding:0 var(--space-4) var(--space-3)}@media (prefers-reduced-motion:reduce){.abaabil-accordion__summary:after{transition:none}}.abaabil-alert{--alert-accent:var(--color-primary);gap:var(--space-1);padding-block:var(--space-2);padding-inline:var(--space-3);border:1px solid color-mix(in srgb, var(--alert-accent) 35%, var(--color-surface));border-inline-start:3px solid var(--alert-accent);border-radius:var(--radius-md);background:color-mix(in srgb, var(--alert-accent) 8%, var(--color-surface));color:var(--color-text);font-size:var(--font-size-sm);flex-direction:column;line-height:1.5;display:flex}.abaabil-alert[data-variant=success]{--alert-accent:var(--color-success)}.abaabil-alert[data-variant=warning]{--alert-accent:var(--color-warning)}.abaabil-alert[data-variant=danger]{--alert-accent:var(--color-danger)}.abaabil-alert__title{font-weight:var(--font-weight-semibold);color:var(--color-text)}.abaabil-alert:empty{display:none}.abaabil-breadcrumb__list{align-items:center;gap:var(--space-2);font-size:var(--font-size-sm);flex-wrap:wrap;margin:0;padding:0;list-style:none;display:flex}.abaabil-breadcrumb__item{align-items:center;gap:var(--space-2);color:var(--color-text-muted);display:flex}.abaabil-breadcrumb__item+.abaabil-breadcrumb__item:before{content:"";opacity:.6;border-block-start:1.5px solid;border-inline-end:1.5px solid;flex:none;block-size:.35rem;inline-size:.35rem;rotate:45deg}.abaabil-breadcrumb__list:is(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi)) .abaabil-breadcrumb__item+.abaabil-breadcrumb__item:before{rotate:-135deg}.abaabil-breadcrumb__link{color:var(--color-text-muted);text-underline-offset:.2em;border-radius:var(--radius-md);text-decoration:underline}.abaabil-breadcrumb__link:hover{color:var(--color-text)}.abaabil-breadcrumb__link:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:2px}.abaabil-breadcrumb__current{color:var(--color-text);font-weight:var(--font-weight-semibold)}.abaabil-button{--btn-bg:var(--color-primary);--btn-fg:var(--color-primary-fg);--btn-border:var(--color-primary);--btn-height:var(--control-height-md);--btn-padding-x:max(.375rem, calc((var(--btn-height) - var(--font-size-sm)) / 2));--btn-radius:var(--radius-md);justify-content:center;align-items:center;gap:var(--space-2);height:var(--btn-height);padding-inline:var(--btn-padding-x);border:1px solid var(--btn-border);border-radius:var(--btn-radius);background:var(--btn-bg);color:var(--btn-fg);font-size:var(--font-size-sm);font-weight:var(--font-weight-semibold);white-space:nowrap;cursor:pointer;transition:background-color var(--duration-fast) ease, border-color var(--duration-fast) ease;line-height:1;text-decoration:none;display:inline-flex}.abaabil-button:hover{--btn-bg:var(--color-primary-hover);--btn-border:var(--color-primary-hover)}.abaabil-button:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:2px}.abaabil-button[data-variant=secondary]{--btn-bg:transparent;--btn-fg:var(--color-text);--btn-border:var(--color-border)}.abaabil-button[data-variant=ghost]{--btn-bg:transparent;--btn-fg:var(--color-text);--btn-border:transparent}.abaabil-button[data-size=sm]{--btn-height:2rem}.abaabil-button[data-size=lg]{--btn-height:3rem}.abaabil-button:disabled,.abaabil-button[aria-disabled=true]{opacity:.5;cursor:not-allowed}@media (prefers-reduced-motion:reduce){.abaabil-button{transition:none}}.abaabil-checkbox{--checkbox-radius:min(var(--radius-md), .25rem);appearance:none;border:1px solid var(--color-border);border-radius:var(--checkbox-radius);background:var(--color-surface);cursor:pointer;width:1.125rem;height:1.125rem;transition:background-color var(--duration-fast) ease, border-color var(--duration-fast) ease;flex-shrink:0;place-content:center;margin:0;display:inline-grid}.abaabil-checkbox:before{content:"";background:var(--color-primary-fg);clip-path:polygon(14% 44%,0 65%,50% 100%,100% 16%,80% 0%,43% 62%);width:.65rem;height:.65rem;transition:transform var(--duration-fast) ease;transform:scale(0)}.abaabil-checkbox:checked,.abaabil-checkbox:indeterminate{background:var(--color-primary);border-color:var(--color-primary)}.abaabil-checkbox:checked:before,.abaabil-checkbox:indeterminate:before{transform:scale(1)}.abaabil-checkbox:indeterminate:before{clip-path:none;border-radius:1px;width:.65rem;height:.125rem}.abaabil-checkbox:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:2px}.abaabil-checkbox:disabled{opacity:.5;cursor:not-allowed}.abaabil-checkbox__wrapper{align-items:flex-start;gap:var(--space-2);display:inline-flex}.abaabil-checkbox__label{font-size:var(--font-size-sm);color:var(--color-text);cursor:pointer}.abaabil-checkbox__description{font-size:var(--font-size-sm);color:var(--color-text);opacity:.75;display:block}.abaabil-checkbox-group{border:1px solid var(--color-border);border-radius:var(--radius-md);padding:var(--space-3)}.abaabil-checkbox-group__legend{padding-inline:var(--space-1);font-size:var(--font-size-sm);font-weight:var(--font-weight-semibold);color:var(--color-text)}@media (prefers-reduced-motion:reduce){.abaabil-checkbox,.abaabil-checkbox:before{transition:none}}.abaabil-combobox{display:inline-block;position:relative}.abaabil-combobox__label{clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.abaabil-combobox__label--visible{margin-bottom:var(--space-1);font-size:var(--font-size-sm);color:var(--color-text);display:block}.abaabil-combobox__input{height:var(--control-height-md);--input-padding-x:max(.375rem, calc((var(--control-height-md) - var(--font-size-sm)) / 2));padding-inline:var(--input-padding-x);border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface);color:var(--color-text);font-size:var(--font-size-sm);width:100%}.abaabil-combobox__input:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:2px}.abaabil-combobox__list{z-index:10;width:100%;max-height:15rem;padding:var(--space-1);border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface);margin:0;list-style:none;position:absolute;inset-block-start:calc(100% + var(--space-1));inset-inline-start:0;overflow-y:auto}.abaabil-combobox__option{padding:var(--space-2) var(--space-3);border-radius:var(--radius-md);font-size:var(--font-size-sm);color:var(--color-text);cursor:pointer}.abaabil-combobox__option:hover,.abaabil-combobox__option[data-active=true]{background:var(--color-muted)}@supports (anchor-name:--x){.abaabil-combobox__input{anchor-name:--abaabil-combobox-input}.abaabil-combobox__list{position-anchor:--abaabil-combobox-input;position-area:block-end span-inline-end;position-try-fallbacks:block-start span-inline-end;width:anchor-size(width);position:fixed;inset:auto}}.abaabil-dialog{--dialog-width:32rem;--dialog-padding:var(--space-4);width:min(var(--dialog-width), calc(100vw - 2rem));padding:var(--dialog-padding);border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface);color:var(--color-text)}.abaabil-dialog::backdrop{background:var(--color-overlay)}.abaabil-dialog__title{margin:0 0 var(--space-3);font-size:var(--font-size-lg);font-weight:var(--font-weight-semibold)}@starting-style{.abaabil-dialog[open]{opacity:0;transform:translateY(.5rem)}}.abaabil-dialog[open]{opacity:1;transition:opacity var(--duration-fast) ease, transform var(--duration-fast) ease;transform:none}@media (prefers-reduced-motion:reduce){.abaabil-dialog[open]{transition:none}@starting-style{.abaabil-dialog[open]{opacity:1;transform:none}}}.abaabil-input{--input-height:var(--control-height-md);--input-padding-x:max(.375rem, calc((var(--input-height) - var(--font-size-sm)) / 2));--input-border:var(--color-border);--input-placeholder-fg:var(--color-placeholder);width:100%;height:var(--input-height);padding-inline:var(--input-padding-x);border:1px solid var(--input-border);border-radius:var(--radius-md);background:var(--color-surface);color:var(--color-text);font-size:var(--font-size-sm);transition:border-color var(--duration-fast) ease;display:block}.abaabil-input::placeholder{color:var(--input-placeholder-fg)}.abaabil-input:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:2px}.abaabil-input:disabled{opacity:.5;cursor:not-allowed}.abaabil-input[aria-invalid=true]{--input-border:var(--color-danger)}.abaabil-input-group{gap:var(--space-1);flex-direction:column;display:flex}.abaabil-input__label{font-size:var(--font-size-sm);color:var(--color-text)}.abaabil-input__description{--input-description-fg:var(--color-text-muted);font-size:var(--font-size-sm);color:var(--input-description-fg)}.abaabil-input__error{font-size:var(--font-size-sm);color:var(--color-danger)}@media (prefers-reduced-motion:reduce){.abaabil-input{transition:none}}.abaabil-menu{inline-size:max-content;min-inline-size:10rem;max-inline-size:min(18rem,100vw - 2rem);padding:var(--space-1);border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface);color:var(--color-text)}.abaabil-menu::backdrop{background:0 0}.abaabil-menu__trigger{font:inherit;color:inherit}.abaabil-menu__item{appearance:none;inline-size:100%;padding-block:var(--space-2);padding-inline:var(--space-3);border-radius:var(--radius-md);color:var(--color-text);font:inherit;font-size:var(--font-size-sm);text-align:start;white-space:nowrap;cursor:pointer;background:0 0;border:0;text-decoration:none;display:block}.abaabil-menu__item:hover{background:var(--color-muted)}.abaabil-menu__item:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:-2px;background:var(--color-muted)}.abaabil-menu__item:disabled{opacity:.5;cursor:not-allowed}.abaabil-menu__item:disabled:hover{background:0 0}@supports (anchor-name:--x){.abaabil-menu{position-area:block-end span-inline-end;position-try-fallbacks:block-start span-inline-end, block-end span-inline-start;margin:0;margin-block-start:var(--space-1);position:fixed;inset:auto}}@starting-style{.abaabil-menu:popover-open{opacity:0}}.abaabil-menu:popover-open{opacity:1;transition:opacity var(--duration-fast) ease}@media (prefers-reduced-motion:reduce){.abaabil-menu:popover-open{transition:none}@starting-style{.abaabil-menu:popover-open{opacity:1}}}.abaabil-popover{inline-size:max-content;max-inline-size:min(22rem,100vw - 2rem);padding:var(--space-3);border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface);color:var(--color-text);font-size:var(--font-size-sm)}.abaabil-popover:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:2px}.abaabil-popover::backdrop{background:0 0}.abaabil-popover__trigger{font:inherit;color:inherit}@supports (anchor-name:--x){.abaabil-popover{position-area:block-end span-inline-end;position-try-fallbacks:block-start span-inline-end, block-end span-inline-start;margin:0;margin-block-start:var(--space-1);position:fixed;inset:auto}}@starting-style{.abaabil-popover:popover-open{opacity:0}}.abaabil-popover:popover-open{opacity:1;transition:opacity var(--duration-fast) ease}@media (prefers-reduced-motion:reduce){.abaabil-popover:popover-open{transition:none}@starting-style{.abaabil-popover:popover-open{opacity:1}}}.abaabil-progress{appearance:none;background:var(--color-muted);block-size:.5rem;inline-size:100%;color:var(--color-primary);border:0;border-radius:999px;display:block;overflow:hidden}.abaabil-progress::-webkit-progress-bar{background:var(--color-muted);border-radius:999px}.abaabil-progress::-webkit-progress-value{background:var(--color-primary);transition:inline-size var(--duration-fast) ease;border-radius:999px}.abaabil-progress::-moz-progress-bar{background:var(--color-primary);border-radius:999px}.abaabil-progress:indeterminate{background:linear-gradient(90deg, var(--color-primary) 0 35%, transparent 35% 100%) 0 0 / 250% 100% no-repeat var(--color-muted);animation:1.4s linear infinite abaabil-progress-slide}.abaabil-progress:indeterminate::-webkit-progress-bar{background:0 0}.abaabil-progress:indeterminate::-moz-progress-bar{background:0 0}@keyframes abaabil-progress-slide{0%{background-position:-60% 0}to{background-position:160% 0}}.abaabil-progress-group{gap:var(--space-1);flex-direction:column;display:flex}.abaabil-progress__label{font-size:var(--font-size-sm);color:var(--color-text)}.abaabil-progress__description{font-size:var(--font-size-sm);color:var(--color-text-muted)}@media (prefers-reduced-motion:reduce){.abaabil-progress::-webkit-progress-value{transition:none}.abaabil-progress:indeterminate{background-position:40% 0;animation:none}}.abaabil-radio{appearance:none;border:1px solid var(--color-border);background:var(--color-surface);cursor:pointer;width:1.125rem;height:1.125rem;transition:background-color var(--duration-fast) ease, border-color var(--duration-fast) ease;border-radius:50%;flex-shrink:0;place-content:center;margin:0;display:inline-grid}.abaabil-radio:before{content:"";background:var(--color-primary-fg);width:.5rem;height:.5rem;transition:transform var(--duration-fast) ease;border-radius:50%;transform:scale(0)}.abaabil-radio:checked{background:var(--color-primary);border-color:var(--color-primary)}.abaabil-radio:checked:before{transform:scale(1)}.abaabil-radio:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:2px}.abaabil-radio:disabled{opacity:.5;cursor:not-allowed}.abaabil-radio__wrapper{align-items:flex-start;gap:var(--space-2);display:inline-flex}.abaabil-radio__label{font-size:var(--font-size-sm);color:var(--color-text);cursor:pointer}.abaabil-radio__description{font-size:var(--font-size-sm);color:var(--color-text);opacity:.75;display:block}.abaabil-radio-group{border:1px solid var(--color-border);border-radius:var(--radius-md);padding:var(--space-3)}.abaabil-radio-group__legend{padding-inline:var(--space-1);font-size:var(--font-size-sm);font-weight:var(--font-weight-semibold);color:var(--color-text)}@media (prefers-reduced-motion:reduce){.abaabil-radio,.abaabil-radio:before{transition:none}}.abaabil-select{--select-height:var(--control-height-md);--select-padding-x:max(.375rem, calc((var(--select-height) - var(--font-size-sm)) / 2));appearance:none;height:var(--select-height);padding-inline:var(--select-padding-x);border:1px solid var(--color-border);border-radius:var(--radius-md);background-color:var(--color-surface);background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M2.5 4.5L6 8l3.5-3.5' stroke='currentColor' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right var(--select-padding-x) center;color:var(--color-text);font-size:var(--font-size-sm);cursor:pointer;transition:border-color var(--duration-fast) ease;background-size:.75rem .75rem;padding-inline-end:calc(var(--select-padding-x) + 1.125rem);line-height:1}.abaabil-select:is(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi)){background-position:left var(--select-padding-x) center;padding-inline-start:calc(var(--select-padding-x) + 1.125rem);padding-inline-end:var(--select-padding-x)}.abaabil-select:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:2px}.abaabil-select:disabled{opacity:.5;cursor:not-allowed}.abaabil-select-group{gap:var(--space-1);flex-direction:column;display:flex}.abaabil-select__label{font-size:var(--font-size-sm);color:var(--color-text)}.abaabil-select__description{font-size:var(--font-size-sm);color:var(--color-text);opacity:.75;margin:0}.abaabil-select__error{font-size:var(--font-size-sm);color:var(--color-danger);margin:0}.abaabil-select[aria-invalid=true]{border-color:var(--color-danger)}@media (prefers-reduced-motion:reduce){.abaabil-select{transition:none}}.abaabil-slider{--slider-track:.3rem;--slider-thumb:1.1rem;appearance:none;inline-size:100%;block-size:var(--slider-thumb);cursor:pointer;background:0 0;margin:0}.abaabil-slider::-webkit-slider-runnable-track{block-size:var(--slider-track);background:var(--color-muted);border-radius:999px}.abaabil-slider::-moz-range-track{block-size:var(--slider-track);background:var(--color-muted);border-radius:999px}.abaabil-slider::-webkit-slider-thumb{appearance:none;inline-size:var(--slider-thumb);block-size:var(--slider-thumb);border:2px solid var(--color-surface);background:var(--color-primary);border-radius:50%;margin-block-start:calc((var(--slider-track) - var(--slider-thumb)) / 2)}.abaabil-slider::-moz-range-thumb{inline-size:var(--slider-thumb);block-size:var(--slider-thumb);border:2px solid var(--color-surface);background:var(--color-primary);border-radius:50%}.abaabil-slider::-moz-range-progress{block-size:var(--slider-track);background:var(--color-primary);border-radius:999px}.abaabil-slider:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:4px;border-radius:999px}.abaabil-slider:disabled{opacity:.5;cursor:not-allowed}.abaabil-slider-group{gap:var(--space-1);flex-direction:column;display:flex}.abaabil-slider__header{justify-content:space-between;align-items:baseline;gap:var(--space-2);display:flex}.abaabil-slider__label{font-size:var(--font-size-sm);color:var(--color-text)}.abaabil-slider__output{font-size:var(--font-size-sm);font-weight:var(--font-weight-semibold);font-variant-numeric:tabular-nums;color:var(--color-text)}.abaabil-slider__description{font-size:var(--font-size-sm);color:var(--color-text-muted)}.abaabil-switch{--switch-width:2.25rem;--switch-height:1.25rem;--switch-gap:2px;--switch-thumb:calc(var(--switch-height) - (var(--switch-gap) * 2));appearance:none;inline-size:var(--switch-width);block-size:var(--switch-height);border:1px solid var(--color-border);background:var(--color-muted);cursor:pointer;transition:background-color var(--duration-fast) ease, border-color var(--duration-fast) ease;border-radius:999px;flex-shrink:0;margin:0;position:relative}.abaabil-switch:before{content:"";inline-size:var(--switch-thumb);block-size:var(--switch-thumb);background:var(--color-surface);transition:inset-inline-start var(--duration-fast) ease;border-radius:50%;position:absolute;inset-block-start:50%;inset-inline-start:var(--switch-gap);translate:0 -50%}.abaabil-switch:checked{background:var(--color-primary);border-color:var(--color-primary)}.abaabil-switch:checked:before{inset-inline-start:calc(100% - var(--switch-thumb) - var(--switch-gap))}.abaabil-switch:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:2px}.abaabil-switch:disabled{opacity:.5;cursor:not-allowed}.abaabil-switch__wrapper{align-items:center;gap:var(--space-2);flex-wrap:wrap;display:inline-flex}.abaabil-switch__label{font-size:var(--font-size-sm);color:var(--color-text);cursor:pointer}.abaabil-switch__description{font-size:var(--font-size-sm);color:var(--color-text-muted);flex-basis:100%;display:block}@media (prefers-reduced-motion:reduce){.abaabil-switch,.abaabil-switch:before{transition:none}}.abaabil-tabs{gap:var(--space-3);flex-direction:column;display:flex}.abaabil-tabs__list{gap:var(--space-1);border-block-end:1px solid var(--color-border);display:flex}.abaabil-tabs__tab{appearance:none;padding-block:var(--space-2);padding-inline:var(--space-3);color:var(--color-text-muted);font:inherit;font-size:var(--font-size-sm);font-weight:var(--font-weight-semibold);white-space:nowrap;cursor:pointer;transition:color var(--duration-fast) ease, border-color var(--duration-fast) ease;background:0 0;border:0;border-block-end:2px solid #0000;margin-block-end:-1px;line-height:1.3}.abaabil-tabs__tab:hover{color:var(--color-text)}.abaabil-tabs__tab[data-selected=true]{color:var(--color-text);border-block-end-color:var(--color-primary)}.abaabil-tabs__tab:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:-2px}.abaabil-tabs__tab:disabled{opacity:.5;cursor:not-allowed}.abaabil-tabs__list[data-orientation=vertical]{border-block-end:0;border-inline-end:1px solid var(--color-border);flex-direction:column}.abaabil-tabs__list[data-orientation=vertical] .abaabil-tabs__tab{text-align:start;border-block-end:0;border-inline-end:2px solid #0000;margin-block-end:0;margin-inline-end:-1px}.abaabil-tabs__list[data-orientation=vertical] .abaabil-tabs__tab[data-selected=true]{border-inline-end-color:var(--color-primary)}.abaabil-tabs__panel:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:2px}@media (prefers-reduced-motion:reduce){.abaabil-tabs__tab{transition:none}}.abaabil-textarea{--textarea-padding-block:var(--space-2);--textarea-padding-x:var(--space-3);--textarea-border:var(--color-border);--textarea-placeholder-fg:var(--color-placeholder);width:100%;padding-block:var(--textarea-padding-block);padding-inline:var(--textarea-padding-x);border:1px solid var(--textarea-border);border-radius:var(--radius-md);background:var(--color-surface);color:var(--color-text);font:inherit;font-size:var(--font-size-sm);resize:block;transition:border-color var(--duration-fast) ease;line-height:1.5;display:block}.abaabil-textarea::placeholder{color:var(--textarea-placeholder-fg)}.abaabil-textarea:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:2px}.abaabil-textarea:disabled{opacity:.5;cursor:not-allowed;resize:none}.abaabil-textarea[aria-invalid=true]{--textarea-border:var(--color-danger)}.abaabil-textarea-group{gap:var(--space-1);flex-direction:column;display:flex}.abaabil-textarea__label{font-size:var(--font-size-sm);color:var(--color-text)}.abaabil-textarea__description{font-size:var(--font-size-sm);color:var(--color-text-muted)}.abaabil-textarea__error{font-size:var(--font-size-sm);color:var(--color-danger)}@media (prefers-reduced-motion:reduce){.abaabil-textarea{transition:none}}.abaabil-tooltip{display:inline-flex;position:relative}.abaabil-tooltip__bubble{z-index:20;inline-size:max-content;max-inline-size:16rem;padding-block:var(--space-1);padding-inline:var(--space-2);border-radius:var(--radius-md);background:var(--color-text);color:var(--color-surface);font-size:var(--font-size-sm);text-align:start;opacity:0;visibility:hidden;transition:opacity var(--duration-fast) ease, visibility 0s linear var(--duration-fast);pointer-events:none;line-height:1.4;position:absolute;inset-inline-start:50%;translate:-50%}.abaabil-tooltip[data-placement=top] .abaabil-tooltip__bubble{inset-block-end:calc(100% + var(--space-1))}.abaabil-tooltip[data-placement=bottom] .abaabil-tooltip__bubble{inset-block-start:calc(100% + var(--space-1))}.abaabil-tooltip:hover .abaabil-tooltip__bubble,.abaabil-tooltip:focus-within .abaabil-tooltip__bubble{opacity:1;visibility:visible;transition:none}.abaabil-tooltip[data-dismissed=true] .abaabil-tooltip__bubble{opacity:0;visibility:hidden}@media (prefers-reduced-motion:reduce){.abaabil-tooltip__bubble{transition:none}}}
@@ -0,0 +1,2 @@
1
+ "use client";
2
+ import{jsxs as b,jsx as l}from"react/jsx-runtime";import{useId as p}from"react";import"./switch.css";import h from"./index.js";function m({label:a,description:e,id:o,"aria-describedby":c,...i}){const s=p(),r=o??`${s}-switch`,n=`${s}-description`,t=!!(a||i["aria-label"]||i["aria-labelledby"]);typeof process<"u"&&process.env.NODE_ENV!=="production"&&!t&&console.warn("abaabil/switch: no `label` given, so the switch has no accessible name and screen readers announce it as unnamed.");const d=[c,e?n:null].filter(Boolean).join(" ")||void 0;return b("span",{className:"abaabil-switch__wrapper",children:[l(h,{...i,id:r,"aria-describedby":d}),a?l("label",{htmlFor:r,className:"abaabil-switch__label",children:a}):null,e?l("span",{id:n,className:"abaabil-switch__description",children:e}):null]})}export{m as default};
@@ -0,0 +1 @@
1
+ import{jsx as c}from"react/jsx-runtime";function i({className:a,...s}){const t=a?`abaabil-switch ${a}`:"abaabil-switch";return c("input",{type:"checkbox",role:"switch",className:t,...s})}export{i as default};
@@ -0,0 +1 @@
1
+ import"./switch.css";import{default as e}from"./index.js";export{e as default};
@@ -0,0 +1 @@
1
+ @layer abaabil.components{.abaabil-switch{--switch-width:2.25rem;--switch-height:1.25rem;--switch-gap:2px;--switch-thumb:calc(var(--switch-height) - (var(--switch-gap) * 2));appearance:none;inline-size:var(--switch-width);block-size:var(--switch-height);border:1px solid var(--color-border);background:var(--color-muted);cursor:pointer;transition:background-color var(--duration-fast) ease, border-color var(--duration-fast) ease;border-radius:999px;flex-shrink:0;margin:0;position:relative}.abaabil-switch:before{content:"";inline-size:var(--switch-thumb);block-size:var(--switch-thumb);background:var(--color-surface);transition:inset-inline-start var(--duration-fast) ease;border-radius:50%;position:absolute;inset-block-start:50%;inset-inline-start:var(--switch-gap);translate:0 -50%}.abaabil-switch:checked{background:var(--color-primary);border-color:var(--color-primary)}.abaabil-switch:checked:before{inset-inline-start:calc(100% - var(--switch-thumb) - var(--switch-gap))}.abaabil-switch:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:2px}.abaabil-switch:disabled{opacity:.5;cursor:not-allowed}.abaabil-switch__wrapper{align-items:center;gap:var(--space-2);flex-wrap:wrap;display:inline-flex}.abaabil-switch__label{font-size:var(--font-size-sm);color:var(--color-text);cursor:pointer}.abaabil-switch__description{font-size:var(--font-size-sm);color:var(--color-text-muted);flex-basis:100%;display:block}@media (prefers-reduced-motion:reduce){.abaabil-switch,.abaabil-switch:before{transition:none}}}
@@ -0,0 +1,2 @@
1
+ "use client";
2
+ import{jsxs as B,jsx as s}from"react/jsx-runtime";import{useState as w,useRef as R,useId as j}from"react";import"./tabs.css";function C({items:n,label:c,labelledBy:d,defaultIndex:u=0,onChange:N,orientation:i="horizontal",activation:f="automatic",className:m,...r}){const[o,_]=w(u),[x,p]=w(u),y=R([]),h=j(),D=m?`abaabil-tabs ${m}`:"abaabil-tabs",v=a=>`${h}-tab-${a}`,k=a=>`${h}-panel-${a}`,$=!!(c||d||r["aria-label"]||r["aria-labelledby"]);typeof process<"u"&&process.env.NODE_ENV!=="production"&&!$&&console.warn("abaabil/tabs: no `label` or `labelledBy` given, so the tablist has no accessible name. Pass `label`, `labelledBy`, `aria-label`, or `aria-labelledby`.");function b(a){_(a),N?.(a)}function A(a){p(a),y.current[a]?.focus(),f==="automatic"&&b(a)}const E=i==="vertical"?"ArrowDown":"ArrowRight",I=i==="vertical"?"ArrowUp":"ArrowLeft";function g(a,e){const l=n.length-1;let t=null;if(a.key===E)t=e===l?0:e+1;else if(a.key===I)t=e===0?l:e-1;else if(a.key==="Home")t=0;else if(a.key==="End")t=l;else if(f==="manual"&&(a.key==="Enter"||a.key===" ")){a.preventDefault(),b(e);return}else return;a.preventDefault(),A(t)}return B("div",{className:D,...r,children:[s("div",{role:"tablist","aria-label":c,"aria-labelledby":d,"aria-orientation":i,className:"abaabil-tabs__list","data-orientation":i,children:n.map((a,e)=>s("button",{type:"button",role:"tab",id:v(e),"aria-selected":e===o,"aria-controls":k(e),tabIndex:e===x?0:-1,ref:l=>{y.current[e]=l},className:"abaabil-tabs__tab","data-selected":e===o?"true":void 0,onClick:()=>{p(e),b(e)},onKeyDown:l=>g(l,e),children:a.label},a.key??e))}),n.map((a,e)=>e===o?s("div",{role:"tabpanel",id:k(e),"aria-labelledby":v(e),tabIndex:0,className:"abaabil-tabs__panel",children:a.children},a.key??e):null)]})}export{C as default};
@@ -0,0 +1,2 @@
1
+ "use client";
2
+ import{jsxs as m,jsx as s}from"react/jsx-runtime";import{useState as u}from"react";function p({items:l,defaultIndex:n=0,onChange:b,className:t,...c}){const[i,d]=u(n),o=t?`abaabil-tabs ${t}`:"abaabil-tabs";function r(a){d(a),b?.(a)}return m("div",{className:o,...c,children:[s("div",{className:"abaabil-tabs__list",children:l.map((a,e)=>s("button",{type:"button",className:"abaabil-tabs__tab","data-selected":e===i?"true":void 0,onClick:()=>r(e),children:a.label},a.key??e))}),l.map((a,e)=>e===i?s("div",{className:"abaabil-tabs__panel",children:a.children},a.key??e):null)]})}export{p as default};
@@ -0,0 +1 @@
1
+ import"./tabs.css";import{default as e}from"./index.js";export{e as default};
@@ -0,0 +1 @@
1
+ @layer abaabil.components{.abaabil-tabs{gap:var(--space-3);flex-direction:column;display:flex}.abaabil-tabs__list{gap:var(--space-1);border-block-end:1px solid var(--color-border);display:flex}.abaabil-tabs__tab{appearance:none;padding-block:var(--space-2);padding-inline:var(--space-3);border:0;color:var(--color-text-muted);font:inherit;font-size:var(--font-size-sm);font-weight:var(--font-weight-semibold);white-space:nowrap;cursor:pointer;transition:color var(--duration-fast) ease, border-color var(--duration-fast) ease;background:0 0;border-block-end:2px solid #0000;margin-block-end:-1px;line-height:1.3}.abaabil-tabs__tab:hover{color:var(--color-text)}.abaabil-tabs__tab[data-selected=true]{color:var(--color-text);border-block-end-color:var(--color-primary)}.abaabil-tabs__tab:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:-2px}.abaabil-tabs__tab:disabled{opacity:.5;cursor:not-allowed}.abaabil-tabs__list[data-orientation=vertical]{border-block-end:0;border-inline-end:1px solid var(--color-border);flex-direction:column}.abaabil-tabs__list[data-orientation=vertical] .abaabil-tabs__tab{text-align:start;border-block-end:0;border-inline-end:2px solid #0000;margin-block-end:0;margin-inline-end:-1px}.abaabil-tabs__list[data-orientation=vertical] .abaabil-tabs__tab[data-selected=true]{border-inline-end-color:var(--color-primary)}.abaabil-tabs__panel:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:2px}@media (prefers-reduced-motion:reduce){.abaabil-tabs__tab{transition:none}}}
@@ -0,0 +1,2 @@
1
+ "use client";
2
+ import{jsxs as x,jsx as e}from"react/jsx-runtime";import{useId as f}from"react";import"./textarea.css";import _ from"./index.js";function h({label:l,hideLabel:d=!1,description:r,error:a,required:n=!1,id:c,"aria-describedby":u,...i}){const o=f(),s=c??`${o}-textarea`,t=`${o}-description`,b=`${o}-error`,m=!!(l||i["aria-label"]||i["aria-labelledby"]);typeof process<"u"&&process.env.NODE_ENV!=="production"&&!m&&console.warn("abaabil/textarea: no `label` given, so the textarea has no accessible name. Pass `label`, `aria-label`, or `aria-labelledby`.");const p=[u,r?t:null,a?b:null].filter(Boolean).join(" ")||void 0;return x("div",{className:"abaabil-textarea-group",children:[l?e("label",{htmlFor:s,className:d?"abaabil-textarea__label abaabil-visually-hidden":"abaabil-textarea__label",children:l}):null,e(_,{...i,id:s,required:n,"aria-invalid":a?!0:void 0,"aria-describedby":p}),r?e("div",{id:t,className:"abaabil-textarea__description",children:r}):null,a?e("div",{id:b,className:"abaabil-textarea__error",role:"alert",children:a}):null]})}export{h as default};
@@ -0,0 +1 @@
1
+ import{jsx as s}from"react/jsx-runtime";function o({rows:t=3,className:a,...e}){const r=a?`abaabil-textarea ${a}`:"abaabil-textarea";return s("textarea",{rows:t,className:r,...e})}export{o as default};
@@ -0,0 +1 @@
1
+ import"./textarea.css";import{default as e}from"./index.js";export{e as default};
@@ -0,0 +1 @@
1
+ @layer abaabil.components{.abaabil-textarea{--textarea-padding-block:var(--space-2);--textarea-padding-x:var(--space-3);--textarea-border:var(--color-border);--textarea-placeholder-fg:var(--color-placeholder);width:100%;padding-block:var(--textarea-padding-block);padding-inline:var(--textarea-padding-x);border:1px solid var(--textarea-border);border-radius:var(--radius-md);background:var(--color-surface);color:var(--color-text);font:inherit;font-size:var(--font-size-sm);resize:block;transition:border-color var(--duration-fast) ease;line-height:1.5;display:block}.abaabil-textarea::placeholder{color:var(--textarea-placeholder-fg)}.abaabil-textarea:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:2px}.abaabil-textarea:disabled{opacity:.5;cursor:not-allowed;resize:none}.abaabil-textarea[aria-invalid=true]{--textarea-border:var(--color-danger)}.abaabil-textarea-group{gap:var(--space-1);flex-direction:column;display:flex}.abaabil-textarea__label{font-size:var(--font-size-sm);color:var(--color-text)}.abaabil-textarea__description{font-size:var(--font-size-sm);color:var(--color-text-muted)}.abaabil-textarea__error{font-size:var(--font-size-sm);color:var(--color-danger)}@media (prefers-reduced-motion:reduce){.abaabil-textarea{transition:none}}}
package/dist/theme.css CHANGED
@@ -1 +1 @@
1
- @layer abaabil.reset;@layer abaabil.tokens{:root{--abaabil-blue-500:#2563eb;--abaabil-blue-400:#3b82f6;--abaabil-gray-900:#111827;--abaabil-gray-300:#d1d5db;--abaabil-gray-100:#f3f4f6;--abaabil-white:#fff;--abaabil-red-600:#dc2626;--color-primary:var(--abaabil-blue-500);--color-primary-hover:var(--abaabil-blue-400);--color-primary-fg:var(--abaabil-white);--color-surface:var(--abaabil-white);--color-text:var(--abaabil-gray-900);--color-border:var(--abaabil-gray-300);--color-muted:var(--abaabil-gray-100);--color-danger:var(--abaabil-red-600);--color-focus-ring:var(--abaabil-blue-500);--color-overlay:#00000080;--color-text-muted:#6b7280;--color-placeholder:#9ca3af;--radius-md:.375rem;--space-1:.25rem;--space-2:.5rem;--space-3:.75rem;--space-4:1rem;--font-size-sm:.875rem;--font-size-lg:1.125rem;--font-weight-semibold:600;--control-height-md:2.5rem;--duration-fast:.15s}@media (prefers-color-scheme:dark){:root:not([data-theme=light]){--color-surface:#0b0f19;--color-text:#e5e7eb;--color-border:#374151;--color-muted:#1f2937;--color-text-muted:#9ca3af;--color-placeholder:#6b7280}}:root[data-theme=dark]{--color-surface:#0b0f19;--color-text:#e5e7eb;--color-border:#374151;--color-muted:#1f2937;--color-text-muted:#9ca3af;--color-placeholder:#6b7280}}@layer abaabil.base{.abaabil-visually-hidden{clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}}@layer abaabil.components;
1
+ @layer abaabil.reset;@layer abaabil.tokens{:root{--abaabil-blue-500:#2563eb;--abaabil-blue-400:#3b82f6;--abaabil-gray-900:#111827;--abaabil-gray-300:#d1d5db;--abaabil-gray-100:#f3f4f6;--abaabil-white:#fff;--abaabil-red-600:#dc2626;--abaabil-green-600:#16a34a;--abaabil-amber-600:#b45309;--color-primary:var(--abaabil-blue-500);--color-primary-hover:var(--abaabil-blue-400);--color-primary-fg:var(--abaabil-white);--color-surface:var(--abaabil-white);--color-text:var(--abaabil-gray-900);--color-border:var(--abaabil-gray-300);--color-muted:var(--abaabil-gray-100);--color-danger:var(--abaabil-red-600);--color-success:var(--abaabil-green-600);--color-warning:var(--abaabil-amber-600);--color-focus-ring:var(--abaabil-blue-500);--color-overlay:#00000080;--color-text-muted:#6b7280;--color-placeholder:#9ca3af;--radius-md:.375rem;--space-1:.25rem;--space-2:.5rem;--space-3:.75rem;--space-4:1rem;--font-size-sm:.875rem;--font-size-lg:1.125rem;--font-weight-semibold:600;--control-height-md:2.5rem;--duration-fast:.15s}@media (prefers-color-scheme:dark){:root:not([data-theme=light]){--color-surface:#0b0f19;--color-text:#e5e7eb;--color-border:#374151;--color-muted:#1f2937;--color-text-muted:#9ca3af;--color-placeholder:#6b7280}}:root[data-theme=dark]{--color-surface:#0b0f19;--color-text:#e5e7eb;--color-border:#374151;--color-muted:#1f2937;--color-text-muted:#9ca3af;--color-placeholder:#6b7280}}@layer abaabil.base{.abaabil-visually-hidden{clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}}@layer abaabil.components;
@@ -0,0 +1,2 @@
1
+ "use client";
2
+ import{jsxs as m,jsx as f}from"react/jsx-runtime";import{useId as y,useState as E,useRef as h,isValidElement as s,cloneElement as N}from"react";import"./tooltip.css";function g({content:i,placement:l="top",className:o,children:e,...r}){const n=y(),[t,a]=E(!1),c=h(null),p=o?`abaabil-tooltip ${o}`:"abaabil-tooltip";typeof process<"u"&&process.env.NODE_ENV!=="production"&&(s(e)||console.warn("abaabil/tooltip: children must be a single element that can receive props (a button, a link, an input). A string or fragment cannot be described, and a tooltip on a non-focusable element is invisible to keyboard users."));function b(u){u.key==="Escape"&&!t&&a(!0)}const d=s(e)?N(e,{"aria-describedby":[e.props["aria-describedby"],n].filter(Boolean).join(" ")}):e;return m("span",{ref:c,className:p,"data-placement":l,"data-dismissed":t?"true":void 0,onKeyDown:b,onPointerEnter:()=>a(!1),onFocus:()=>a(!1),...r,children:[d,f("span",{id:n,className:"abaabil-tooltip__bubble",role:"tooltip",children:i})]})}export{g as default};
@@ -0,0 +1 @@
1
+ import{jsxs as s,jsx as c}from"react/jsx-runtime";function i({content:t,placement:l="top",className:a,children:e,...n}){const o=a?`abaabil-tooltip ${a}`:"abaabil-tooltip";return s("span",{className:o,"data-placement":l,...n,children:[e,c("span",{className:"abaabil-tooltip__bubble",children:t})]})}export{i as default};
@@ -0,0 +1 @@
1
+ import"./tooltip.css";import{default as e}from"./index.js";export{e as default};
@@ -0,0 +1 @@
1
+ @layer abaabil.components{.abaabil-tooltip{display:inline-flex;position:relative}.abaabil-tooltip__bubble{z-index:20;inline-size:max-content;max-inline-size:16rem;padding-block:var(--space-1);padding-inline:var(--space-2);border-radius:var(--radius-md);background:var(--color-text);color:var(--color-surface);font-size:var(--font-size-sm);text-align:start;opacity:0;visibility:hidden;transition:opacity var(--duration-fast) ease, visibility 0s linear var(--duration-fast);pointer-events:none;line-height:1.4;position:absolute;inset-inline-start:50%;translate:-50%}.abaabil-tooltip[data-placement=top] .abaabil-tooltip__bubble{inset-block-end:calc(100% + var(--space-1))}.abaabil-tooltip[data-placement=bottom] .abaabil-tooltip__bubble{inset-block-start:calc(100% + var(--space-1))}.abaabil-tooltip:hover .abaabil-tooltip__bubble,.abaabil-tooltip:focus-within .abaabil-tooltip__bubble{opacity:1;visibility:visible;transition:none}.abaabil-tooltip[data-dismissed=true] .abaabil-tooltip__bubble{opacity:0;visibility:hidden}@media (prefers-reduced-motion:reduce){.abaabil-tooltip__bubble{transition:none}}}
package/llms.txt ADDED
@@ -0,0 +1,122 @@
1
+ # abaabil
2
+
3
+ > A small React component library: eighteen components, three import tiers
4
+ > each, plain CSS in cascade layers, zero runtime dependencies. React 19 is
5
+ > the only peer dependency.
6
+
7
+ This file is for language models and coding agents. It is the short version
8
+ of README.md: the rules you need to write correct code with this library,
9
+ and the mistakes that are easy to make. Full docs: https://www.abaabil.com
10
+ and https://www.abaabil.com/llms-full.txt
11
+
12
+ ## The three rules that matter most
13
+
14
+ 1. **There is no root export.** `import { Button } from 'abaabil'` throws.
15
+ Import the entry point: `import Button from 'abaabil/button/a11y'`.
16
+ 2. **Import `abaabil/theme.css` once, at your app root.** Without it the
17
+ components render unthemed, because every stylesheet reads custom
18
+ properties that file defines.
19
+ 3. **Use the `a11y` tier** unless you have a specific reason not to. The
20
+ other two exist so you can opt out of CSS or ARIA deliberately, not
21
+ because they are lighter defaults.
22
+
23
+ ## Import shape
24
+
25
+ Every component has exactly three entry points:
26
+
27
+ abaabil/<name> structure only, no CSS, no ARIA
28
+ abaabil/<name>/styled + the component's stylesheet
29
+ abaabil/<name>/a11y + labels, ARIA wiring, keyboard behaviour
30
+
31
+ Each tier default-exports its main component. Components with extra parts
32
+ also export them by name: `CheckboxGroup`, `RadioGroup`, `Disclosure`,
33
+ `Accordion`, `PopoverTrigger`, `PopoverPanel`.
34
+
35
+ Names: button, dialog, popover, menu, tooltip, combobox, input, textarea,
36
+ checkbox, radio, switch, select, slider, accordion, tabs, breadcrumb,
37
+ progress, alert.
38
+
39
+ Stylesheets are also importable on their own as `abaabil/<name>.css`, and
40
+ `abaabil/styles.css` is every component's CSS in one file.
41
+
42
+ ## Per-component notes an agent will otherwise get wrong
43
+
44
+ - **popover** requires an explicit `id` prop. It is not generated, because
45
+ generating it would need `useId` and that would make the component a
46
+ client component. `<Popover id="menu" trigger="Options" label="...">`.
47
+ - **alert** is a live region. It is announced when its *contents change*,
48
+ so render it empty from the start and fill it in. Mounting the whole
49
+ component at the moment you have a message often announces nothing.
50
+ Its role follows `variant`: info/success are polite, warning/danger
51
+ interrupt.
52
+ - **switch** carries `role="switch"` at every tier, including `normal`.
53
+ Use it for a setting that applies immediately; use `checkbox` for a
54
+ value the form submits later.
55
+ - **combobox** and **tabs** are uncontrolled. There is no `value` or
56
+ `selectedIndex` prop; observe changes through `onChange`.
57
+ - **accordion** exclusivity comes from the native `name` attribute on
58
+ `<details>`, which defaults to a shared value. Pass `name={undefined}`
59
+ to allow several panels open at once.
60
+ - **checkbox** takes `indeterminate` as a prop because the DOM property
61
+ has no HTML attribute.
62
+ - **dialog** is the native `<dialog>` with `showModal()`. Do not add a
63
+ focus trap; the browser already contains focus.
64
+ - **menu** requires an explicit `id`, like popover. Use it only for a
65
+ list of *actions*. A button revealing navigation links is not a menu;
66
+ that is `popover` with links inside, and marking it `role="menu"`
67
+ announces a widget the user cannot operate as one.
68
+ - **tooltip** takes a single focusable element as its child and clones
69
+ it. Never put essential information or anything interactive in one:
70
+ there is no hover on touch, and moving towards a link inside a tooltip
71
+ dismisses it. If you need either, use `popover`.
72
+ - **progress** leaves `value` unset for an indeterminate bar. Pass
73
+ `valueText` whenever the number is not a percentage, or "3 of 8 files"
74
+ is announced as "38 percent".
75
+ - **slider** is a native range input. Pass `formatValue` when the raw
76
+ number is not self-explanatory; it sets `aria-valuetext`.
77
+ - **breadcrumb** renders the last item as text rather than a link, and
78
+ marks it `aria-current="page"`. Pass items root first.
79
+ - Components needing an accessible name (**input**, **textarea**,
80
+ **select**, **checkbox**, **radio**, **switch**, **combobox**,
81
+ **popover**, **tabs**, **progress**, **slider**) warn in development
82
+ when given none. Pass
83
+ `label`, or `aria-label`/`aria-labelledby`.
84
+
85
+ ## Server Components
86
+
87
+ Thirty-six of the fifty-four entry points carry no `"use client"` and
88
+ render inside a React Server Component tree.
89
+
90
+ - Server-safe at **every** tier: button, accordion, popover, alert,
91
+ breadcrumb. Tooltip too at `normal` and `styled`, because it shows and
92
+ hides in CSS rather than JavaScript.
93
+ - Client at **every** tier: combobox, tabs. Neither has a native element
94
+ to build on, so both need state.
95
+ - Everything else is server-safe at `normal` and `styled`, and a client
96
+ component at `a11y`, where `useId` mints the ids that tie labels to
97
+ controls.
98
+
99
+ ## Theming
100
+
101
+ CSS custom properties only. No provider, no context, no JS theme object.
102
+ Set `--color-primary`, `--radius-md`, `--control-height-md` and the rest on
103
+ `:root` or on any scope element. Dark mode is the same tokens redefined
104
+ under `prefers-color-scheme` and `[data-theme="dark"]`, both of which the
105
+ shipped theme already handles.
106
+
107
+ Component CSS lives in the `abaabil.components` cascade layer, so ordinary
108
+ unlayered CSS in your own app beats it without `!important`.
109
+
110
+ ## Browser floor
111
+
112
+ Chrome 99, Firefox 98, Safari 15.4, set by `@layer` and `<dialog>`.
113
+ `abaabil/popover` is the exception: the Popover API needs Chrome 114,
114
+ Firefox 125, Safari 17. Below that the panel renders as an always-visible
115
+ block instead of opening and closing.
116
+
117
+ ## What this library deliberately does not do
118
+
119
+ No focus trap, no layout or spacing utilities, no theming runtime, no
120
+ `headingLevel` on accordion, no controlled combobox or tabs, no date
121
+ picker, no data table, no drag and drop. Each is a
122
+ decision with a reason, documented at https://www.abaabil.com/why.md
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abaabil",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "Minimal, themeable, accessible React components. Zero dependencies.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -31,7 +31,8 @@
31
31
  ],
32
32
  "files": [
33
33
  "dist",
34
- "CHANGELOG.md"
34
+ "CHANGELOG.md",
35
+ "llms.txt"
35
36
  ],
36
37
  "exports": {
37
38
  "./package.json": "./package.json",
@@ -59,6 +60,36 @@
59
60
  "./accordion": "./dist/accordion/index.js",
60
61
  "./accordion/styled": "./dist/accordion/styled.js",
61
62
  "./accordion/a11y": "./dist/accordion/a11y.js",
63
+ "./textarea": "./dist/textarea/index.js",
64
+ "./textarea/styled": "./dist/textarea/styled.js",
65
+ "./textarea/a11y": "./dist/textarea/a11y.js",
66
+ "./switch": "./dist/switch/index.js",
67
+ "./switch/styled": "./dist/switch/styled.js",
68
+ "./switch/a11y": "./dist/switch/a11y.js",
69
+ "./popover": "./dist/popover/index.js",
70
+ "./popover/styled": "./dist/popover/styled.js",
71
+ "./popover/a11y": "./dist/popover/a11y.js",
72
+ "./tabs": "./dist/tabs/index.js",
73
+ "./tabs/styled": "./dist/tabs/styled.js",
74
+ "./tabs/a11y": "./dist/tabs/a11y.js",
75
+ "./alert": "./dist/alert/index.js",
76
+ "./alert/styled": "./dist/alert/styled.js",
77
+ "./alert/a11y": "./dist/alert/a11y.js",
78
+ "./progress": "./dist/progress/index.js",
79
+ "./progress/styled": "./dist/progress/styled.js",
80
+ "./progress/a11y": "./dist/progress/a11y.js",
81
+ "./slider": "./dist/slider/index.js",
82
+ "./slider/styled": "./dist/slider/styled.js",
83
+ "./slider/a11y": "./dist/slider/a11y.js",
84
+ "./breadcrumb": "./dist/breadcrumb/index.js",
85
+ "./breadcrumb/styled": "./dist/breadcrumb/styled.js",
86
+ "./breadcrumb/a11y": "./dist/breadcrumb/a11y.js",
87
+ "./tooltip": "./dist/tooltip/index.js",
88
+ "./tooltip/styled": "./dist/tooltip/styled.js",
89
+ "./tooltip/a11y": "./dist/tooltip/a11y.js",
90
+ "./menu": "./dist/menu/index.js",
91
+ "./menu/styled": "./dist/menu/styled.js",
92
+ "./menu/a11y": "./dist/menu/a11y.js",
62
93
  "./theme.css": "./dist/theme.css",
63
94
  "./styles.css": "./dist/styles.css",
64
95
  "./button.css": "./dist/button/button.css",
@@ -68,10 +99,20 @@
68
99
  "./checkbox.css": "./dist/checkbox/checkbox.css",
69
100
  "./radio.css": "./dist/radio/radio.css",
70
101
  "./select.css": "./dist/select/select.css",
71
- "./accordion.css": "./dist/accordion/accordion.css"
102
+ "./accordion.css": "./dist/accordion/accordion.css",
103
+ "./textarea.css": "./dist/textarea/textarea.css",
104
+ "./switch.css": "./dist/switch/switch.css",
105
+ "./popover.css": "./dist/popover/popover.css",
106
+ "./tabs.css": "./dist/tabs/tabs.css",
107
+ "./alert.css": "./dist/alert/alert.css",
108
+ "./progress.css": "./dist/progress/progress.css",
109
+ "./slider.css": "./dist/slider/slider.css",
110
+ "./breadcrumb.css": "./dist/breadcrumb/breadcrumb.css",
111
+ "./tooltip.css": "./dist/tooltip/tooltip.css",
112
+ "./menu.css": "./dist/menu/menu.css"
72
113
  },
73
114
  "scripts": {
74
- "build": "node scripts/build-css.js && rollup -c && node scripts/check-directives.js && node scripts/measure.js",
115
+ "build": "node scripts/build-css.js && rollup -c && node scripts/check-directives.js && node scripts/check-complete.js && node scripts/measure.js",
75
116
  "test": "vitest run",
76
117
  "test:watch": "vitest",
77
118
  "prepublishOnly": "npm run build && node scripts/check-package.js"