opui-css 5.4.1 → 5.4.3

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 (328) hide show
  1. package/CHANGELOG.md +373 -361
  2. package/LICENSE +21 -21
  3. package/MIGRATING.md +138 -138
  4. package/README.md +87 -87
  5. package/astro/index.ts +46 -46
  6. package/components/AGENTS.md +247 -247
  7. package/components/Accordion/Accordion.astro +48 -48
  8. package/components/Accordion/Accordion.vue +38 -38
  9. package/components/Accordion/types.astro.ts +4 -4
  10. package/components/Accordion/types.d.vue.ts +15 -15
  11. package/components/Accordion/types.solid.ts +6 -6
  12. package/components/Accordion/types.svelte.ts +7 -7
  13. package/components/Accordion/types.ts +10 -10
  14. package/components/Anchor/Anchor.astro +49 -49
  15. package/components/Anchor/Anchor.vue +41 -41
  16. package/components/Anchor/types.astro.ts +4 -4
  17. package/components/Anchor/types.d.vue.ts +10 -10
  18. package/components/Anchor/types.solid.ts +6 -6
  19. package/components/Anchor/types.svelte.ts +7 -7
  20. package/components/Anchor/types.ts +9 -9
  21. package/components/Avatar/Avatar.astro +29 -29
  22. package/components/Avatar/Avatar.vue +24 -24
  23. package/components/Avatar/types.astro.ts +10 -10
  24. package/components/Avatar/types.d.vue.ts +12 -12
  25. package/components/Avatar/types.solid.ts +11 -11
  26. package/components/Avatar/types.svelte.ts +12 -12
  27. package/components/Avatar/types.ts +29 -29
  28. package/components/Badge/Badge.astro +51 -51
  29. package/components/Badge/Badge.vue +40 -39
  30. package/components/Badge/types.astro.ts +4 -4
  31. package/components/Badge/types.d.vue.ts +10 -10
  32. package/components/Badge/types.solid.ts +4 -4
  33. package/components/Badge/types.svelte.ts +4 -4
  34. package/components/Badge/types.ts +12 -8
  35. package/components/Button/Button.astro +36 -36
  36. package/components/Button/Button.vue +27 -27
  37. package/components/Button/types.astro.ts +6 -6
  38. package/components/Button/types.d.vue.ts +10 -10
  39. package/components/Button/types.solid.ts +8 -8
  40. package/components/Button/types.svelte.ts +6 -6
  41. package/components/Button/types.ts +16 -16
  42. package/components/ButtonGroup/ButtonGroup.astro +29 -29
  43. package/components/ButtonGroup/ButtonGroup.vue +22 -22
  44. package/components/ButtonGroup/types.astro.ts +4 -4
  45. package/components/ButtonGroup/types.d.vue.ts +10 -10
  46. package/components/ButtonGroup/types.solid.ts +4 -4
  47. package/components/ButtonGroup/types.svelte.ts +4 -4
  48. package/components/ButtonGroup/types.ts +7 -7
  49. package/components/Callout/Callout.astro +76 -76
  50. package/components/Callout/Callout.vue +63 -63
  51. package/components/Callout/types.astro.ts +4 -4
  52. package/components/Callout/types.d.vue.ts +10 -10
  53. package/components/Callout/types.solid.ts +6 -6
  54. package/components/Callout/types.svelte.ts +7 -7
  55. package/components/Callout/types.ts +10 -10
  56. package/components/Card/Card.astro +34 -34
  57. package/components/Card/Card.vue +28 -28
  58. package/components/Card/types.astro.ts +4 -4
  59. package/components/Card/types.d.vue.ts +10 -10
  60. package/components/Card/types.solid.ts +6 -6
  61. package/components/Card/types.svelte.ts +7 -7
  62. package/components/Card/types.ts +11 -11
  63. package/components/Checkbox/Checkbox.astro +46 -46
  64. package/components/Checkbox/Checkbox.vue +43 -43
  65. package/components/Checkbox/CheckboxInput.astro +16 -16
  66. package/components/Checkbox/CheckboxInput.vue +32 -32
  67. package/components/Checkbox/types.astro.ts +8 -8
  68. package/components/Checkbox/types.d.vue.ts +11 -11
  69. package/components/Checkbox/types.solid.ts +9 -9
  70. package/components/Checkbox/types.svelte.ts +12 -12
  71. package/components/Checkbox/types.ts +16 -16
  72. package/components/Chip/Chip.astro +31 -31
  73. package/components/Chip/Chip.vue +37 -37
  74. package/components/Chip/types.astro.ts +9 -9
  75. package/components/Chip/types.d.vue.ts +10 -10
  76. package/components/Chip/types.solid.ts +10 -10
  77. package/components/Chip/types.svelte.ts +14 -14
  78. package/components/Chip/types.ts +13 -13
  79. package/components/ClassicSelect/ClassicSelect.astro +60 -60
  80. package/components/ClassicSelect/ClassicSelect.vue +54 -54
  81. package/components/ClassicSelect/types.d.vue.ts +10 -10
  82. package/components/ClassicSelect/types.ts +15 -15
  83. package/components/DescriptionList/Description.astro +9 -9
  84. package/components/DescriptionList/Description.vue +12 -12
  85. package/components/DescriptionList/DescriptionList.astro +18 -18
  86. package/components/DescriptionList/DescriptionList.vue +23 -23
  87. package/components/DescriptionList/Item.astro +9 -9
  88. package/components/DescriptionList/Item.vue +12 -12
  89. package/components/DescriptionList/Term.astro +9 -9
  90. package/components/DescriptionList/Term.vue +12 -12
  91. package/components/DescriptionList/index.ts +12 -12
  92. package/components/DescriptionList/types.astro.ts +10 -10
  93. package/components/DescriptionList/types.d.vue.ts +10 -10
  94. package/components/DescriptionList/types.solid.ts +4 -4
  95. package/components/DescriptionList/types.svelte.ts +4 -4
  96. package/components/DescriptionList/types.ts +3 -3
  97. package/components/Dialog/Dialog.astro +38 -38
  98. package/components/Dialog/Dialog.vue +35 -35
  99. package/components/Dialog/types.astro.ts +4 -4
  100. package/components/Dialog/types.d.vue.ts +10 -10
  101. package/components/Dialog/types.solid.ts +8 -8
  102. package/components/Dialog/types.svelte.ts +7 -7
  103. package/components/Dialog/types.ts +10 -10
  104. package/components/Divider/Divider.astro +12 -12
  105. package/components/Divider/Divider.vue +12 -12
  106. package/components/Divider/types.astro.ts +4 -4
  107. package/components/Divider/types.d.vue.ts +6 -6
  108. package/components/Divider/types.solid.ts +4 -4
  109. package/components/Divider/types.svelte.ts +4 -4
  110. package/components/Divider/types.ts +3 -3
  111. package/components/Drawer/Drawer.astro +47 -47
  112. package/components/Drawer/Drawer.vue +46 -46
  113. package/components/Drawer/DrawerFooter.astro +11 -11
  114. package/components/Drawer/DrawerFooter.vue +16 -16
  115. package/components/Drawer/DrawerHeader.astro +27 -27
  116. package/components/Drawer/DrawerHeader.vue +36 -36
  117. package/components/Drawer/types.astro.ts +8 -8
  118. package/components/Drawer/types.d.vue.ts +31 -31
  119. package/components/Drawer/types.solid.ts +12 -12
  120. package/components/Drawer/types.svelte.ts +15 -15
  121. package/components/Drawer/types.ts +20 -20
  122. package/components/FieldDescription/FieldDescription.astro +9 -9
  123. package/components/FieldDescription/FieldDescription.vue +12 -12
  124. package/components/FieldDescription/types.astro.ts +4 -4
  125. package/components/FieldDescription/types.d.vue.ts +10 -10
  126. package/components/FieldDescription/types.solid.ts +5 -5
  127. package/components/FieldDescription/types.svelte.ts +4 -4
  128. package/components/FieldDescription/types.ts +1 -1
  129. package/components/FieldGroup/FieldGroup.astro +24 -24
  130. package/components/FieldGroup/FieldGroup.vue +25 -25
  131. package/components/FieldGroup/types.astro.ts +4 -4
  132. package/components/FieldGroup/types.d.vue.ts +12 -12
  133. package/components/FieldGroup/types.solid.ts +5 -5
  134. package/components/FieldGroup/types.svelte.ts +4 -4
  135. package/components/FieldGroup/types.ts +4 -4
  136. package/components/FieldLegend/FieldLegend.astro +11 -11
  137. package/components/FieldLegend/FieldLegend.vue +18 -18
  138. package/components/FieldLegend/types.astro.ts +6 -6
  139. package/components/FieldLegend/types.d.vue.ts +11 -11
  140. package/components/FieldLegend/types.solid.ts +5 -5
  141. package/components/FieldLegend/types.svelte.ts +6 -6
  142. package/components/FieldLegend/types.ts +1 -1
  143. package/components/FieldSet/FieldSet.astro +11 -11
  144. package/components/FieldSet/FieldSet.vue +18 -18
  145. package/components/FieldSet/types.astro.ts +6 -6
  146. package/components/FieldSet/types.d.vue.ts +11 -11
  147. package/components/FieldSet/types.solid.ts +5 -5
  148. package/components/FieldSet/types.svelte.ts +8 -8
  149. package/components/FieldSet/types.ts +5 -5
  150. package/components/Form/Form.astro +10 -10
  151. package/components/Form/Form.vue +14 -14
  152. package/components/Form/types.astro.ts +6 -6
  153. package/components/Form/types.d.vue.ts +11 -11
  154. package/components/Form/types.solid.ts +5 -5
  155. package/components/Form/types.svelte.ts +7 -7
  156. package/components/Form/types.ts +1 -1
  157. package/components/IconButton/IconButton.astro +24 -24
  158. package/components/IconButton/IconButton.vue +24 -24
  159. package/components/IconButton/types.astro.ts +6 -6
  160. package/components/IconButton/types.d.vue.ts +10 -10
  161. package/components/IconButton/types.solid.ts +8 -8
  162. package/components/IconButton/types.svelte.ts +6 -6
  163. package/components/IconButton/types.ts +16 -16
  164. package/components/List/List.astro +29 -29
  165. package/components/List/List.vue +28 -28
  166. package/components/List/types.astro.ts +4 -4
  167. package/components/List/types.d.vue.ts +10 -10
  168. package/components/List/types.solid.ts +4 -4
  169. package/components/List/types.svelte.ts +4 -4
  170. package/components/List/types.ts +7 -7
  171. package/components/ListItem/ListItem.astro +108 -108
  172. package/components/ListItem/ListItem.vue +98 -98
  173. package/components/ListItem/types.astro.ts +10 -10
  174. package/components/ListItem/types.d.vue.ts +10 -10
  175. package/components/ListItem/types.solid.ts +12 -12
  176. package/components/ListItem/types.svelte.ts +14 -14
  177. package/components/ListItem/types.ts +32 -32
  178. package/components/Progress/Progress.astro +31 -31
  179. package/components/Progress/Progress.vue +34 -34
  180. package/components/Progress/types.astro.ts +4 -4
  181. package/components/Progress/types.d.vue.ts +10 -10
  182. package/components/Progress/types.solid.ts +4 -4
  183. package/components/Progress/types.svelte.ts +4 -4
  184. package/components/Progress/types.ts +10 -10
  185. package/components/Radio/Radio.astro +44 -44
  186. package/components/Radio/Radio.vue +41 -41
  187. package/components/Radio/RadioInput.astro +7 -7
  188. package/components/Radio/RadioInput.vue +14 -14
  189. package/components/Radio/types.astro.ts +6 -6
  190. package/components/Radio/types.d.vue.ts +11 -11
  191. package/components/Radio/types.solid.ts +9 -9
  192. package/components/Radio/types.svelte.ts +12 -12
  193. package/components/Radio/types.ts +12 -12
  194. package/components/Range/Range.astro +128 -128
  195. package/components/Range/Range.vue +85 -85
  196. package/components/Range/types.ts +25 -25
  197. package/components/Select/Select.astro +108 -108
  198. package/components/Select/Select.vue +85 -85
  199. package/components/Switch/Switch.astro +76 -76
  200. package/components/Switch/Switch.vue +63 -63
  201. package/components/Switch/SwitchInput.astro +7 -7
  202. package/components/Switch/SwitchInput.vue +14 -14
  203. package/components/Switch/types.astro.ts +5 -5
  204. package/components/Switch/types.d.vue.ts +13 -13
  205. package/components/Switch/types.solid.ts +9 -9
  206. package/components/Switch/types.svelte.ts +9 -9
  207. package/components/Switch/types.ts +16 -16
  208. package/components/Table/Body.astro +11 -11
  209. package/components/Table/Body.vue +5 -5
  210. package/components/Table/Cell.astro +11 -11
  211. package/components/Table/Cell.vue +5 -5
  212. package/components/Table/Column.astro +12 -12
  213. package/components/Table/Column.vue +19 -19
  214. package/components/Table/ColumnGroup.astro +11 -11
  215. package/components/Table/ColumnGroup.vue +5 -5
  216. package/components/Table/Head.astro +11 -11
  217. package/components/Table/Head.vue +5 -5
  218. package/components/Table/HeaderCell.astro +11 -11
  219. package/components/Table/HeaderCell.vue +5 -5
  220. package/components/Table/Row.astro +11 -11
  221. package/components/Table/Row.vue +5 -5
  222. package/components/Table/Table.astro +14 -14
  223. package/components/Table/Table.vue +19 -19
  224. package/components/Table/index.ts +20 -20
  225. package/components/Table/types.astro.ts +16 -16
  226. package/components/Table/types.d.vue.ts +10 -10
  227. package/components/Table/types.solid.ts +8 -8
  228. package/components/Table/types.svelte.ts +11 -11
  229. package/components/Table/types.ts +9 -9
  230. package/components/Tabs/Tabs.astro +24 -24
  231. package/components/Tabs/Tabs.vue +20 -20
  232. package/components/Tabs/TabsItem.astro +40 -40
  233. package/components/Tabs/TabsItem.vue +39 -39
  234. package/components/Tabs/TabsPanel.astro +16 -16
  235. package/components/Tabs/TabsPanel.vue +31 -31
  236. package/components/Tabs/TabsTab.astro +15 -15
  237. package/components/Tabs/TabsTab.vue +24 -24
  238. package/components/Tabs/index.ts +14 -14
  239. package/components/Tabs/types.astro.ts +11 -11
  240. package/components/Tabs/types.d.vue.ts +31 -31
  241. package/components/Tabs/types.solid.ts +12 -12
  242. package/components/Tabs/types.svelte.ts +10 -10
  243. package/components/Tabs/types.ts +19 -19
  244. package/components/TextField/TextField.astro +124 -124
  245. package/components/TextField/TextField.vue +74 -74
  246. package/components/TextField/types.astro.ts +17 -17
  247. package/components/Textarea/Textarea.astro +118 -118
  248. package/components/Textarea/Textarea.vue +64 -64
  249. package/components/Textarea/types.astro.ts +10 -10
  250. package/components/Textarea/types.d.vue.ts +19 -19
  251. package/components/Textarea/types.solid.ts +6 -6
  252. package/components/Textarea/types.svelte.ts +21 -21
  253. package/components/Textarea/types.ts +33 -33
  254. package/components/Toast/Toast.astro +42 -42
  255. package/components/Toast/Toast.vue +48 -48
  256. package/components/ToggleButton/ToggleButton.astro +43 -43
  257. package/components/ToggleButton/ToggleButton.vue +41 -41
  258. package/components/ToggleButton/types.astro.ts +4 -4
  259. package/components/ToggleButton/types.d.vue.ts +10 -10
  260. package/components/ToggleButton/types.solid.ts +7 -7
  261. package/components/ToggleButton/types.svelte.ts +7 -7
  262. package/components/ToggleButton/types.ts +10 -10
  263. package/components/ToggleGroup/ToggleGroup.astro +42 -42
  264. package/components/ToggleGroup/ToggleGroup.vue +36 -36
  265. package/components/ToggleGroup/types.astro.ts +4 -4
  266. package/components/ToggleGroup/types.d.vue.ts +15 -15
  267. package/components/ToggleGroup/types.solid.ts +4 -4
  268. package/components/ToggleGroup/types.svelte.ts +9 -9
  269. package/components/ToggleGroup/types.ts +15 -15
  270. package/components/Tooltip/Tooltip.astro +23 -23
  271. package/components/Tooltip/Tooltip.vue +22 -22
  272. package/components/Tooltip/types.astro.ts +4 -4
  273. package/components/Tooltip/types.d.vue.ts +10 -10
  274. package/components/Tooltip/types.solid.ts +6 -6
  275. package/components/Tooltip/types.svelte.ts +7 -7
  276. package/components/Tooltip/types.ts +11 -11
  277. package/core/normalize.css +214 -214
  278. package/core/palette.css +314 -314
  279. package/core/utils.css +68 -69
  280. package/css/components/accordion.css +173 -173
  281. package/css/components/anchor.css +27 -27
  282. package/css/components/avatar.css +65 -65
  283. package/css/components/badge.css +107 -107
  284. package/css/components/button-group.css +179 -193
  285. package/css/components/button.css +212 -228
  286. package/css/components/callout.css +112 -112
  287. package/css/components/card.css +116 -115
  288. package/css/components/checkbox.css +211 -211
  289. package/css/components/chip.css +92 -92
  290. package/css/components/description-list.css +81 -81
  291. package/css/components/dialog.css +64 -64
  292. package/css/components/divider.css +20 -20
  293. package/css/components/drawer.css +170 -172
  294. package/css/components/form.css +146 -147
  295. package/css/components/icon-button.css +139 -156
  296. package/css/components/link.css +21 -21
  297. package/css/components/list.css +343 -343
  298. package/css/components/progress.css +99 -99
  299. package/css/components/radio.css +170 -170
  300. package/css/components/range.css +320 -339
  301. package/css/components/select.css +170 -171
  302. package/css/components/spinner.css +38 -38
  303. package/css/components/switch.css +270 -270
  304. package/css/components/table.css +173 -173
  305. package/css/components/tabs.css +84 -84
  306. package/css/components/text-field.css +449 -449
  307. package/css/components/text-input.css +68 -68
  308. package/css/components/textarea.css +26 -26
  309. package/css/components/toast.css +157 -160
  310. package/css/components/toggle-button.css +91 -91
  311. package/css/components/toggle-group.css +56 -56
  312. package/css/components/tooltip.css +68 -70
  313. package/css/components/typography.css +580 -585
  314. package/css/components.css +41 -41
  315. package/css/imports.css +17 -17
  316. package/css/js/checkbox.js +21 -21
  317. package/css/js/toast.js +94 -94
  318. package/css/theme.css +124 -125
  319. package/dist/op.css +4 -4
  320. package/dist/op.css.map +1 -1
  321. package/dist/opui.components.css +5140 -5219
  322. package/dist/opui.components.css.map +1 -1
  323. package/dist/opui.css +5907 -5988
  324. package/dist/opui.css.map +1 -1
  325. package/env.d.ts +14 -14
  326. package/open-props.css +6 -6
  327. package/package.json +90 -90
  328. package/vue/index.ts +54 -54
