@fictjs/ui-primitives 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +181 -0
  3. package/dist/index.cjs +5091 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +1123 -0
  6. package/dist/index.d.ts +1123 -0
  7. package/dist/index.js +4907 -0
  8. package/dist/index.js.map +1 -0
  9. package/docs/README.md +39 -0
  10. package/docs/accessibility.md +50 -0
  11. package/docs/api-reference.md +200 -0
  12. package/docs/architecture.md +113 -0
  13. package/docs/components/core/accessible-icon.md +23 -0
  14. package/docs/components/core/id.md +26 -0
  15. package/docs/components/core/portal.md +30 -0
  16. package/docs/components/core/presence.md +27 -0
  17. package/docs/components/core/primitive.md +22 -0
  18. package/docs/components/core/separator.md +25 -0
  19. package/docs/components/core/slot.md +25 -0
  20. package/docs/components/core/visually-hidden.md +21 -0
  21. package/docs/components/disclosure/accordion.md +33 -0
  22. package/docs/components/disclosure/collapsible.md +29 -0
  23. package/docs/components/disclosure/navigation-menu.md +43 -0
  24. package/docs/components/disclosure/tabs.md +35 -0
  25. package/docs/components/feedback/toast.md +60 -0
  26. package/docs/components/form/calendar.md +35 -0
  27. package/docs/components/form/controls.md +52 -0
  28. package/docs/components/form/date-picker.md +44 -0
  29. package/docs/components/form/form-field.md +39 -0
  30. package/docs/components/form/inputs.md +99 -0
  31. package/docs/components/interaction/dismissable-layer.md +28 -0
  32. package/docs/components/interaction/focus-scope.md +27 -0
  33. package/docs/components/interaction/live-region.md +26 -0
  34. package/docs/components/interaction/popper.md +30 -0
  35. package/docs/components/interaction/roving-focus.md +27 -0
  36. package/docs/components/interaction/scroll-lock.md +22 -0
  37. package/docs/components/layout/layout.md +61 -0
  38. package/docs/components/menu/context-menu.md +44 -0
  39. package/docs/components/menu/dropdown-menu.md +62 -0
  40. package/docs/components/menu/menubar.md +38 -0
  41. package/docs/components/overlay/alert-dialog.md +46 -0
  42. package/docs/components/overlay/command-palette.md +54 -0
  43. package/docs/components/overlay/dialog.md +69 -0
  44. package/docs/components/overlay/hover-card.md +25 -0
  45. package/docs/components/overlay/popover.md +36 -0
  46. package/docs/components/overlay/tooltip.md +28 -0
  47. package/docs/examples.md +155 -0
  48. package/docs/release.md +60 -0
  49. package/docs/testing.md +36 -0
  50. package/package.json +89 -0
