@pithyjs/pidl-catalog 0.1.0-beta.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 (93) hide show
  1. package/LICENSE +21 -0
  2. package/dist/specs/atoms/actions/button.pidl.json +289 -0
  3. package/dist/specs/atoms/actions/icon-group.pidl.json +141 -0
  4. package/dist/specs/atoms/actions/segmented-control.pidl.json +137 -0
  5. package/dist/specs/atoms/content/action-tile.pidl.json +135 -0
  6. package/dist/specs/atoms/content/badge.pidl.json +189 -0
  7. package/dist/specs/atoms/content/callout.pidl.json +170 -0
  8. package/dist/specs/atoms/content/code-block.pidl.json +170 -0
  9. package/dist/specs/atoms/content/heading.pidl.json +175 -0
  10. package/dist/specs/atoms/content/icon.pidl.json +127 -0
  11. package/dist/specs/atoms/content/image.pidl.json +227 -0
  12. package/dist/specs/atoms/content/prose.pidl.json +183 -0
  13. package/dist/specs/atoms/content/stat.pidl.json +158 -0
  14. package/dist/specs/atoms/content/tabs.pidl.json +207 -0
  15. package/dist/specs/atoms/content/text.pidl.json +212 -0
  16. package/dist/specs/atoms/content/tile.pidl.json +156 -0
  17. package/dist/specs/atoms/data/donut.pidl.json +352 -0
  18. package/dist/specs/atoms/data/gauge.pidl.json +274 -0
  19. package/dist/specs/atoms/forms/checkbox-group.pidl.json +170 -0
  20. package/dist/specs/atoms/forms/checkbox.pidl.json +323 -0
  21. package/dist/specs/atoms/forms/file-picker.pidl.json +161 -0
  22. package/dist/specs/atoms/forms/form-field.pidl.json +106 -0
  23. package/dist/specs/atoms/forms/input.pidl.json +466 -0
  24. package/dist/specs/atoms/forms/radio-group.pidl.json +174 -0
  25. package/dist/specs/atoms/forms/radio.pidl.json +312 -0
  26. package/dist/specs/atoms/forms/search-input.pidl.json +265 -0
  27. package/dist/specs/atoms/forms/select.pidl.json +420 -0
  28. package/dist/specs/atoms/forms/textarea.pidl.json +422 -0
  29. package/dist/specs/atoms/forms/toggle.pidl.json +314 -0
  30. package/dist/specs/layout/grid-item.pidl.json +120 -0
  31. package/dist/specs/layout/grid.pidl.json +157 -0
  32. package/dist/specs/layout/page-section.pidl.json +105 -0
  33. package/dist/specs/layout/smart-container.pidl.json +148 -0
  34. package/dist/specs/layout/stack.pidl.json +144 -0
  35. package/dist/specs/organisms/content/accordion.pidl.json +196 -0
  36. package/dist/specs/organisms/content/card.pidl.json +283 -0
  37. package/dist/specs/organisms/feedback/alert-dialog.pidl.json +158 -0
  38. package/dist/specs/organisms/feedback/modal.pidl.json +133 -0
  39. package/dist/specs/organisms/feedback/toast.pidl.json +150 -0
  40. package/dist/specs/organisms/forms/dynamic-form.pidl.json +314 -0
  41. package/dist/specs/organisms/forms/form.pidl.json +135 -0
  42. package/dist/specs/organisms/forms/subscribe-form.pidl.json +188 -0
  43. package/dist/specs/organisms/navigation/command-palette.pidl.json +263 -0
  44. package/dist/specs/organisms/navigation/docs-sidebar.pidl.json +264 -0
  45. package/dist/specs/organisms/navigation/navbar.pidl.json +356 -0
  46. package/dist/specs/organisms/navigation/table-of-contents.pidl.json +197 -0
  47. package/dist/specs/sections/contact-section.pidl.json +183 -0
  48. package/dist/specs/sections/cta-section.pidl.json +168 -0
  49. package/dist/specs/sections/faq-section.pidl.json +202 -0
  50. package/dist/specs/sections/features-section.pidl.json +214 -0
  51. package/dist/specs/sections/footer-section.pidl.json +187 -0
  52. package/dist/specs/sections/hero-section.pidl.json +202 -0
  53. package/dist/src/borders-psychology.d.ts +17 -0
  54. package/dist/src/borders-psychology.d.ts.map +1 -0
  55. package/dist/src/borders-psychology.js +42 -0
  56. package/dist/src/borders-psychology.js.map +1 -0
  57. package/dist/src/color-psychology.d.ts +20 -0
  58. package/dist/src/color-psychology.d.ts.map +1 -0
  59. package/dist/src/color-psychology.js +57 -0
  60. package/dist/src/color-psychology.js.map +1 -0
  61. package/dist/src/design-intelligence.d.ts +13 -0
  62. package/dist/src/design-intelligence.d.ts.map +1 -0
  63. package/dist/src/design-intelligence.js +36 -0
  64. package/dist/src/design-intelligence.js.map +1 -0
  65. package/dist/src/index.d.ts +6238 -0
  66. package/dist/src/index.d.ts.map +1 -0
  67. package/dist/src/index.js +105 -0
  68. package/dist/src/index.js.map +1 -0
  69. package/dist/src/motion-psychology.d.ts +19 -0
  70. package/dist/src/motion-psychology.d.ts.map +1 -0
  71. package/dist/src/motion-psychology.js +47 -0
  72. package/dist/src/motion-psychology.js.map +1 -0
  73. package/dist/src/mutation-vocabulary.d.ts +13 -0
  74. package/dist/src/mutation-vocabulary.d.ts.map +1 -0
  75. package/dist/src/mutation-vocabulary.js +67 -0
  76. package/dist/src/mutation-vocabulary.js.map +1 -0
  77. package/dist/src/radius-psychology.d.ts +17 -0
  78. package/dist/src/radius-psychology.d.ts.map +1 -0
  79. package/dist/src/radius-psychology.js +42 -0
  80. package/dist/src/radius-psychology.js.map +1 -0
  81. package/dist/src/shadows-psychology.d.ts +17 -0
  82. package/dist/src/shadows-psychology.d.ts.map +1 -0
  83. package/dist/src/shadows-psychology.js +44 -0
  84. package/dist/src/shadows-psychology.js.map +1 -0
  85. package/dist/src/spacing-psychology.d.ts +22 -0
  86. package/dist/src/spacing-psychology.d.ts.map +1 -0
  87. package/dist/src/spacing-psychology.js +59 -0
  88. package/dist/src/spacing-psychology.js.map +1 -0
  89. package/dist/src/typography-psychology.d.ts +22 -0
  90. package/dist/src/typography-psychology.d.ts.map +1 -0
  91. package/dist/src/typography-psychology.js +68 -0
  92. package/dist/src/typography-psychology.js.map +1 -0
  93. package/package.json +64 -0