@@ -1,247 +1,247 @@
1
- # UI Component Implementation Guide
2
-
3
- This guide defines the standards for implementing reusable Astro UI components in this package (`packages/opui/components/<Name>/<Name>.astro`) based on the documentation specs at `src/docs/components/`. Use it as a reference for maintaining consistency and accessibility across the library.
4
-
5
- **CRITICAL INSTRUCTION**: Always use components from this package (imported via the `@opui/astro` alias) and their corresponding CSS from `packages/opui/css/components/` whenever possible. Avoid writing custom CSS if an existing component or utility can achieve the desired result.
6
-
7
- ## 0. Implementation Checklist
8
-
9
- When implementing or updating a component, ensure:
10
- - [ ] **Title Export**: `export const title = "Component Name"` is present.
11
- - [ ] **Props Type**: Imported `Props` from `./types.astro` (avoid interfaces).
12
- - [ ] **Sorting**: Props, Destructuring, and Classes are sorted alphabetically.
13
- - [ ] **Rest Props**: `...rest` is captured and spread onto the root element.
14
- - [ ] **Class Management**: Used `class:list` for all class manipulations.
15
- - [ ] **ID Stability**: Used `Astro.locals.$id` for any internal element linking.
16
- - [ ] **Accessibility**: ARIA labels, roles, and relationships are correctly handled.
17
- - [ ] **Slot Strategy**: Named slots are used for structural content (icons, actions).
18
-
19
- ---
20
-
21
- ## 1. Frontmatter Structure
22
-
23
- Every component uses layered type files:
24
-
25
- - `types.ts` — shared, framework-agnostic props (component-specific only; no `HTMLAttributes`)
26
- - `types.astro.ts` — Astro props = base + `HTMLAttributes<element>`
27
- - `types.d.vue.ts` / `types.svelte.ts` / `types.solid.ts` — framework-specific extensions (for merge parity)
28
-
29
- Every component must follow this exact frontmatter layout:
30
-
31
- ```astro
32
- ---
33
- import type { Props } from "./types.astro"
34
-
35
- export const title = "My Component"
36
-
37
- const {
38
- class: className,
39
- disabled,
40
- label,
41
- size,
42
- variant = "outlined",
43
- ...rest
44
- } = Astro.props
45
- ---
46
- ```
47
-
48
- ### Key Rules:
49
- - **Alphabetical Sorting**: Sort property definitions in `types.ts` and variables in the destructuring statement.
50
- - **REST Support**: Use `HTMLAttributes` in `types.astro.ts` and `...rest` in destructuring. Do not add `[key: string]: any` to `types.ts` — native attributes belong in the framework type files.
51
- - **Title Export**: This is mandatory for documentation generation.
52
-
53
- ---
54
-
55
- ## 2. Component Composition & Tags
56
-
57
- ### Dynamic Elements (Polymorphism)
58
- For components that can be different HTML elements (e.g., `Button` as `<a>` or `<button>`), use a dynamic `Tag` variable.
59
-
60
- ```astro
61
- ---
62
- const { as: Tag = Astro.props.href ? "a" : "button", ...rest } = Astro.props
63
- ---
64
- <Tag {...rest}><slot /></Tag>
65
- ```
66
- - **Inference**: Default to `"a"` if `href` is present.
67
- - **Override**: Allow manual override via the `as` prop.
68
-
69
- ### Styling Inheritance
70
- Components should often inherit styles from others (e.g., `Dialog` looking like a `Card`). Use conditional classes to apply the inherited base class. All library classes are prefixed with `ui-`.
71
-
72
- ```astro
73
- <dialog class:list={["ui-dialog", { "ui-card": variant }, variant && `ui-${variant}`, className]} {...rest}>
74
- ```
75
-
76
- ---
77
-
78
- ## 3. Class Management
79
-
80
- Use `class:list` exclusively. Every library-owned class is prefixed with `ui-`; the public prop API stays unprefixed (`<Button size="small" variant="outlined">`) and the component interpolates the prefix when rendering.
81
-
82
- Follow this order for readability:
83
- 1. **Component Base Class**: The primary CSS class, prefixed (e.g., `"ui-button"`).
84
- 2. **State Objects**: Boolean flags as quoted prefixed keys (e.g., `{ "ui-disabled": disabled }`).
85
- 3. **Variant Props**: Interpolate the prefix from the prop value (e.g., `size && \`ui-${size}\``).
86
- 4. **External Class**: Always include `className` at the end to allow for overrides. Do not prefix `className`.
87
-
88
- ```astro
89
- <div
90
- class:list={[
91
- "ui-chip",
92
- {
93
- "ui-multiline": isMultiline,
94
- },
95
- size && `ui-${size}`,
96
- variant && `ui-${variant}`,
97
- className,
98
- ]}
99
- >
100
- ```
101
-
102
- ---
103
-
104
- ## 4. Slot Patterns
105
-
106
- ### Default Slots vs Label Props
107
- For simple text-based components, provide both a `label` prop and a default slot.
108
-
109
- ```astro
110
- <div class="ui-component">
111
- { (label || Astro.slots.has("default")) && (
112
- <span class="ui-text">
113
- {label}
114
- <slot />
115
- </span>
116
- )}
117
- </div>
118
- ```
119
-
120
- ### Named Slots (Icons & Actions)
121
- Use named slots for specific functional areas. Check for existence before rendering wrapper tags.
122
-
123
- ```astro
124
- <div class="ui-card">
125
- {Astro.slots.has("header") && <header><slot name="header" /></header>}
126
- <slot />
127
- {Astro.slots.has("actions") && <footer class="ui-actions"><slot name="actions" /></footer>}
128
- </div>
129
- ```
130
-
131
- ---
132
-
133
- ## 5. Form & Input Patterns
134
-
135
- ### Label Wrapping
136
- Most inputs should be wrapped in a `<label>` to provide a larger hit area and built-in accessibility.
137
-
138
- ```astro
139
- <label class:list={["ui-field", { "ui-disabled": disabled }]} data-invalid={error || undefined}>
140
- <span class="ui-label">{label}</span>
141
- <input type="text" {...rest} />
142
- {endText && <span class="ui-end-text">{endText}</span>}
143
- </label>
144
- ```
145
-
146
- ### End Text & ARIA
147
- When providing `endText`, use `Astro.locals.$id` to link it to the input via `aria-describedby`.
148
-
149
- ```astro
150
- ---
151
- const { $id } = Astro.locals
152
- const helpId = $id("help")
153
- ---
154
- <input aria-describedby={endText ? helpId : undefined} />
155
- {endText && <span id={helpId}>{endText}</span>}
156
- ```
157
-
158
- ### Input Attribute Mapping
159
- Explicitly define and pass through native input attributes in the `Props` type for better IDE support.
160
-
161
- ```astro
162
- <input
163
- disabled={disabled}
164
- name={name}
165
- required={required}
166
- type={type}
167
- value={value}
168
- {...rest}
169
- />
170
- ```
171
-
172
- ### Sub-component Decomposition
173
- For complex inputs (like `Checkbox` with custom visuals), split the component into a public wrapper (`Checkbox.astro`) and a private raw input (`CheckboxInput.astro`) to keep the root element clean.
174
-
175
- ### Form Structure: FieldSet, FieldLegend, FieldDescription, FieldGroup
176
- When building forms, follow this nesting order inside a `FieldSet`:
177
- 1. `FieldLegend` - The heading/label for the fieldset
178
- 2. `FieldDescription` - Optional helper text
179
- 3. `FieldGroup` - Wrapper for the actual form controls
180
-
181
- **Always wrap form elements in a `FieldGroup`**, never place them directly after `FieldLegend` or `FieldDescription`.
182
-
183
- ```astro
184
- <!-- ✅ Correct -->
185
- <FieldSet>
186
- <FieldLegend>Username</FieldLegend>
187
- <FieldDescription>Enter your preferred username.</FieldDescription>
188
- <FieldGroup>
189
- <TextField placeholder="e.g. jdoe" />
190
- </FieldGroup>
191
- </FieldSet>
192
-
193
- <!-- ❌ Incorrect - TextField directly after FieldDescription -->
194
- <FieldSet>
195
- <FieldLegend>Username</FieldLegend>
196
- <FieldDescription>Enter your preferred username.</FieldDescription>
197
- <TextField placeholder="e.g. jdoe" />
198
- </FieldSet>
199
- ```
200
-
201
- ---
202
-
203
- ## 6. Identification & Accessibility
204
-
205
- ### Unique IDs
206
- Always use `Astro.locals.$id` for IDs. This ensures stability across server and client rendering and prevents ID collisions when multiple instances of the same component are on a page.
207
-
208
- ```astro
209
- const { $id } = Astro.locals
210
- const fieldId = id || $id("input") // Prefer passed ID if available
211
- ```
212
-
213
- ### Default Icons
214
- Components like `Callout` or `Toast` should provide default SVG icons within their named slots, while allowing users to override them.
215
-
216
- ```astro
217
- <slot name="icon">
218
- {severity === "error" && <svg>...</svg>}
219
- </slot>
220
- ```
221
-
222
- ---
223
-
224
- ## 7. Data-Driven Components
225
-
226
- For repetitive structures (DescriptionLists, Menus, Selects), prefer an `items` prop alongside standard slots.
227
-
228
- ```astro
229
- type Item = { label: string; value: any }
230
- type Props = { items?: Item[] }
231
-
232
- <ul>
233
- {items.map(item => <li>{item.label}</li>)}
234
- <slot /> <!-- Still allow manual entries -->
235
- </ul>
236
- ```
237
-
238
- ---
239
-
240
- ## 8. Development Workflow
241
-
242
- 1. **Read the Spec**: Open `src/docs/components/[name].astro` to see required HTML and CSS classes. (Component docs live under `src/docs/components/` now; `src/pages/components/` only contains route shells.)
243
- 2. **Analyze the API**: Check `src/component-api/[name]/` (`Astro.astro` and `HTML.astro`) or `src/component-api/[name]-api.astro` for the documented props.
244
- 3. **Implement**: Follow the checklist in Section 0.
245
- 4. **Verify**: Check that your implementation matches the "HTML" preview in the rendered `Example` for the spec page (`/components/[name]`). For framework-specific behaviour, also verify the Astro variant at `/astro/components/[name]`.
246
-
247
-
1
+ # UI Component Implementation Guide
2
+
3
+ This guide defines the standards for implementing reusable Astro UI components in this package (`packages/opui/components/<Name>/<Name>.astro`) based on the documentation specs at `src/docs/components/`. Use it as a reference for maintaining consistency and accessibility across the library.
4
+
5
+ **CRITICAL INSTRUCTION**: Always use components from this package (imported via the `@opui/astro` alias) and their corresponding CSS from `packages/opui/css/components/` whenever possible. Avoid writing custom CSS if an existing component or utility can achieve the desired result.
6
+
7
+ ## 0. Implementation Checklist
8
+
9
+ When implementing or updating a component, ensure:
10
+ - [ ] **Title Export**: `export const title = "Component Name"` is present.
11
+ - [ ] **Props Type**: Imported `Props` from `./types.astro` (avoid interfaces).
12
+ - [ ] **Sorting**: Props, Destructuring, and Classes are sorted alphabetically.
13
+ - [ ] **Rest Props**: `...rest` is captured and spread onto the root element.
14
+ - [ ] **Class Management**: Used `class:list` for all class manipulations.
15
+ - [ ] **ID Stability**: Used `Astro.locals.$id` for any internal element linking.
16
+ - [ ] **Accessibility**: ARIA labels, roles, and relationships are correctly handled.
17
+ - [ ] **Slot Strategy**: Named slots are used for structural content (icons, actions).
18
+
19
+ ---
20
+
21
+ ## 1. Frontmatter Structure
22
+
23
+ Every component uses layered type files:
24
+
25
+ - `types.ts` - shared, framework-agnostic props (component-specific only; no `HTMLAttributes`)
26
+ - `types.astro.ts` - Astro props = base + `HTMLAttributes<element>`
27
+ - `types.d.vue.ts` / `types.svelte.ts` / `types.solid.ts` - framework-specific extensions (for merge parity)
28
+
29
+ Every component must follow this exact frontmatter layout:
30
+
31
+ ```astro
32
+ ---
33
+ import type { Props } from "./types.astro"
34
+
35
+ export const title = "My Component"
36
+
37
+ const {
38
+ class: className,
39
+ disabled,
40
+ label,
41
+ size,
42
+ variant = "outlined",
43
+ ...rest
44
+ } = Astro.props
45
+ ---
46
+ ```
47
+
48
+ ### Key Rules:
49
+ - **Alphabetical Sorting**: Sort property definitions in `types.ts` and variables in the destructuring statement.
50
+ - **REST Support**: Use `HTMLAttributes` in `types.astro.ts` and `...rest` in destructuring. Do not add `[key: string]: any` to `types.ts` - native attributes belong in the framework type files.
51
+ - **Title Export**: This is mandatory for documentation generation.
52
+
53
+ ---
54
+
55
+ ## 2. Component Composition & Tags
56
+
57
+ ### Dynamic Elements (Polymorphism)
58
+ For components that can be different HTML elements (e.g., `Button` as `<a>` or `<button>`), use a dynamic `Tag` variable.
59
+
60
+ ```astro
61
+ ---
62
+ const { as: Tag = Astro.props.href ? "a" : "button", ...rest } = Astro.props
63
+ ---
64
+ <Tag {...rest}><slot /></Tag>
65
+ ```
66
+ - **Inference**: Default to `"a"` if `href` is present.
67
+ - **Override**: Allow manual override via the `as` prop.
68
+
69
+ ### Styling Inheritance
70
+ Components should often inherit styles from others (e.g., `Dialog` looking like a `Card`). Use conditional classes to apply the inherited base class. All library classes are prefixed with `ui-`.
71
+
72
+ ```astro
73
+ <dialog class:list={["ui-dialog", { "ui-card": variant }, variant && `ui-${variant}`, className]} {...rest}>
74
+ ```
75
+
76
+ ---
77
+
78
+ ## 3. Class Management
79
+
80
+ Use `class:list` exclusively. Every library-owned class is prefixed with `ui-`; the public prop API stays unprefixed (`<Button size="small" variant="outlined">`) and the component interpolates the prefix when rendering.
81
+
82
+ Follow this order for readability:
83
+ 1. **Component Base Class**: The primary CSS class, prefixed (e.g., `"ui-button"`).
84
+ 2. **State Objects**: Boolean flags as quoted prefixed keys (e.g., `{ "ui-disabled": disabled }`).
85
+ 3. **Variant Props**: Interpolate the prefix from the prop value (e.g., `size && \`ui-${size}\``).
86
+ 4. **External Class**: Always include `className` at the end to allow for overrides. Do not prefix `className`.
87
+
88
+ ```astro
89
+ <div
90
+ class:list={[
91
+ "ui-chip",
92
+ {
93
+ "ui-multiline": isMultiline,
94
+ },
95
+ size && `ui-${size}`,
96
+ variant && `ui-${variant}`,
97
+ className,
98
+ ]}
99
+ >
100
+ ```
101
+
102
+ ---
103
+
104
+ ## 4. Slot Patterns
105
+
106
+ ### Default Slots vs Label Props
107
+ For simple text-based components, provide both a `label` prop and a default slot.
108
+
109
+ ```astro
110
+ <div class="ui-component">
111
+ { (label || Astro.slots.has("default")) && (
112
+ <span class="ui-text">
113
+ {label}
114
+ <slot />
115
+ </span>
116
+ )}
117
+ </div>
118
+ ```
119
+
120
+ ### Named Slots (Icons & Actions)
121
+ Use named slots for specific functional areas. Check for existence before rendering wrapper tags.
122
+
123
+ ```astro
124
+ <div class="ui-card">
125
+ {Astro.slots.has("header") && <header><slot name="header" /></header>}
126
+ <slot />
127
+ {Astro.slots.has("actions") && <footer class="ui-actions"><slot name="actions" /></footer>}
128
+ </div>
129
+ ```
130
+
131
+ ---
132
+
133
+ ## 5. Form & Input Patterns
134
+
135
+ ### Label Wrapping
136
+ Most inputs should be wrapped in a `<label>` to provide a larger hit area and built-in accessibility.
137
+
138
+ ```astro
139
+ <label class:list={["ui-field", { "ui-disabled": disabled }]} data-invalid={error || undefined}>
140
+ <span class="ui-label">{label}</span>
141
+ <input type="text" {...rest} />
142
+ {endText && <span class="ui-end-text">{endText}</span>}
143
+ </label>
144
+ ```
145
+
146
+ ### End Text & ARIA
147
+ When providing `endText`, use `Astro.locals.$id` to link it to the input via `aria-describedby`.
148
+
149
+ ```astro
150
+ ---
151
+ const { $id } = Astro.locals
152
+ const helpId = $id("help")
153
+ ---
154
+ <input aria-describedby={endText ? helpId : undefined} />
155
+ {endText && <span id={helpId}>{endText}</span>}
156
+ ```
157
+
158
+ ### Input Attribute Mapping
159
+ Explicitly define and pass through native input attributes in the `Props` type for better IDE support.
160
+
161
+ ```astro
162
+ <input
163
+ disabled={disabled}
164
+ name={name}
165
+ required={required}
166
+ type={type}
167
+ value={value}
168
+ {...rest}
169
+ />
170
+ ```
171
+
172
+ ### Sub-component Decomposition
173
+ For complex inputs (like `Checkbox` with custom visuals), split the component into a public wrapper (`Checkbox.astro`) and a private raw input (`CheckboxInput.astro`) to keep the root element clean.
174
+
175
+ ### Form Structure: FieldSet, FieldLegend, FieldDescription, FieldGroup
176
+ When building forms, follow this nesting order inside a `FieldSet`:
177
+ 1. `FieldLegend` - The heading/label for the fieldset
178
+ 2. `FieldDescription` - Optional helper text
179
+ 3. `FieldGroup` - Wrapper for the actual form controls
180
+
181
+ **Always wrap form elements in a `FieldGroup`**, never place them directly after `FieldLegend` or `FieldDescription`.
182
+
183
+ ```astro
184
+ <!-- ✅ Correct -->
185
+ <FieldSet>
186
+ <FieldLegend>Username</FieldLegend>
187
+ <FieldDescription>Enter your preferred username.</FieldDescription>
188
+ <FieldGroup>
189
+ <TextField placeholder="e.g. jdoe" />
190
+ </FieldGroup>
191
+ </FieldSet>
192
+
193
+ <!-- ❌ Incorrect - TextField directly after FieldDescription -->
194
+ <FieldSet>
195
+ <FieldLegend>Username</FieldLegend>
196
+ <FieldDescription>Enter your preferred username.</FieldDescription>
197
+ <TextField placeholder="e.g. jdoe" />
198
+ </FieldSet>
199
+ ```
200
+
201
+ ---
202
+
203
+ ## 6. Identification & Accessibility
204
+
205
+ ### Unique IDs
206
+ Always use `Astro.locals.$id` for IDs. This ensures stability across server and client rendering and prevents ID collisions when multiple instances of the same component are on a page.
207
+
208
+ ```astro
209
+ const { $id } = Astro.locals
210
+ const fieldId = id || $id("input") // Prefer passed ID if available
211
+ ```
212
+
213
+ ### Default Icons
214
+ Components like `Callout` or `Toast` should provide default SVG icons within their named slots, while allowing users to override them.
215
+
216
+ ```astro
217
+ <slot name="icon">
218
+ {severity === "error" && <svg>...</svg>}
219
+ </slot>
220
+ ```
221
+
222
+ ---
223
+
224
+ ## 7. Data-Driven Components
225
+
226
+ For repetitive structures (DescriptionLists, Menus, Selects), prefer an `items` prop alongside standard slots.
227
+
228
+ ```astro
229
+ type Item = { label: string; value: any }
230
+ type Props = { items?: Item[] }
231
+
232
+ <ul>
233
+ {items.map(item => <li>{item.label}</li>)}
234
+ <slot /> <!-- Still allow manual entries -->
235
+ </ul>
236
+ ```
237
+
238
+ ---
239
+
240
+ ## 8. Development Workflow
241
+
242
+ 1. **Read the Spec**: Open `src/docs/components/[name].astro` to see required HTML and CSS classes. (Component docs live under `src/docs/components/` now; `src/pages/components/` only contains route shells.)
243
+ 2. **Analyze the API**: Check `src/component-api/[name]/` (`Astro.astro` and `HTML.astro`) or `src/component-api/[name]-api.astro` for the documented props.
244
+ 3. **Implement**: Follow the checklist in Section 0.
245
+ 4. **Verify**: Check that your implementation matches the "HTML" preview in the rendered `Example` for the spec page (`/components/[name]`). For framework-specific behaviour, also verify the Astro variant at `/astro/components/[name]`.
246
+
247
+
@@ -1,48 +1,48 @@
1
- ---
2
- import type { Props } from "./types.astro"
3
-
4
- export const title = "Accordion"
5
-
6
- const { $id } = Astro.locals
7
- const { class: className, name, open, variant, ...rest } = Astro.props
8
-
9
- const summaryId = $id("summary")
10
- const contentId = $id("content")
11
- ---
12
-
13
- <details
14
- name={name}
15
- class:list={[
16
- "ui-accordion",
17
- "ui-card",
18
- variant && `ui-${variant}`,
19
- className,
20
- ]}
21
- open={open}
22
- {...rest}
23
- >
24
- <!-- Summary -->
25
-
26
- <summary id={summaryId} aria-controls={contentId}
27
- ><slot name="summary" /><slot name="marker" /></summary
28
- >
29
-
30
- <!-- Content -->
31
- <div
32
- id={contentId}
33
- class="ui-content"
34
- role="region"
35
- aria-labelledby={summaryId}
36
- >
37
- <slot />
38
- </div>
39
-
40
- <!-- Actions -->
41
- {
42
- Astro.slots.has("actions") && (
43
- <div class="ui-actions">
44
- <slot name="actions" />
45
- </div>
46
- )
47
- }
48
- </details>
1
+ ---
2
+ import type { Props } from "./types.astro"
3
+
4
+ export const title = "Accordion"
5
+
6
+ const { $id } = Astro.locals
7
+ const { class: className, name, open, variant, ...rest } = Astro.props
8
+
9
+ const summaryId = $id("summary")
10
+ const contentId = $id("content")
11
+ ---
12
+
13
+ <details
14
+ name={name}
15
+ class:list={[
16
+ "ui-accordion",
17
+ "ui-card",
18
+ variant && `ui-${variant}`,
19
+ className,
20
+ ]}
21
+ open={open}
22
+ {...rest}
23
+ >
24
+ <!-- Summary -->
25
+
26
+ <summary id={summaryId} aria-controls={contentId}
27
+ ><slot name="summary" /><slot name="marker" /></summary
28
+ >
29
+
30
+ <!-- Content -->
31
+ <div
32
+ id={contentId}
33
+ class="ui-content"
34
+ role="region"
35
+ aria-labelledby={summaryId}
36
+ >
37
+ <slot />
38
+ </div>
39
+
40
+ <!-- Actions -->
41
+ {
42
+ Astro.slots.has("actions") && (
43
+ <div class="ui-actions">
44
+ <slot name="actions" />
45
+ </div>
46
+ )
47
+ }
48
+ </details>
@@ -1,38 +1,38 @@
1
- <script setup lang="ts">
2
- import { useId } from "vue"
3
- import type { Props, Slots } from "./types.d.vue"
4
-
5
- const { class: className, name, open, variant } = defineProps<Props>()
6
- defineSlots<Slots>()
7
-
8
- const summaryId = useId()
9
- const contentId = useId()
10
- </script>
11
-
12
- <template>
13
- <details
14
- :name="name"
15
- :class="['ui-accordion', 'ui-card', variant && `ui-${variant}`, className]"
16
- :open="open"
17
- >
18
- <!-- Summary -->
19
- <summary :id="summaryId" :aria-controls="contentId">
20
- <slot name="summary"></slot><slot name="marker"></slot>
21
- </summary>
22
-
23
- <!-- Content -->
24
- <div
25
- :id="contentId"
26
- class="ui-content"
27
- role="region"
28
- :aria-labelledby="summaryId"
29
- >
30
- <slot></slot>
31
- </div>
32
-
33
- <!-- Actions -->
34
- <div v-if="$slots.actions" class="ui-actions">
35
- <slot name="actions"></slot>
36
- </div>
37
- </details>
38
- </template>
1
+ <script setup lang="ts">
2
+ import { useId } from "vue"
3
+ import type { Props, Slots } from "./types.d.vue"
4
+
5
+ const { class: className, name, open, variant } = defineProps<Props>()
6
+ defineSlots<Slots>()
7
+
8
+ const summaryId = useId()
9
+ const contentId = useId()
10
+ </script>
11
+
12
+ <template>
13
+ <details
14
+ :name="name"
15
+ :class="['ui-accordion', 'ui-card', variant && `ui-${variant}`, className]"
16
+ :open="open"
17
+ >
18
+ <!-- Summary -->
19
+ <summary :id="summaryId" :aria-controls="contentId">
20
+ <slot name="summary"></slot><slot name="marker"></slot>
21
+ </summary>
22
+
23
+ <!-- Content -->
24
+ <div
25
+ :id="contentId"
26
+ class="ui-content"
27
+ role="region"
28
+ :aria-labelledby="summaryId"
29
+ >
30
+ <slot></slot>
31
+ </div>
32
+
33
+ <!-- Actions -->
34
+ <div v-if="$slots.actions" class="ui-actions">
35
+ <slot name="actions"></slot>
36
+ </div>
37
+ </details>
38
+ </template>