package/docs/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # Documentation Index
2
+
3
+ Start here for all package documentation.
4
+
5
+ ## Getting Started
6
+
7
+ - `README.md`: package overview, install, quick start, and command map
8
+ - `docs/examples.md`: copyable composition snippets
9
+ - `examples/README.md`: executable demo app and screenshot baseline workflow
10
+
11
+ ## Component Documentation
12
+
13
+ - `docs/components/core`
14
+ - `docs/components/interaction`
15
+ - `docs/components/overlay`
16
+ - `docs/components/menu`
17
+ - `docs/components/feedback`
18
+ - `docs/components/disclosure`
19
+ - `docs/components/form`
20
+ - `docs/components/layout`
21
+
22
+ Each component file includes:
23
+
24
+ - minimal example
25
+ - accessibility notes
26
+ - behavior/API summary
27
+
28
+ ## Engineering and Quality Docs
29
+
30
+ - `docs/api-reference.md`: full export index
31
+ - `docs/architecture.md`: implementation design and internal patterns
32
+ - `docs/testing.md`: testing strategy and expectations
33
+ - `docs/accessibility.md`: accessibility verification checklist
34
+ - `docs/release.md`: release and publish checklist
35
+ - `CONTRIBUTING.md`: contributor workflow and PR quality expectations
36
+
37
+ ## Source Planning
38
+
39
+ - `ui-primitives.md`: source planning/scope document used during implementation
@@ -0,0 +1,50 @@
1
+ # Accessibility Checklist
2
+
3
+ Use this checklist when building or reviewing primitives.
4
+
5
+ ## Global checks
6
+
7
+ 1. Every interactive element is keyboard reachable (`Tab`, `Shift+Tab`, `Enter`, `Space`, arrow keys where applicable).
8
+ 2. Focus is always visible and never trapped unless intentionally scoped (`FocusScope`, modal overlays).
9
+ 3. Interactive controls expose semantic roles and states (`role`, `aria-*`, `data-state`).
10
+ 4. Dismiss flows are complete: Escape, outside click (when expected), and explicit close action.
11
+ 5. Dynamic regions (toast/live messages) use polite or assertive live-region semantics.
12
+ 6. Hidden helper text is exposed only to assistive tech (`VisuallyHidden`, `AccessibleIcon` labels).
13
+
14
+ ## Overlay and popups
15
+
16
+ - `Dialog` / `AlertDialog`: trigger uses `aria-haspopup`, content has dialog role, title/description IDs are wired, focus enters content and restores on close.
17
+ - `Popover` / `HoverCard` / `Tooltip`: trigger/content relationships are explicit (`aria-describedby` for tooltip), and hover/focus behavior has deterministic open/close.
18
+ - Modal overlays lock background scroll and block outside interaction where expected.
19
+
20
+ ## Menus and navigation
21
+
22
+ - Menu roots render proper container roles (`menu`, `menubar`) and items expose matching item roles.
23
+ - Checkbox/radio menu items expose checked state (`aria-checked`) and maintain controlled/uncontrolled behavior.
24
+ - Roving focus handles arrow-key traversal and loop behavior consistently.
25
+ - Navigation content open state is exposed through `data-state` and trigger state attributes.
26
+
27
+ ## Tabs and disclosure
28
+
29
+ - Tabs: triggers use `role="tab"` + `aria-controls`, content uses `role="tabpanel"` + `aria-labelledby`.
30
+ - Accordion/collapsible triggers expose expanded state and toggle content visibility predictably.
31
+ - `forceMount` variants keep semantic state in sync while hidden (`data-state="closed"` / inactive).
32
+
33
+ ## Form primitives
34
+
35
+ - Labels are explicitly associated with controls (`for` / `id`).
36
+ - Descriptions and messages are connected through `aria-describedby`.
37
+ - Error messages use alert semantics when they need immediate announcement.
38
+ - Select/combobox/listbox controls provide role/state attributes for active and selected options.
39
+
40
+ ## Feedback primitives
41
+
42
+ - Toast viewport has live region semantics and close/action controls have explicit labels.
43
+ - Auto-dismiss timing can be overridden and manual dismissal is always possible.
44
+
45
+ ## Verification workflow
46
+
47
+ 1. Run `pnpm test` to verify behavioral and semantic assertions.
48
+ 2. Run through keyboard-only flows for each updated primitive.
49
+ 3. Validate screen-reader announcements for dialogs, form errors, and toasts.
50
+ 4. Use the runnable demo (`pnpm examples:dev`) for manual interaction walkthroughs.
@@ -0,0 +1,200 @@
1
+ # API Reference Index
2
+
3
+ This document lists runtime exports from `@fictjs/ui-primitives`.
4
+
5
+ Note: each module also exports related TypeScript interfaces/types for props.
6
+
7
+ ## Core
8
+
9
+ - `Primitive`
10
+ - `PrimitiveElements`
11
+ - `Slot`
12
+ - `Presence`
13
+ - `Portal`
14
+ - `PortalHost`
15
+ - `VisuallyHidden`
16
+ - `Separator`
17
+ - `AccessibleIcon`
18
+ - `IdProvider`
19
+ - `useId`
20
+
21
+ ## Interaction
22
+
23
+ - `FocusScope`
24
+ - `FocusTrap`
25
+ - `DismissableLayer`
26
+ - `RovingFocusGroup`
27
+ - `RovingFocusItem`
28
+ - `ScrollLock`
29
+ - `LiveRegionProvider`
30
+ - `useAnnouncer`
31
+ - `Announce`
32
+ - `PopperRoot`
33
+ - `PopperAnchor`
34
+ - `PopperContent`
35
+ - `PopperArrow`
36
+
37
+ ## Overlay
38
+
39
+ - `DialogRoot`
40
+ - `DialogPortal`
41
+ - `DialogTrigger`
42
+ - `DialogOverlay`
43
+ - `DialogContent`
44
+ - `DialogClose`
45
+ - `DialogTitle`
46
+ - `DialogDescription`
47
+ - `AlertDialogRoot`
48
+ - `AlertDialogTrigger`
49
+ - `AlertDialogPortal`
50
+ - `AlertDialogOverlay`
51
+ - `AlertDialogContent`
52
+ - `AlertDialogTitle`
53
+ - `AlertDialogDescription`
54
+ - `AlertDialogAction`
55
+ - `AlertDialogCancel`
56
+ - `PopoverRoot`
57
+ - `PopoverTrigger`
58
+ - `PopoverContent`
59
+ - `PopoverClose`
60
+ - `TooltipProvider`
61
+ - `TooltipRoot`
62
+ - `TooltipTrigger`
63
+ - `TooltipContent`
64
+ - `HoverCardRoot`
65
+ - `HoverCardTrigger`
66
+ - `HoverCardContent`
67
+ - `CommandPaletteRoot`
68
+ - `CommandPaletteTrigger`
69
+ - `CommandPaletteContent`
70
+ - `CommandPaletteInput`
71
+ - `CommandPaletteList`
72
+ - `CommandPaletteItem`
73
+ - `CommandPaletteEmpty`
74
+ - `CommandPaletteGroup`
75
+ - `CommandPaletteSeparator`
76
+ - `CommandPaletteClose`
77
+
78
+ ## Menu
79
+
80
+ - `DropdownMenuRoot`
81
+ - `DropdownMenuTrigger`
82
+ - `DropdownMenuContent`
83
+ - `DropdownMenuItem`
84
+ - `DropdownMenuCheckboxItem`
85
+ - `DropdownMenuRadioGroup`
86
+ - `DropdownMenuRadioItem`
87
+ - `DropdownMenuSub`
88
+ - `DropdownMenuSubTrigger`
89
+ - `DropdownMenuSubContent`
90
+ - `DropdownMenuLabel`
91
+ - `DropdownMenuSeparator`
92
+ - `ContextMenuRoot`
93
+ - `ContextMenuTrigger`
94
+ - `ContextMenuContent`
95
+ - `ContextMenuItem`
96
+ - `ContextMenuSub`
97
+ - `ContextMenuSubTrigger`
98
+ - `ContextMenuSubContent`
99
+ - `MenubarRoot`
100
+ - `MenubarMenu`
101
+ - `MenubarTrigger`
102
+ - `MenubarContent`
103
+ - `MenubarItem`
104
+
105
+ ## Feedback
106
+
107
+ - `ToastProvider`
108
+ - `ToastViewport`
109
+ - `ToastRoot`
110
+ - `ToastTitle`
111
+ - `ToastDescription`
112
+ - `ToastAction`
113
+ - `ToastClose`
114
+ - `useToast`
115
+
116
+ ## Disclosure and Navigation
117
+
118
+ - `CollapsibleRoot`
119
+ - `CollapsibleTrigger`
120
+ - `CollapsibleContent`
121
+ - `AccordionRoot`
122
+ - `AccordionItem`
123
+ - `AccordionTrigger`
124
+ - `AccordionContent`
125
+ - `TabsRoot`
126
+ - `TabsList`
127
+ - `TabsTrigger`
128
+ - `TabsContent`
129
+ - `NavigationMenuRoot`
130
+ - `NavigationMenuList`
131
+ - `NavigationMenuItem`
132
+ - `NavigationMenuTrigger`
133
+ - `NavigationMenuContent`
134
+ - `NavigationMenuLink`
135
+ - `NavigationMenuIndicator`
136
+ - `NavigationMenuViewport`
137
+
138
+ ## Form and Inputs
139
+
140
+ - `Label`
141
+ - `Checkbox`
142
+ - `RadioGroup`
143
+ - `RadioItem`
144
+ - `Switch`
145
+ - `SwitchThumb`
146
+ - `Toggle`
147
+ - `ToggleGroup`
148
+ - `ToggleGroupItem`
149
+ - `Slider`
150
+ - `RangeSlider`
151
+ - `CalendarRoot`
152
+ - `Calendar`
153
+ - `CalendarHeader`
154
+ - `CalendarTitle`
155
+ - `CalendarPrevButton`
156
+ - `CalendarNextButton`
157
+ - `CalendarGrid`
158
+ - `DatePickerRoot`
159
+ - `DatePickerTrigger`
160
+ - `DatePickerValue`
161
+ - `DatePickerContent`
162
+ - `DatePickerCalendar`
163
+ - `SelectRoot`
164
+ - `SelectTrigger`
165
+ - `SelectValue`
166
+ - `SelectContent`
167
+ - `SelectItem`
168
+ - `ComboboxRoot`
169
+ - `ComboboxInput`
170
+ - `ComboboxList`
171
+ - `ComboboxItem`
172
+ - `Form`
173
+ - `FormField`
174
+ - `FormLabel`
175
+ - `FormControl`
176
+ - `FormDescription`
177
+ - `FormMessage`
178
+
179
+ ## Layout
180
+
181
+ - `ScrollArea`
182
+ - `ScrollAreaViewport`
183
+ - `ScrollAreaScrollbar`
184
+ - `ScrollAreaThumb`
185
+ - `ResizablePanelGroup`
186
+ - `ResizablePanel`
187
+ - `ResizableHandle`
188
+ - `AspectRatio`
189
+ - `Progress`
190
+ - `Meter`
191
+ - `Skeleton`
192
+ - `KeyboardModeProvider`
193
+ - `FocusVisible`
194
+ - `useKeyboardMode`
195
+
196
+ ## Import Pattern
197
+
198
+ ```ts
199
+ import { DialogRoot, TabsRoot, ToastProvider } from '@fictjs/ui-primitives'
200
+ ```
@@ -0,0 +1,113 @@
1
+ # Architecture Notes
2
+
3
+ This document describes the design and implementation structure of `@fictjs/ui-primitives`.
4
+
5
+ ## Goals
6
+
7
+ - Provide headless primitives with minimal assumptions about styling
8
+ - Keep accessibility and interaction semantics explicit and testable
9
+ - Support controlled and uncontrolled patterns consistently
10
+ - Compose small primitives into larger compound components
11
+
12
+ ## Repository Structure
13
+
14
+ - `src/components/core`: foundational composition primitives
15
+ - `src/components/interaction`: focus, dismissal, positioning, live regions
16
+ - `src/components/overlay`: dialog/popover/tooltip/hover-card/command-palette
17
+ - `src/components/menu`: dropdown/context/menubar patterns
18
+ - `src/components/disclosure`: tabs/accordion/collapsible/navigation menu
19
+ - `src/components/form`: form controls, calendar/date-picker, and structured field helpers
20
+ - `src/components/layout`: layout and visual utility primitives
21
+ - `src/internal`: shared internals used across components
22
+
23
+ ## Core Implementation Patterns
24
+
25
+ ### Runtime and hooks foundation
26
+
27
+ - Components are built on top of `@fictjs/runtime` signals/effects/context.
28
+ - Cross-cutting event and timing behavior is standardized with `@fictjs/hooks` (for example `useEventListener` and `useDebounceFn`).
29
+ - This keeps lifecycle cleanup centralized and reduces duplicated low-level DOM/timer logic.
30
+
31
+ ### Compound component + context
32
+
33
+ Many features are implemented as root + parts using context.
34
+
35
+ Examples:
36
+
37
+ - `DialogRoot` + `DialogTrigger` + `DialogContent`
38
+ - `TabsRoot` + `TabsList` + `TabsTrigger` + `TabsContent`
39
+ - `SelectRoot` + `SelectTrigger` + `SelectContent` + `SelectItem`
40
+
41
+ ### Controlled and uncontrolled state
42
+
43
+ Shared helper:
44
+
45
+ - `src/internal/state.ts` -> `createControllableState`
46
+
47
+ This ensures consistent behavior for `value/defaultValue/onChange` style APIs.
48
+
49
+ ### Accessor-safe reads
50
+
51
+ Shared helper:
52
+
53
+ - `src/internal/accessor.ts` -> `read`
54
+
55
+ This unifies plain values and accessor functions (`T | () => T`) across props.
56
+
57
+ ### Id and ref composition
58
+
59
+ Shared helpers:
60
+
61
+ - `src/internal/ids.ts` -> `createId`, `useId`, and `IdProvider` for deterministic aria wiring
62
+ - `src/internal/ref.ts` -> composed refs across parent and child consumers
63
+
64
+ Most root primitives accept explicit `id` props so teams can force stable SSR/hydration wiring.
65
+
66
+ ## Interaction Stack Design
67
+
68
+ ### Focus management
69
+
70
+ - `FocusScope` handles auto-focus, trap/loop behavior, and restore-focus semantics.
71
+ - Modal overlays use focus scoping as part of their lifecycle.
72
+
73
+ ### Dismiss behavior
74
+
75
+ - `DismissableLayer` centralizes Escape, outside pointer, and outside focus dismissal.
76
+ - Only the top-most active layer can dismiss to avoid nested-layer conflicts.
77
+ - Outside interactions are interceptable (`onEscapeKeyDown`, `onPointerDownOutside`, `onFocusOutside`, `onInteractOutside`).
78
+
79
+ ### Positioning
80
+
81
+ - `PopperRoot`, `PopperAnchor`, and `PopperContent` provide anchor-based placement.
82
+ - Overlay and menu primitives compose on top of Popper where needed.
83
+
84
+ ### Portal strategy
85
+
86
+ - `Portal` and `PortalHost` support default body portaling or scoped containers.
87
+ - Overlay components default to portal rendering but can opt into inline rendering.
88
+
89
+ ## Accessibility Strategy
90
+
91
+ - Prefer native semantics first, then add ARIA for composite widgets.
92
+ - Maintain explicit state attributes (`aria-*`, `data-state`) for testability.
93
+ - Include keyboard/pointer parity for open/close and navigation behavior.
94
+ - Use live regions for async feedback (`Toast`, `LiveRegionProvider`).
95
+ - Ensure `asChild` composition keeps semantics explicit at call sites.
96
+
97
+ See `docs/accessibility.md` for review checklist details.
98
+
99
+ ## Testing Strategy
100
+
101
+ - Behavior-level tests live in `test/` (Vitest + JSDOM).
102
+ - Focus on state transitions, keyboard/pointer interactions, and semantic attributes.
103
+ - Avoid snapshot-only confidence; assert behavior and cleanup paths directly.
104
+
105
+ See `docs/testing.md` for contributor expectations.
106
+
107
+ ## Demo and Baseline Screenshots
108
+
109
+ - Executable demo app lives in `examples/`.
110
+ - Automated baseline capture script lives in `scripts/capture-examples.mjs`.
111
+ - Baselines are stored in `examples/screenshots/baseline`.
112
+
113
+ This supports visual sanity checks alongside behavioral tests.
@@ -0,0 +1,23 @@
1
+ # AccessibleIcon
2
+
3
+ Wraps an icon with a hidden text label.
4
+
5
+ ## Props
6
+
7
+ - `label`: required accessible text
8
+ - `children`: icon node
9
+
10
+ ## Minimal Example
11
+
12
+ ```tsx
13
+ import { AccessibleIcon } from '@fictjs/ui-primitives'
14
+
15
+ <AccessibleIcon label="Close dialog">
16
+ <svg width="16" height="16" aria-hidden="true"><path d="M2 2 L14 14 M14 2 L2 14" /></svg>
17
+ </AccessibleIcon>
18
+ ```
19
+
20
+ ## Accessibility Notes
21
+
22
+ - `label` is required and should describe the icon action, not visual shape.
23
+ - Keep the nested icon decorative (`aria-hidden="true"`) so only one accessible name is announced.
@@ -0,0 +1,26 @@
1
+ # Id Utilities
2
+
3
+ `useId` and `IdProvider` provide deterministic id generation for aria wiring and SSR-safe composition.
4
+
5
+ ## API
6
+
7
+ - `useId(id?: string, prefix?: string): string`
8
+ - `IdProvider` with `prefix?: string`
9
+
10
+ ## Minimal Example
11
+
12
+ ```tsx
13
+ import { IdProvider, useId } from '@fictjs/ui-primitives'
14
+
15
+ function Field() {
16
+ const id = useId(undefined, 'profile-field')
17
+ return <input id={id} aria-describedby={`${id}-hint`} />
18
+ }
19
+
20
+ <IdProvider prefix="settings">{<Field />}</IdProvider>
21
+ ```
22
+
23
+ ## Notes
24
+
25
+ - Prefer explicit `id` on root primitives when you need hard guarantees for SSR/hydration parity.
26
+ - `IdProvider` helps keep generated ids deterministic within a subtree.
@@ -0,0 +1,30 @@
1
+ # Portal / PortalHost
2
+
3
+ - `Portal` renders subtree into a target container (default `document.body`)
4
+ - `PortalHost` sets a container context for nested portals
5
+
6
+ ## Props
7
+
8
+ ### Portal
9
+
10
+ - `container`: HTMLElement or accessor
11
+ - `disabled`: render inline when true
12
+
13
+ ### PortalHost
14
+
15
+ - `container`: HTMLElement or accessor
16
+
17
+ ## Minimal Example
18
+
19
+ ```tsx
20
+ import { Portal } from '@fictjs/ui-primitives'
21
+
22
+ <Portal>
23
+ <div role="dialog" aria-modal="true">Portaled content</div>
24
+ </Portal>
25
+ ```
26
+
27
+ ## Accessibility Notes
28
+
29
+ - Portaling changes DOM position, not semantics; always provide explicit role/aria on the content.
30
+ - Ensure focus entry/restore and dismiss behavior are handled by surrounding primitives (e.g. `Dialog`).
@@ -0,0 +1,27 @@
1
+ # Presence
2
+
3
+ `Presence` conditionally mounts children based on `present`, while supporting force mount.
4
+
5
+ ## Props
6
+
7
+ - `present`: boolean/accessor, default `true`
8
+ - `forceMount`: boolean/accessor, default `false`
9
+ - `children`: node or render function receiving `{ present }`
10
+
11
+ ## Minimal Example
12
+
13
+ ```tsx
14
+ import { createSignal } from '@fictjs/runtime/advanced'
15
+ import { Presence } from '@fictjs/ui-primitives'
16
+
17
+ const open = createSignal(false)
18
+
19
+ <Presence present={() => open()}>
20
+ <div data-state={() => (open() ? 'open' : 'closed')}>Conditional panel</div>
21
+ </Presence>
22
+ ```
23
+
24
+ ## Accessibility Notes
25
+
26
+ - When `present` is false, content is unmounted and removed from the accessibility tree.
27
+ - If using `forceMount`, also expose hidden/closed state (`data-state`, `aria-hidden`, etc.) intentionally.
@@ -0,0 +1,22 @@
1
+ # Primitive
2
+
3
+ `Primitive` is the polymorphic base element wrapper for all headless components.
4
+
5
+ ## API
6
+
7
+ - `as`: intrinsic element tag, default `div`
8
+ - `asChild`: when `true`, merges props into the child vnode via `Slot`
9
+
10
+ ## Minimal Example
11
+
12
+ ```tsx
13
+ <Primitive as="button" type="button">Open</Primitive>
14
+ <Primitive asChild>
15
+ <a href="/docs">Docs</a>
16
+ </Primitive>
17
+ ```
18
+
19
+ ## Accessibility Notes
20
+
21
+ - `as` and `asChild` can change semantics; choose an element with correct native role/keyboard behavior.
22
+ - Avoid replacing native buttons/links with generic tags unless you fully re-implement accessibility semantics.
@@ -0,0 +1,25 @@
1
+ # Separator
2
+
3
+ Semantic separator primitive.
4
+
5
+ ## Props
6
+
7
+ - `orientation`: `horizontal | vertical`
8
+ - `decorative`: when true, uses presentation semantics
9
+
10
+ ## Minimal Example
11
+
12
+ ```tsx
13
+ import { Separator } from '@fictjs/ui-primitives'
14
+
15
+ <div>
16
+ <span>Section A</span>
17
+ <Separator orientation="horizontal" />
18
+ <span>Section B</span>
19
+ </div>
20
+ ```
21
+
22
+ ## Accessibility Notes
23
+
24
+ - Use non-decorative separators for meaningful structure so assistive tech can announce boundaries.
25
+ - Set `decorative` for purely visual dividers to avoid extra noise for screen readers.
@@ -0,0 +1,25 @@
1
+ # Slot
2
+
3
+ `Slot` provides `asChild` behavior by cloning a single child vnode and merging props.
4
+
5
+ Merged behavior:
6
+
7
+ - event handlers are composed (`child` then `slot`)
8
+ - `class` / `className` values are concatenated
9
+ - `style` objects are shallow-merged
10
+ - refs are composed
11
+
12
+ ## Minimal Example
13
+
14
+ ```tsx
15
+ import { Slot } from '@fictjs/ui-primitives'
16
+
17
+ <Slot class="button-like" onClick={() => {}}>
18
+ <a href="/docs">Open docs</a>
19
+ </Slot>
20
+ ```
21
+
22
+ ## Accessibility Notes
23
+
24
+ - The child keeps its native semantics; verify merged props do not conflict with existing keyboard behavior.
25
+ - Keep a single interactive target inside `Slot` to avoid nested-focusable anti-patterns.
@@ -0,0 +1,21 @@
1
+ # VisuallyHidden
2
+
3
+ Renders accessible text for assistive tech while keeping it visually hidden.
4
+
5
+ Supports `as` to change element tag.
6
+
7
+ ## Minimal Example
8
+
9
+ ```tsx
10
+ import { VisuallyHidden } from '@fictjs/ui-primitives'
11
+
12
+ <button type="button">
13
+ <svg aria-hidden="true" />
14
+ <VisuallyHidden>Search</VisuallyHidden>
15
+ </button>
16
+ ```
17
+
18
+ ## Accessibility Notes
19
+
20
+ - Use for supplemental text, not large hidden content blocks.
21
+ - Pair with visible affordances so sighted keyboard users still understand the control.
@@ -0,0 +1,33 @@
1
+ # Accordion
2
+
3
+ Accordion primitives built on collapsible behavior.
4
+
5
+ ## Components
6
+
7
+ - `AccordionRoot`
8
+ - `AccordionItem`
9
+ - `AccordionTrigger`
10
+ - `AccordionContent`
11
+
12
+ ## Minimal Example
13
+
14
+ ```tsx
15
+ import {
16
+ AccordionRoot,
17
+ AccordionItem,
18
+ AccordionTrigger,
19
+ AccordionContent,
20
+ } from '@fictjs/ui-primitives'
21
+
22
+ <AccordionRoot type="single" defaultValue="account" collapsible>
23
+ <AccordionItem value="account">
24
+ <AccordionTrigger>Account</AccordionTrigger>
25
+ <AccordionContent>Account details</AccordionContent>
26
+ </AccordionItem>
27
+ </AccordionRoot>
28
+ ```
29
+
30
+ ## Accessibility Notes
31
+
32
+ - `AccordionTrigger` should remain a button-like control so `aria-expanded` semantics stay valid.
33
+ - Preserve heading hierarchy around triggers/content when embedding in structured pages.
@@ -0,0 +1,29 @@
1
+ # Collapsible
2
+
3
+ Expandable/collapsible section primitives.
4
+
5
+ ## Components
6
+
7
+ - `CollapsibleRoot`
8
+ - `CollapsibleTrigger`
9
+ - `CollapsibleContent`
10
+
11
+ ## Minimal Example
12
+
13
+ ```tsx
14
+ import {
15
+ CollapsibleRoot,
16
+ CollapsibleTrigger,
17
+ CollapsibleContent,
18
+ } from '@fictjs/ui-primitives'
19
+
20
+ <CollapsibleRoot defaultOpen>
21
+ <CollapsibleTrigger>Toggle section</CollapsibleTrigger>
22
+ <CollapsibleContent>Collapsible body</CollapsibleContent>
23
+ </CollapsibleRoot>
24
+ ```
25
+
26
+ ## Accessibility Notes
27
+
28
+ - Trigger state should be exposed via `aria-expanded` (provided by primitive).
29
+ - When using `forceMount`, keep hidden-state signaling consistent (`data-state`, optional `aria-hidden`).