@@ -0,0 +1,105 @@
1
+ {
2
+ "kind": "component",
3
+ "id": "ui.page-section",
4
+ "name": "PageSection",
5
+ "displayName": "Page Section",
6
+ "version": "0.1.0",
7
+ "level": "atom",
8
+ "domain": "layout",
9
+ "bidi": true,
10
+ "description": "Semantic page section wrapper with consistent vertical spacing, optional containment, and full-bleed support. Named PageSection to avoid collision with native <section>.",
11
+ "props": [
12
+ {
13
+ "name": "spacing",
14
+ "label": "Spacing",
15
+ "editor": { "tab": "layout", "type": "token-space" },
16
+ "type": "enum",
17
+ "enum": ["0", "xs", "sm", "md", "lg", "xl"],
18
+ "default": "lg",
19
+ "description": "Vertical padding — maps to u-py-* utilities"
20
+ },
21
+ {
22
+ "name": "contained",
23
+ "label": "Contained",
24
+ "editor": { "tab": "layout" },
25
+ "type": "boolean",
26
+ "default": false,
27
+ "description": "Apply max-width + centered layout with gutters"
28
+ },
29
+ {
30
+ "name": "fullBleed",
31
+ "label": "Full bleed",
32
+ "editor": { "tab": "layout" },
33
+ "type": "boolean",
34
+ "default": false,
35
+ "description": "Edge-to-edge viewport width breakout"
36
+ },
37
+ {
38
+ "name": "as",
39
+ "label": "Element",
40
+ "editor": { "tab": "settings" },
41
+ "type": "enum",
42
+ "enum": ["section", "article", "aside", "header", "footer", "main"],
43
+ "default": "section",
44
+ "description": "Semantic HTML tag"
45
+ },
46
+ {
47
+ "name": "id",
48
+ "label": "Anchor ID",
49
+ "editor": { "tab": "settings" },
50
+ "type": "string",
51
+ "description": "Optional anchor id for in-page linking"
52
+ }
53
+ ],
54
+ "events": [],
55
+ "slots": [{ "name": "default", "displayName": "Content" }],
56
+ "tokens": {
57
+ "spacing": "--space-{spacing} (via u-py-*)",
58
+ "container.max-width": "CONTAINER_BREAKPOINTS.xl",
59
+ "container.gutter": "--space-md"
60
+ },
61
+ "spacing": {
62
+ "vertical-padding": "--space-{spacing} (strangers — between page sections use xl, within use lg)"
63
+ },
64
+ "constraints": [
65
+ "contained and fullBleed are mutually exclusive — fullBleed takes precedence",
66
+ "Use logical properties only — marginInline, paddingInline",
67
+ "Does not add max-width on its own — use contained prop or let the page wrapper control width"
68
+ ],
69
+ "a11y": [
70
+ "Uses semantic HTML elements via as prop — <section>, <article>, <aside>, <header>, <footer>, <main>",
71
+ "id prop enables in-page anchor navigation",
72
+ "No ARIA attributes needed — semantics come from the HTML element"
73
+ ],
74
+ "ai": {
75
+ "synonyms": [
76
+ "section wrapper",
77
+ "page section",
78
+ "content section",
79
+ "layout section"
80
+ ],
81
+ "avoid": [
82
+ "div with padding for page sections — use <PageSection>",
83
+ "contained + fullBleed together — they conflict"
84
+ ],
85
+ "patterns": [
86
+ "Contained section: <PageSection spacing=\"'lg'\" contained><Heading>Features</Heading></PageSection>",
87
+ "Full-bleed hero: <PageSection spacing=\"'xl'\" fullBleed>...</PageSection>",
88
+ "Anchored section: <PageSection id=\"'about'\" as=\"'article'\">...</PageSection>"
89
+ ]
90
+ },
91
+ "examples": [
92
+ {
93
+ "id": "basic-section",
94
+ "title": "Basic page section",
95
+ "pithy": "<PageSection spacing=\"'lg'\"><Heading>Features</Heading><Text>Details here</Text></PageSection>",
96
+ "assertions": ["renders <section>", "has u-py-lg class"]
97
+ },
98
+ {
99
+ "id": "contained",
100
+ "title": "Contained section with max-width",
101
+ "pithy": "<PageSection contained><Text>Centered content</Text></PageSection>",
102
+ "assertions": ["has max-width style", "has margin-inline: auto"]
103
+ }
104
+ ]
105
+ }
@@ -0,0 +1,148 @@
1
+ {
2
+ "kind": "component",
3
+ "id": "ui.smart-container",
4
+ "name": "SmartContainer",
5
+ "displayName": "Smart Container",
6
+ "version": "0.1.0",
7
+ "level": "atom",
8
+ "domain": "layout",
9
+ "bidi": true,
10
+ "description": "Higher-level layout component providing 5 opinionated responsive layout recipes (auto-grid, stack-to-row, row-wrap, sidebar, equal-cards) that adapt to container width via CSS container queries.",
11
+ "props": [
12
+ {
13
+ "name": "recipe",
14
+ "label": "Recipe",
15
+ "editor": { "tab": "layout" },
16
+ "type": "enum",
17
+ "enum": [
18
+ "auto-grid",
19
+ "stack-to-row",
20
+ "row-wrap",
21
+ "sidebar",
22
+ "equal-cards"
23
+ ],
24
+ "default": "auto-grid",
25
+ "description": "Layout recipe — each provides a different responsive behavior pattern"
26
+ },
27
+ {
28
+ "name": "minItem",
29
+ "label": "Min item",
30
+ "editor": { "tab": "layout" },
31
+ "type": "string",
32
+ "description": "Minimum item width for auto-grid recipe, e.g. '14rem', '250px'"
33
+ },
34
+ {
35
+ "name": "gap",
36
+ "label": "Gap",
37
+ "editor": { "tab": "layout", "type": "token-space" },
38
+ "type": "enum",
39
+ "enum": ["0", "xs", "sm", "md", "lg", "xl"],
40
+ "default": "md",
41
+ "description": "Gap between children — maps to u-gap-* utilities"
42
+ },
43
+ {
44
+ "name": "density",
45
+ "label": "Density",
46
+ "editor": { "tab": "layout" },
47
+ "type": "enum",
48
+ "enum": ["compact", "normal", "relaxed"],
49
+ "default": "normal",
50
+ "description": "Density scaling — compact (0.9), normal (1.0), relaxed (1.25)"
51
+ },
52
+ {
53
+ "name": "align",
54
+ "label": "Align",
55
+ "editor": { "tab": "layout" },
56
+ "type": "enum",
57
+ "enum": ["start", "center", "end", "stretch", "baseline"],
58
+ "description": "Cross-axis alignment — maps to u-items-* utilities"
59
+ },
60
+ {
61
+ "name": "justify",
62
+ "label": "Justify",
63
+ "editor": { "tab": "layout" },
64
+ "type": "enum",
65
+ "enum": ["start", "end", "center", "between", "around", "evenly"],
66
+ "description": "Main-axis justification — maps to u-justify-* utilities"
67
+ },
68
+ {
69
+ "name": "lanes",
70
+ "label": "Lanes",
71
+ "editor": { "tab": "layout" },
72
+ "type": "number",
73
+ "default": 3,
74
+ "description": "Max column count for equal-cards recipe (2-6)"
75
+ },
76
+ {
77
+ "name": "as",
78
+ "label": "Element",
79
+ "editor": { "tab": "settings" },
80
+ "type": "enum",
81
+ "enum": ["div", "section", "article", "aside"],
82
+ "default": "div",
83
+ "description": "HTML tag to render (outer container)"
84
+ }
85
+ ],
86
+ "events": [],
87
+ "slots": [{ "name": "default", "displayName": "Content" }],
88
+ "tokens": {
89
+ "gap": "--space-{gap} (via u-gap-*)",
90
+ "density": "density custom property via applyElementDensity()",
91
+ "auto-min": "--grid-auto-min (CSS custom property for auto-grid)"
92
+ },
93
+ "spacing": {
94
+ "between-children": "--space-{gap} (friendship model — choose gap based on child relationship)",
95
+ "card-grid": "--space-md (friends — cards in a grid)"
96
+ },
97
+ "constraints": [
98
+ "minItem only applies to auto-grid recipe — ignored for other recipes",
99
+ "lanes only applies to equal-cards recipe (clamped 2-6) — ignored for other recipes",
100
+ "Uses double-nesting (outer container-type + inner layout) — outer element has container-type: inline-size",
101
+ "Use logical properties only (handled automatically via utility classes)"
102
+ ],
103
+ "a11y": [
104
+ "Uses semantic HTML via as prop for the outer container",
105
+ "Layout is presentational — no ARIA attributes needed",
106
+ "Density scaling is visual only — does not affect screen reader content"
107
+ ],
108
+ "ai": {
109
+ "synonyms": [
110
+ "smart layout",
111
+ "responsive container",
112
+ "auto layout",
113
+ "adaptive grid",
114
+ "card container"
115
+ ],
116
+ "avoid": [
117
+ "manual media queries — use recipe prop for responsive behavior",
118
+ "SmartContainer for simple 1-direction layouts — use <Stack> instead",
119
+ "SmartContainer when exact column control needed — use <Grid> instead"
120
+ ],
121
+ "patterns": [
122
+ "Auto-grid cards: <SmartContainer recipe=\"'auto-grid'\" minItem=\"'14rem'\" gap=\"'lg'\"><Card/><Card/><Card/></SmartContainer>",
123
+ "Sidebar layout: <SmartContainer recipe=\"'sidebar'\"><aside>Nav</aside><main>Content</main></SmartContainer>",
124
+ "Equal cards: <SmartContainer recipe=\"'equal-cards'\" lanes=\"4\" gap=\"'md'\"><Card/><Card/><Card/><Card/></SmartContainer>"
125
+ ]
126
+ },
127
+ "examples": [
128
+ {
129
+ "id": "auto-grid",
130
+ "title": "Auto-grid with minimum item width",
131
+ "pithy": "<SmartContainer recipe=\"'auto-grid'\" minItem=\"'14rem'\" gap=\"'lg'\"><div>Card 1</div><div>Card 2</div><div>Card 3</div></SmartContainer>",
132
+ "assertions": [
133
+ "outer has u-container-inline",
134
+ "inner has u-grid-auto-fit",
135
+ "--grid-auto-min set"
136
+ ]
137
+ },
138
+ {
139
+ "id": "equal-cards",
140
+ "title": "Equal cards with lane limit",
141
+ "pithy": "<SmartContainer recipe=\"'equal-cards'\" lanes=\"4\"><div>A</div><div>B</div><div>C</div><div>D</div></SmartContainer>",
142
+ "assertions": [
143
+ "inner has responsive grid column classes",
144
+ "capped at 4 columns"
145
+ ]
146
+ }
147
+ ]
148
+ }
@@ -0,0 +1,144 @@
1
+ {
2
+ "kind": "component",
3
+ "id": "ui.stack",
4
+ "name": "Stack",
5
+ "displayName": "Stack",
6
+ "version": "0.1.0",
7
+ "level": "atom",
8
+ "domain": "layout",
9
+ "bidi": true,
10
+ "description": "Flexbox layout atom that arranges children in a vertical or horizontal stack. Maps props to design-system utility classes — no custom CSS required.",
11
+ "props": [
12
+ {
13
+ "name": "direction",
14
+ "label": "Direction",
15
+ "editor": { "tab": "layout" },
16
+ "type": "enum",
17
+ "enum": ["vertical", "horizontal"],
18
+ "default": "vertical",
19
+ "description": "Stack direction — vertical (column) or horizontal (row)"
20
+ },
21
+ {
22
+ "name": "gap",
23
+ "label": "Gap",
24
+ "editor": { "tab": "layout", "type": "token-space" },
25
+ "type": "enum",
26
+ "enum": ["0", "xs", "sm", "md", "lg", "xl"],
27
+ "default": "md",
28
+ "description": "Gap between children — maps to u-gap-* utilities"
29
+ },
30
+ {
31
+ "name": "align",
32
+ "label": "Align",
33
+ "editor": { "tab": "layout" },
34
+ "type": "enum",
35
+ "enum": ["start", "center", "end", "stretch", "baseline"],
36
+ "default": "stretch",
37
+ "description": "Cross-axis alignment — maps to u-items-* utilities"
38
+ },
39
+ {
40
+ "name": "wrap",
41
+ "label": "Wrap",
42
+ "editor": { "tab": "layout" },
43
+ "type": "boolean",
44
+ "default": false,
45
+ "description": "Whether children wrap to next line"
46
+ },
47
+ {
48
+ "name": "reverse",
49
+ "label": "Reverse",
50
+ "editor": { "tab": "layout" },
51
+ "type": "boolean",
52
+ "default": false,
53
+ "description": "Reverse the stacking order"
54
+ },
55
+ {
56
+ "name": "minChildWidth",
57
+ "label": "Min child width",
58
+ "editor": { "tab": "layout" },
59
+ "type": "string",
60
+ "description": "Minimum child width (CSS value, e.g. '200px'). Only applies to horizontal stacks — injects a <style> with flex-basis/min-inline-size per child."
61
+ },
62
+ {
63
+ "name": "as",
64
+ "label": "Element",
65
+ "editor": { "tab": "settings" },
66
+ "type": "enum",
67
+ "enum": [
68
+ "div",
69
+ "section",
70
+ "article",
71
+ "aside",
72
+ "header",
73
+ "footer",
74
+ "main",
75
+ "nav",
76
+ "ul",
77
+ "ol",
78
+ "fieldset"
79
+ ],
80
+ "default": "div",
81
+ "description": "HTML tag to render"
82
+ }
83
+ ],
84
+ "events": [],
85
+ "slots": [{ "name": "default", "displayName": "Content" }],
86
+ "tokens": {
87
+ "gap": "--space-{gap} (via u-gap-*)",
88
+ "align": "u-items-{align}",
89
+ "direction": "u-vstack | u-hstack"
90
+ },
91
+ "spacing": {
92
+ "between-children": "--space-{gap} (friendship model — choose gap based on child relationship)"
93
+ },
94
+ "constraints": [
95
+ "Never use raw margin/padding between children — use gap prop",
96
+ "Use logical properties only (handled automatically via utility classes)",
97
+ "Choose gap value using the friendship spacing model"
98
+ ],
99
+ "a11y": [
100
+ "Uses semantic HTML via as prop — choose correct element for context",
101
+ "No ARIA attributes needed — layout is presentational"
102
+ ],
103
+ "ai": {
104
+ "synonyms": [
105
+ "flex column",
106
+ "flex row",
107
+ "vertical layout",
108
+ "horizontal layout",
109
+ "vstack",
110
+ "hstack"
111
+ ],
112
+ "avoid": [
113
+ "nested divs with margin — use <Stack> with gap prop",
114
+ "manual flexbox styling — use <Stack> component"
115
+ ],
116
+ "patterns": [
117
+ "Form field group: <Stack gap=\"'sm'\"><label>Name</label><input/></Stack>",
118
+ "Button row: <Stack direction=\"'horizontal'\" gap=\"'md'\" align=\"'center'\"><Button>Save</Button><Button variant=\"'ghost'\">Cancel</Button></Stack>",
119
+ "Page layout: <Stack gap=\"'xl'\" as=\"'main'\"><section>...</section></Stack>"
120
+ ]
121
+ },
122
+ "examples": [
123
+ {
124
+ "id": "vertical-stack",
125
+ "title": "Basic vertical stack",
126
+ "pithy": "<Stack gap=\"'md'\"><Text>Item 1</Text><Text>Item 2</Text></Stack>",
127
+ "assertions": [
128
+ "renders <div>",
129
+ "has u-vstack class",
130
+ "has u-gap-md class"
131
+ ]
132
+ },
133
+ {
134
+ "id": "horizontal-stack",
135
+ "title": "Horizontal button row",
136
+ "pithy": "<Stack direction=\"'horizontal'\" gap=\"'sm'\" align=\"'center'\"><Button>Save</Button><Button variant=\"'ghost'\">Cancel</Button></Stack>",
137
+ "assertions": [
138
+ "has u-hstack class",
139
+ "has u-gap-sm class",
140
+ "has u-items-center class"
141
+ ]
142
+ }
143
+ ]
144
+ }
@@ -0,0 +1,196 @@
1
+ {
2
+ "kind": "component",
3
+ "id": "ui.accordion",
4
+ "name": "Accordion",
5
+ "displayName": "Accordion",
6
+ "version": "0.1.0",
7
+ "level": "organism",
8
+ "domain": "content",
9
+ "bidi": true,
10
+ "description": "Disclosure list built on native <details>/<summary>. Keyboard and screen-reader correct with zero ARIA scaffolding and a zero-JS baseline. Items are passed as a typed array and/or projected as children. Optional single-open mode closes siblings when one item opens (progressive enhancement).",
11
+ "props": [
12
+ {
13
+ "name": "items",
14
+ "reactive": true,
15
+ "label": "Items",
16
+ "type": "AccordionItem[] | Signal<AccordionItem[]>",
17
+ "placeholder": [
18
+ {
19
+ "title": "What is this component?",
20
+ "content": "A reusable disclosure list. Each row is a native <details> element, so it works with the keyboard and assistive technology out of the box."
21
+ },
22
+ {
23
+ "title": "Do I need JavaScript?",
24
+ "content": "No. The base markup is fully usable without JavaScript. Single-open behavior is layered on as a progressive enhancement."
25
+ },
26
+ {
27
+ "title": "Can more than one item be open at once?",
28
+ "content": "Yes, in the default 'multi' mode. Switch to 'single' mode to close sibling items automatically when one opens."
29
+ },
30
+ {
31
+ "title": "How do I set an item open by default?",
32
+ "content": "Pass a defaultOpen index (or array of indices), or set open: true on an individual item."
33
+ }
34
+ ],
35
+ "description": "Array of disclosure items, each with a title and content. Titles and content may be plain strings or reactive accessors.",
36
+ "itemSchema": {
37
+ "title": {
38
+ "type": "string",
39
+ "required": true,
40
+ "description": "Trigger label rendered inside the native <summary>"
41
+ },
42
+ "content": {
43
+ "type": "string",
44
+ "required": true,
45
+ "description": "Panel body revealed when the item is open"
46
+ },
47
+ "open": {
48
+ "type": "boolean",
49
+ "description": "Force this item open on first render"
50
+ }
51
+ }
52
+ },
53
+ {
54
+ "name": "mode",
55
+ "label": "Mode",
56
+ "type": "enum",
57
+ "enum": ["single", "multi"],
58
+ "default": "multi",
59
+ "description": "'multi' (default) lets every item open independently — pure native behavior, zero JS. 'single' closes sibling items when one opens via a progressive-enhancement toggle listener; with JS disabled it still degrades to independent multi-open."
60
+ },
61
+ {
62
+ "name": "defaultOpen",
63
+ "label": "Default open",
64
+ "editor": { "tab": "settings" },
65
+ "type": "number | number[]",
66
+ "description": "Which item index(es) start open. Accepts a single index or an array of indices, applied at build time via the open attribute. In 'single' mode more than one index is clamped to the first."
67
+ },
68
+ {
69
+ "name": "id",
70
+ "label": "Anchor ID",
71
+ "editor": { "tab": "settings" },
72
+ "type": "string",
73
+ "description": "Anchor id on the .ui-accordion root, useful for deep-linking."
74
+ }
75
+ ],
76
+ "requiredProps": [],
77
+ "events": [],
78
+ "slots": [
79
+ {
80
+ "name": "items",
81
+ "displayName": "Items",
82
+ "description": "Projected disclosure content rendered after the array-driven items. Author raw <details> blocks here to mix bespoke rows with the items array; single-open mode manages them too.",
83
+ "accepts": ["ui.text", "ui.heading", "ui.card"]
84
+ }
85
+ ],
86
+ "tokens": {
87
+ "item.surface": "--color-surface + --border-width-hairline --color-border + --radius-md",
88
+ "item.border.hover": "color-mix(in oklch, --color-primary 30%, --color-border) on hover / focus-within",
89
+ "trigger.padding-block": "--space-sm",
90
+ "trigger.padding-inline": "--space-md",
91
+ "trigger.font": "--font-size-md + --font-weight-semibold, --color-text",
92
+ "trigger.hover": "a11y.state-background(hover, --color-primary)",
93
+ "trigger.focus": "a11y.focus-ring on :focus-visible",
94
+ "chevron": "rotated border corner (--border-width-thin currentColor), flips 180deg via rotate on [open]",
95
+ "panel.color": "--color-text-muted",
96
+ "panel.padding": "--space-md inline + block-end",
97
+ "gap.items": "--space-sm (Close Friends — items in a list)",
98
+ "gap.trigger": "--space-sm (title ↔ chevron)",
99
+ "motion.chevron": "rotate --motion-duration-normal --motion-easing-standard (motion-safe)",
100
+ "motion.border": "--motion-transition-border (motion-safe)"
101
+ },
102
+ "spacing": {
103
+ "items-gap": "--space-sm (Close Friends — items in a list)",
104
+ "trigger-inline": "--space-md (comfortable tap target inset)",
105
+ "title-to-chevron": "--space-sm (Close Friends — label and disclosure affordance)",
106
+ "panel-inset": "--space-md (Friends — the answer is a distinct block under the trigger)"
107
+ },
108
+ "designGuidance": {
109
+ "surface": "Each item is a bordered --color-surface card with a hairline and --radius-md. The whole list reads as a stack of peers separated by --space-sm. No shadows — depth comes from the border and the open/closed affordance, not elevation.",
110
+ "depth": "Flat by design. The only motion is the chevron rotation and a border-color tint on hover/focus-within, both motion-safe wrapped so prefers-reduced-motion zeroes them.",
111
+ "borders": "Hairline --color-border at rest; on hover or focus-within the border tints toward --color-primary (30% mix) to signal interactivity. Forced-colors mode draws a 1px ButtonText border.",
112
+ "interactivity": "Interaction is entirely native: <summary> is a real disclosure control with built-in Tab focus, Enter/Space activation, and correct AT semantics. Do NOT add role='button' or aria-expanded — the native elements already expose the right state. The custom chevron replaces the default triangle (list-style: none + ::-webkit-details-marker hidden) purely for visual polish.",
113
+ "bestPractices": [
114
+ "Reach for 'single' mode when answers are long and only one should occupy the viewport at a time (classic FAQ); use 'multi' when users compare answers side by side.",
115
+ "Keep titles short and scannable — they render as the always-visible trigger.",
116
+ "Set defaultOpen to surface the most common question, but avoid opening several items in 'single' mode (the component clamps to the first).",
117
+ "For rich answers (lists, links, embedded components) project custom <details> children instead of the plain-text content prop.",
118
+ "Never wrap the trigger text in a heading for each item — repeated headings distort document outline; the native <summary> already conveys structure."
119
+ ]
120
+ },
121
+ "constraints": [
122
+ "Each item renders a native <details> containing a <summary> (trigger) and a .ui-accordion-panel (content) — accessible and usable with zero JavaScript",
123
+ "No ARIA attributes are added — native <details>/<summary> semantics are correct on their own",
124
+ "Single-open mode is a progressive enhancement: a toggle listener on each <details> closes open siblings; without JS the list degrades to independent multi-open",
125
+ "defaultOpen sets the open attribute at build time; in 'single' mode more than one index is clamped to the first",
126
+ "All CSS uses logical properties (inline/block) — RTL-safe",
127
+ "All transitions (chevron rotate, border tint) are wrapped in @include a11y.motion-safe",
128
+ "Forced-colors mode renders a 1px ButtonText border and ButtonText chevron",
129
+ "destroy() removes every toggle listener, disposes every reactive text effect, and empties the root"
130
+ ],
131
+ "a11y": {
132
+ "landmarks": [
133
+ "No landmark role — the accordion is a plain grouping of native disclosures. Wrap it in a labeled <section> (see FaqSection) when it needs a region name."
134
+ ],
135
+ "keyboard": {
136
+ "trigger": "Native <summary> receives Tab / Shift+Tab focus and toggles on Enter or Space — no custom key handling.",
137
+ "singleOpen": "Closing siblings happens on the toggle event, so keyboard-driven opens behave identically to pointer opens."
138
+ },
139
+ "focus": {
140
+ "visible": ":focus-visible on the <summary> paints the design-system focus ring via a11y.focus-ring."
141
+ },
142
+ "aria": {
143
+ "root": "<div class='ui-accordion'>",
144
+ "note": "Intentionally no aria-expanded / role=button — native <details>/<summary> already expose expanded/collapsed state to assistive technology."
145
+ },
146
+ "forcedColors": "forced-colors mode renders a 1px ButtonText border on each item and a ButtonText chevron so state stays visible without color.",
147
+ "motion": {
148
+ "reducedMotion": "Chevron rotation and border transition are wrapped in @include a11y.motion-safe — prefers-reduced-motion zeroes them. WCAG 2.3.3 satisfied."
149
+ }
150
+ },
151
+ "ai": {
152
+ "synonyms": [
153
+ "accordion",
154
+ "disclosure",
155
+ "expander",
156
+ "collapsible",
157
+ "collapse",
158
+ "details",
159
+ "expandable list",
160
+ "toggle list",
161
+ "show more"
162
+ ],
163
+ "avoid": [
164
+ "<div> with role='button' + aria-expanded and manual click handlers — use native <details>/<summary> via <Accordion>",
165
+ "Hand-rolled height animations that trap content from assistive tech — the native disclosure toggles content correctly",
166
+ "Wrapping each trigger in a heading — distorts the document outline"
167
+ ],
168
+ "patterns": [
169
+ "Basic multi-open: <Accordion items=\"items\"/>",
170
+ "Single-open FAQ: <Accordion mode=\"'single'\" items=\"faqs\" defaultOpen=\"0\"/>",
171
+ "First item open: <Accordion items=\"items\" defaultOpen=\"0\"/>"
172
+ ]
173
+ },
174
+ "examples": [
175
+ {
176
+ "id": "basic-accordion",
177
+ "title": "Multi-open disclosure list",
178
+ "pithy": "<Accordion items=\"items\"/>",
179
+ "assertions": [
180
+ "renders a .ui-accordion root",
181
+ "one <details> per item",
182
+ "each <details> has a <summary> trigger",
183
+ "items open independently"
184
+ ]
185
+ },
186
+ {
187
+ "id": "single-open-accordion",
188
+ "title": "Single-open accordion",
189
+ "pithy": "<Accordion mode=\"'single'\" items=\"items\" defaultOpen=\"0\"/>",
190
+ "assertions": [
191
+ "first item starts open",
192
+ "opening one item closes the others"
193
+ ]
194
+ }
195
+ ]
196
+ }