@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.
- package/LICENSE +21 -0
- package/dist/specs/atoms/actions/button.pidl.json +289 -0
- package/dist/specs/atoms/actions/icon-group.pidl.json +141 -0
- package/dist/specs/atoms/actions/segmented-control.pidl.json +137 -0
- package/dist/specs/atoms/content/action-tile.pidl.json +135 -0
- package/dist/specs/atoms/content/badge.pidl.json +189 -0
- package/dist/specs/atoms/content/callout.pidl.json +170 -0
- package/dist/specs/atoms/content/code-block.pidl.json +170 -0
- package/dist/specs/atoms/content/heading.pidl.json +175 -0
- package/dist/specs/atoms/content/icon.pidl.json +127 -0
- package/dist/specs/atoms/content/image.pidl.json +227 -0
- package/dist/specs/atoms/content/prose.pidl.json +183 -0
- package/dist/specs/atoms/content/stat.pidl.json +158 -0
- package/dist/specs/atoms/content/tabs.pidl.json +207 -0
- package/dist/specs/atoms/content/text.pidl.json +212 -0
- package/dist/specs/atoms/content/tile.pidl.json +156 -0
- package/dist/specs/atoms/data/donut.pidl.json +352 -0
- package/dist/specs/atoms/data/gauge.pidl.json +274 -0
- package/dist/specs/atoms/forms/checkbox-group.pidl.json +170 -0
- package/dist/specs/atoms/forms/checkbox.pidl.json +323 -0
- package/dist/specs/atoms/forms/file-picker.pidl.json +161 -0
- package/dist/specs/atoms/forms/form-field.pidl.json +106 -0
- package/dist/specs/atoms/forms/input.pidl.json +466 -0
- package/dist/specs/atoms/forms/radio-group.pidl.json +174 -0
- package/dist/specs/atoms/forms/radio.pidl.json +312 -0
- package/dist/specs/atoms/forms/search-input.pidl.json +265 -0
- package/dist/specs/atoms/forms/select.pidl.json +420 -0
- package/dist/specs/atoms/forms/textarea.pidl.json +422 -0
- package/dist/specs/atoms/forms/toggle.pidl.json +314 -0
- package/dist/specs/layout/grid-item.pidl.json +120 -0
- package/dist/specs/layout/grid.pidl.json +157 -0
- package/dist/specs/layout/page-section.pidl.json +105 -0
- package/dist/specs/layout/smart-container.pidl.json +148 -0
- package/dist/specs/layout/stack.pidl.json +144 -0
- package/dist/specs/organisms/content/accordion.pidl.json +196 -0
- package/dist/specs/organisms/content/card.pidl.json +283 -0
- package/dist/specs/organisms/feedback/alert-dialog.pidl.json +158 -0
- package/dist/specs/organisms/feedback/modal.pidl.json +133 -0
- package/dist/specs/organisms/feedback/toast.pidl.json +150 -0
- package/dist/specs/organisms/forms/dynamic-form.pidl.json +314 -0
- package/dist/specs/organisms/forms/form.pidl.json +135 -0
- package/dist/specs/organisms/forms/subscribe-form.pidl.json +188 -0
- package/dist/specs/organisms/navigation/command-palette.pidl.json +263 -0
- package/dist/specs/organisms/navigation/docs-sidebar.pidl.json +264 -0
- package/dist/specs/organisms/navigation/navbar.pidl.json +356 -0
- package/dist/specs/organisms/navigation/table-of-contents.pidl.json +197 -0
- package/dist/specs/sections/contact-section.pidl.json +183 -0
- package/dist/specs/sections/cta-section.pidl.json +168 -0
- package/dist/specs/sections/faq-section.pidl.json +202 -0
- package/dist/specs/sections/features-section.pidl.json +214 -0
- package/dist/specs/sections/footer-section.pidl.json +187 -0
- package/dist/specs/sections/hero-section.pidl.json +202 -0
- package/dist/src/borders-psychology.d.ts +17 -0
- package/dist/src/borders-psychology.d.ts.map +1 -0
- package/dist/src/borders-psychology.js +42 -0
- package/dist/src/borders-psychology.js.map +1 -0
- package/dist/src/color-psychology.d.ts +20 -0
- package/dist/src/color-psychology.d.ts.map +1 -0
- package/dist/src/color-psychology.js +57 -0
- package/dist/src/color-psychology.js.map +1 -0
- package/dist/src/design-intelligence.d.ts +13 -0
- package/dist/src/design-intelligence.d.ts.map +1 -0
- package/dist/src/design-intelligence.js +36 -0
- package/dist/src/design-intelligence.js.map +1 -0
- package/dist/src/index.d.ts +6238 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +105 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/motion-psychology.d.ts +19 -0
- package/dist/src/motion-psychology.d.ts.map +1 -0
- package/dist/src/motion-psychology.js +47 -0
- package/dist/src/motion-psychology.js.map +1 -0
- package/dist/src/mutation-vocabulary.d.ts +13 -0
- package/dist/src/mutation-vocabulary.d.ts.map +1 -0
- package/dist/src/mutation-vocabulary.js +67 -0
- package/dist/src/mutation-vocabulary.js.map +1 -0
- package/dist/src/radius-psychology.d.ts +17 -0
- package/dist/src/radius-psychology.d.ts.map +1 -0
- package/dist/src/radius-psychology.js +42 -0
- package/dist/src/radius-psychology.js.map +1 -0
- package/dist/src/shadows-psychology.d.ts +17 -0
- package/dist/src/shadows-psychology.d.ts.map +1 -0
- package/dist/src/shadows-psychology.js +44 -0
- package/dist/src/shadows-psychology.js.map +1 -0
- package/dist/src/spacing-psychology.d.ts +22 -0
- package/dist/src/spacing-psychology.d.ts.map +1 -0
- package/dist/src/spacing-psychology.js +59 -0
- package/dist/src/spacing-psychology.js.map +1 -0
- package/dist/src/typography-psychology.d.ts +22 -0
- package/dist/src/typography-psychology.d.ts.map +1 -0
- package/dist/src/typography-psychology.js +68 -0
- package/dist/src/typography-psychology.js.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "component",
|
|
3
|
+
"id": "ui.code-block",
|
|
4
|
+
"name": "CodeBlock",
|
|
5
|
+
"displayName": "Code Block",
|
|
6
|
+
"version": "0.1.0",
|
|
7
|
+
"level": "atom",
|
|
8
|
+
"domain": "content",
|
|
9
|
+
"bidi": false,
|
|
10
|
+
"description": "Syntax-highlighted code display for documentation pages. Renders a plaintext <pre><code> immediately (no layout shift), then lazy-loads Shiki and swaps in dual-theme highlighted markup. Supports 'Copy' and 'Copy as AI prompt' actions via ui-kit Button components.",
|
|
11
|
+
"props": [
|
|
12
|
+
{
|
|
13
|
+
"name": "code",
|
|
14
|
+
"label": "Code",
|
|
15
|
+
"type": "string",
|
|
16
|
+
"required": true,
|
|
17
|
+
"placeholder": "const x = 1;",
|
|
18
|
+
"description": "Source code to display. Rendered HTML-escaped in the synchronous fallback; passed to Shiki for highlighted output."
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "lang",
|
|
22
|
+
"label": "Language",
|
|
23
|
+
"type": "string",
|
|
24
|
+
"required": false,
|
|
25
|
+
"default": "text",
|
|
26
|
+
"placeholder": "typescript",
|
|
27
|
+
"description": "Language identifier for Shiki syntax highlighting (e.g. 'typescript', 'css', 'bash'). Falls back to plain text when omitted or unknown."
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"name": "filename",
|
|
31
|
+
"label": "Filename",
|
|
32
|
+
"type": "string",
|
|
33
|
+
"required": false,
|
|
34
|
+
"placeholder": "index.ts",
|
|
35
|
+
"description": "Filename shown in the header bar. Falls back to the lang value when omitted."
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "highlightLines",
|
|
39
|
+
"label": "Highlight lines",
|
|
40
|
+
"type": "string",
|
|
41
|
+
"required": false,
|
|
42
|
+
"placeholder": "1,3-5",
|
|
43
|
+
"description": "Line numbers to highlight, e.g. '1,3-5'. Parsed into a Set<number> and forwarded to Shiki's line transformer. Has no effect until Shiki loads."
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"name": "showCopy",
|
|
47
|
+
"label": "Show copy button",
|
|
48
|
+
"type": "boolean",
|
|
49
|
+
"required": false,
|
|
50
|
+
"default": true,
|
|
51
|
+
"description": "Show the 'Copy code' button in the header."
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"name": "showCopyAsPrompt",
|
|
55
|
+
"label": "Show 'Copy as AI prompt' button",
|
|
56
|
+
"type": "boolean",
|
|
57
|
+
"required": false,
|
|
58
|
+
"default": true,
|
|
59
|
+
"description": "Show the 'Copy as AI prompt' button, which wraps the code in a markdown code fence prefixed by promptPrefix."
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"name": "promptPrefix",
|
|
63
|
+
"label": "AI prompt prefix",
|
|
64
|
+
"type": "string",
|
|
65
|
+
"required": false,
|
|
66
|
+
"default": "Explain this code:",
|
|
67
|
+
"placeholder": "Explain this code:",
|
|
68
|
+
"description": "Text prepended before the markdown-fenced code block when 'Copy as AI prompt' is clicked."
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"name": "ariaLabel",
|
|
72
|
+
"label": "ARIA label",
|
|
73
|
+
"editor": { "tab": "settings" },
|
|
74
|
+
"type": "string",
|
|
75
|
+
"required": false,
|
|
76
|
+
"description": "Accessible label forwarded as aria-label on the root element. Recommended when the visible filename/lang label is insufficient context."
|
|
77
|
+
}
|
|
78
|
+
],
|
|
79
|
+
"tokens": {
|
|
80
|
+
"color": "--color-surface-variant (body bg), --color-surface-dim (header bg), --color-border, --color-text, --color-text-muted, --color-primary (line highlight tint)",
|
|
81
|
+
"spacing": "--space-xs (header padding, action gap), --space-sm (code padding)",
|
|
82
|
+
"radius": "--radius-md (root border-radius)",
|
|
83
|
+
"size": "--font-size-sm (code body), --font-size-xs (header label)",
|
|
84
|
+
"border": "--border-width-thin (outer border), --border-width-hairline (header divider)",
|
|
85
|
+
"motion": "--motion-transition-background"
|
|
86
|
+
},
|
|
87
|
+
"spacing": {
|
|
88
|
+
"header-actions-gap": "--space-xs (lovers — icon buttons beside each other)"
|
|
89
|
+
},
|
|
90
|
+
"constraints": [
|
|
91
|
+
"code is required — renders an empty <pre><code></code></pre> when code is an empty string",
|
|
92
|
+
"Shiki is loaded via dynamic import and module-scope memoised — one highlighter shared across all CodeBlock instances on the page",
|
|
93
|
+
"highlightLines applies only after Shiki resolves; the synchronous plaintext fallback shows no highlights",
|
|
94
|
+
"Both Copy buttons compose <Button variant='ghost' size='sm'> — no raw <button> elements",
|
|
95
|
+
"The language label composes <Text> — no raw <span> elements",
|
|
96
|
+
"Light/dark theme switching is CSS-only via [data-theme='dark'] on the root element — no JS event subscription"
|
|
97
|
+
],
|
|
98
|
+
"a11y": [
|
|
99
|
+
"Root element accepts an aria-label for the code block as a whole",
|
|
100
|
+
"Header label uses <Text as='span'> with color='text-muted'",
|
|
101
|
+
"Copy buttons include reactive ariaLabel: 'Copy code' → 'Copied to clipboard' after click",
|
|
102
|
+
"An aria-live='polite' region (class ui-code-block-live) announces 'Copied to clipboard!' and 'Copied as AI prompt!' to screen readers",
|
|
103
|
+
"Forced-colors: outer border set to ButtonText; header divider set to ButtonText"
|
|
104
|
+
],
|
|
105
|
+
"ai": {
|
|
106
|
+
"synonyms": [
|
|
107
|
+
"code block",
|
|
108
|
+
"syntax highlight",
|
|
109
|
+
"code snippet",
|
|
110
|
+
"code viewer",
|
|
111
|
+
"code display",
|
|
112
|
+
"code example",
|
|
113
|
+
"code sample"
|
|
114
|
+
],
|
|
115
|
+
"avoid": [
|
|
116
|
+
"a bare <pre><code> with manually applied highlight.js or Prism — use <CodeBlock> which provides copy buttons, a11y live regions, and design-system token integration",
|
|
117
|
+
"hand-rolling copy buttons as raw <button> elements — CodeBlock's buttons compose from ui-kit <Button>"
|
|
118
|
+
],
|
|
119
|
+
"patterns": [
|
|
120
|
+
"Basic: <CodeBlock code=\"'const x = 1;'\" lang=\"'typescript'\" />",
|
|
121
|
+
"With filename: <CodeBlock code=\"src\" lang=\"'tsx'\" filename=\"'App.tsx'\" />",
|
|
122
|
+
"Highlighted lines: <CodeBlock code=\"src\" lang=\"'css'\" highlightLines=\"'2,4-6'\" />",
|
|
123
|
+
"Copy only, no prompt button: <CodeBlock code=\"src\" lang=\"'bash'\" showCopyAsPrompt=\"false\" />",
|
|
124
|
+
"No copy buttons: <CodeBlock code=\"src\" lang=\"'json'\" showCopy=\"false\" showCopyAsPrompt=\"false\" />"
|
|
125
|
+
]
|
|
126
|
+
},
|
|
127
|
+
"designGuidance": {
|
|
128
|
+
"surface": "Body uses --color-surface-variant; header uses --color-surface-dim — a subtle step darker. Together they read as one card-like container.",
|
|
129
|
+
"depth": "Thin border (--border-width-thin) defines the container boundary. No shadow — code blocks are content, not elevated UI.",
|
|
130
|
+
"borders": "Outer radius --radius-md; header divided from body by a hairline border in --color-border.",
|
|
131
|
+
"interactivity": "Copy buttons are ghost-variant — they disappear into the header and only become visible on hover/focus. Transient 'Copied!' label resets after 2 seconds.",
|
|
132
|
+
"darkMode": "Shiki dual-theme: --shiki-light and --shiki-dark CSS variables applied per token. Switching is purely CSS via [data-theme='dark'] — no re-render.",
|
|
133
|
+
"bestPractices": "Use ariaLabel when the code block is the primary content of a section, or when there are multiple code blocks on the same page with similar lang/filename labels."
|
|
134
|
+
},
|
|
135
|
+
"examples": [
|
|
136
|
+
{
|
|
137
|
+
"id": "default",
|
|
138
|
+
"title": "Basic TypeScript block",
|
|
139
|
+
"pithy": "<CodeBlock code=\"'const x: number = 1;'\" lang=\"'typescript'\" />",
|
|
140
|
+
"assertions": [
|
|
141
|
+
"renders <div class='ui-code-block'>",
|
|
142
|
+
"contains <pre class='ui-code-block-pre'>",
|
|
143
|
+
"header contains 'typescript'"
|
|
144
|
+
]
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"id": "with-filename",
|
|
148
|
+
"title": "With filename",
|
|
149
|
+
"pithy": "<CodeBlock code=\"'export default function App() {}'\" lang=\"'typescript'\" filename=\"'App.ts'\" />",
|
|
150
|
+
"assertions": [
|
|
151
|
+
"header contains 'App.ts'",
|
|
152
|
+
"does not contain 'typescript' as separate label"
|
|
153
|
+
]
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"id": "no-copy",
|
|
157
|
+
"title": "Read-only (no copy buttons)",
|
|
158
|
+
"pithy": "<CodeBlock code=\"'Hello, World!'\" lang=\"'text'\" showCopy=\"false\" showCopyAsPrompt=\"false\" />",
|
|
159
|
+
"assertions": ["no .ui-button elements rendered"]
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
"id": "highlight-lines",
|
|
163
|
+
"title": "With line highlights",
|
|
164
|
+
"pithy": "<CodeBlock code=\"src\" lang=\"'javascript'\" highlightLines=\"'1,3-5'\" />",
|
|
165
|
+
"assertions": [
|
|
166
|
+
"data-highlighted on lines 1, 3, 4, 5 (after Shiki resolves)"
|
|
167
|
+
]
|
|
168
|
+
}
|
|
169
|
+
]
|
|
170
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "component",
|
|
3
|
+
"id": "ui.heading",
|
|
4
|
+
"name": "Heading",
|
|
5
|
+
"displayName": "Heading",
|
|
6
|
+
"version": "0.1.0",
|
|
7
|
+
"level": "atom",
|
|
8
|
+
"domain": "content",
|
|
9
|
+
"bidi": true,
|
|
10
|
+
"description": "Semantic heading component for page titles and section headers. Renders the correct <h1>-<h6> tag based on level prop. Size and weight can be overridden independently of heading level.",
|
|
11
|
+
"props": [
|
|
12
|
+
{
|
|
13
|
+
"name": "text",
|
|
14
|
+
"reactive": true,
|
|
15
|
+
"label": "Text",
|
|
16
|
+
"type": "string | Signal<string>",
|
|
17
|
+
"placeholder": "Your heading",
|
|
18
|
+
"description": "Visible heading text. Rendered as a text node inside the h1-h6 tag. When omitted, the component falls back to slot content."
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "level",
|
|
22
|
+
"label": "Level",
|
|
23
|
+
"editor": { "tab": "settings" },
|
|
24
|
+
"type": "number",
|
|
25
|
+
"enum": [1, 2, 3, 4, 5, 6],
|
|
26
|
+
"default": 2,
|
|
27
|
+
"description": "Heading level — determines the HTML tag <h1>-<h6>. Non-integers are rounded, out-of-range values are clamped to 1-6."
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"name": "size",
|
|
31
|
+
"label": "Size",
|
|
32
|
+
"editor": { "type": "token-font" },
|
|
33
|
+
"type": "enum",
|
|
34
|
+
"enum": ["xs", "sm", "base", "lg", "xl", "2xl", "3xl", "4xl"],
|
|
35
|
+
"description": "Font size override — maps to u-text-* utilities. When omitted, uses native heading level size from browser defaults + typography scale."
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "weight",
|
|
39
|
+
"label": "Weight",
|
|
40
|
+
"type": "enum",
|
|
41
|
+
"enum": [
|
|
42
|
+
"thin",
|
|
43
|
+
"light",
|
|
44
|
+
"normal",
|
|
45
|
+
"medium",
|
|
46
|
+
"semibold",
|
|
47
|
+
"bold",
|
|
48
|
+
"extrabold"
|
|
49
|
+
],
|
|
50
|
+
"description": "Font weight override — maps to u-font-* utilities. When omitted, uses native heading weight (semibold)."
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"name": "color",
|
|
54
|
+
"label": "Color",
|
|
55
|
+
"editor": { "type": "token-color" },
|
|
56
|
+
"type": "enum",
|
|
57
|
+
"enum": [
|
|
58
|
+
"text",
|
|
59
|
+
"text-muted",
|
|
60
|
+
"text-inverse",
|
|
61
|
+
"primary",
|
|
62
|
+
"secondary",
|
|
63
|
+
"tertiary"
|
|
64
|
+
],
|
|
65
|
+
"default": "text",
|
|
66
|
+
"description": "Text color — maps to --color-* design tokens. Status colors (success/info/warning/danger) are intentionally excluded — those belong on Badge/Button/Icon where the color carries semantic meaning, not on plain text."
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"name": "align",
|
|
70
|
+
"label": "Align",
|
|
71
|
+
"type": "enum",
|
|
72
|
+
"enum": ["start", "center", "end", "justify"],
|
|
73
|
+
"description": "Text alignment — uses logical values (start/end, not left/right) for RTL support"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"name": "truncate",
|
|
77
|
+
"label": "Truncate",
|
|
78
|
+
"editor": { "tab": "layout", "type": "number" },
|
|
79
|
+
"type": "boolean | number",
|
|
80
|
+
"default": false,
|
|
81
|
+
"description": "Truncation — true for single-line ellipsis, number (1-4) for line-clamp"
|
|
82
|
+
}
|
|
83
|
+
],
|
|
84
|
+
"events": [],
|
|
85
|
+
"tokens": {
|
|
86
|
+
"color.default": "--color-text",
|
|
87
|
+
"color.muted": "--color-text-muted",
|
|
88
|
+
"color.inverse": "--color-text-inverse",
|
|
89
|
+
"color.brand": "--color-{primary|secondary|tertiary}",
|
|
90
|
+
"color.status": "--color-{success|info|warning|danger}",
|
|
91
|
+
"font-size": "--font-size-{size} (via u-text-* utility, optional override)",
|
|
92
|
+
"font-weight": "--font-weight-{weight} (via u-font-* utility, optional override)"
|
|
93
|
+
},
|
|
94
|
+
"spacing": {
|
|
95
|
+
"internal": "margin: 0 (reset) — external spacing controlled by parent layout",
|
|
96
|
+
"heading-to-content": "--space-sm (close friends — heading and its content are tightly coupled)",
|
|
97
|
+
"heading-to-heading": "--space-xs (lovers — main heading and subheading)",
|
|
98
|
+
"section-heading": "--space-lg (acquaintances — heading introducing a new section)"
|
|
99
|
+
},
|
|
100
|
+
"constraints": [
|
|
101
|
+
"Never skip heading levels — go from h1 to h2, not h1 to h3",
|
|
102
|
+
"Never use raw hex colors — use color prop with design token values",
|
|
103
|
+
"Never use physical CSS properties — alignment uses logical values (start/end, not left/right)",
|
|
104
|
+
"Use level for document hierarchy and size for visual override — they are independent",
|
|
105
|
+
"Must handle forced-colors mode — color: CanvasText",
|
|
106
|
+
"Must handle high-contrast mode — bump text-muted to full text color"
|
|
107
|
+
],
|
|
108
|
+
"a11y": [
|
|
109
|
+
"Renders correct <h1>-<h6> tag — critical for document outline and screen reader navigation",
|
|
110
|
+
"Never skip heading levels in the document hierarchy",
|
|
111
|
+
"Visual size can differ from semantic level via the size prop",
|
|
112
|
+
"Forced-colors: uses CanvasText for visibility",
|
|
113
|
+
"High-contrast: text-muted upgraded to full text color"
|
|
114
|
+
],
|
|
115
|
+
"ai": {
|
|
116
|
+
"synonyms": [
|
|
117
|
+
"title",
|
|
118
|
+
"header",
|
|
119
|
+
"section title",
|
|
120
|
+
"page title",
|
|
121
|
+
"heading text",
|
|
122
|
+
"h1",
|
|
123
|
+
"h2",
|
|
124
|
+
"h3"
|
|
125
|
+
],
|
|
126
|
+
"avoid": [
|
|
127
|
+
"div with font-size for headings — use <Heading> with correct level",
|
|
128
|
+
"skipping heading levels (h1 to h3) — always use consecutive levels",
|
|
129
|
+
"using level for visual size — use size prop for visual override",
|
|
130
|
+
"hardcoded font sizes or weights — use size/weight props"
|
|
131
|
+
],
|
|
132
|
+
"patterns": [
|
|
133
|
+
"Page title: <Heading level=\"1\">Welcome</Heading>",
|
|
134
|
+
"Section heading: <Heading level=\"2\" color=\"'primary'\">Features</Heading>",
|
|
135
|
+
"Visually large h3: <Heading level=\"3\" size=\"'2xl'\">Big Section</Heading>",
|
|
136
|
+
"Centered subtitle: <Heading level=\"2\" size=\"'lg'\" align=\"'center'\" color=\"'text-muted'\">Our Mission</Heading>",
|
|
137
|
+
"Card title: <Heading level=\"3\" size=\"'md'\" weight=\"'bold'\">Card Title</Heading>"
|
|
138
|
+
]
|
|
139
|
+
},
|
|
140
|
+
"examples": [
|
|
141
|
+
{
|
|
142
|
+
"id": "page-title",
|
|
143
|
+
"title": "Page title (h1)",
|
|
144
|
+
"pithy": "<Heading level=\"1\">Welcome to PithyJS</Heading>",
|
|
145
|
+
"assertions": [
|
|
146
|
+
"renders <h1> element",
|
|
147
|
+
"has class ui-heading",
|
|
148
|
+
"data-color='text'"
|
|
149
|
+
]
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"id": "styled-section",
|
|
153
|
+
"title": "Styled section heading",
|
|
154
|
+
"pithy": "<Heading level=\"2\" size=\"'xl'\" color=\"'primary'\" align=\"'center'\">Features</Heading>",
|
|
155
|
+
"assertions": [
|
|
156
|
+
"renders <h2>",
|
|
157
|
+
"has u-text-xl class",
|
|
158
|
+
"data-color='primary'",
|
|
159
|
+
"data-align='center'"
|
|
160
|
+
]
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"id": "visual-override",
|
|
164
|
+
"title": "Visual size independent of level",
|
|
165
|
+
"pithy": "<Heading level=\"4\" size=\"'2xl'\">Visually Large h4</Heading>",
|
|
166
|
+
"assertions": ["renders <h4> element", "has u-text-2xl class"]
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"id": "truncated-heading",
|
|
170
|
+
"title": "Truncated heading",
|
|
171
|
+
"pithy": "<Heading level=\"3\" truncate>Very Long Heading That Truncates</Heading>",
|
|
172
|
+
"assertions": ["has u-truncate class"]
|
|
173
|
+
}
|
|
174
|
+
]
|
|
175
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "component",
|
|
3
|
+
"id": "ui.icon",
|
|
4
|
+
"name": "Icon",
|
|
5
|
+
"displayName": "Icon",
|
|
6
|
+
"version": "0.1.0",
|
|
7
|
+
"level": "atom",
|
|
8
|
+
"domain": "content",
|
|
9
|
+
"bidi": true,
|
|
10
|
+
"description": "Inline icon wrapper with accessible sizing and labelling. Renders children (inline SVG or icon markup) inside a <span> with design-system icon size tokens and proper ARIA attributes. Discriminated union enforces label when not decorative.",
|
|
11
|
+
"props": [
|
|
12
|
+
{
|
|
13
|
+
"name": "name",
|
|
14
|
+
"reactive": true,
|
|
15
|
+
"label": "Icon name",
|
|
16
|
+
"editor": { "type": "icon" },
|
|
17
|
+
"placeholder": "sparkles",
|
|
18
|
+
"type": "string | Signal<string>",
|
|
19
|
+
"required": false,
|
|
20
|
+
"description": "Icon identifier — sets data-icon attribute and resolves SVG from sprite registry. Pass a function (e.g. () => signal()) for reactive icon swapping; the SVG re-mounts only when the resolved name changes."
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"name": "size",
|
|
24
|
+
"label": "Size",
|
|
25
|
+
"type": "enum",
|
|
26
|
+
"enum": ["xs", "sm", "md", "lg", "xl", "xxl"],
|
|
27
|
+
"default": "md",
|
|
28
|
+
"description": "Icon size — maps to --icon-size-* design tokens via u-icon-* utility classes"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"name": "color",
|
|
32
|
+
"label": "Color",
|
|
33
|
+
"editor": { "type": "token-color" },
|
|
34
|
+
"type": "enum",
|
|
35
|
+
"enum": [
|
|
36
|
+
"text",
|
|
37
|
+
"text-muted",
|
|
38
|
+
"text-inverse",
|
|
39
|
+
"primary",
|
|
40
|
+
"secondary",
|
|
41
|
+
"tertiary",
|
|
42
|
+
"success",
|
|
43
|
+
"info",
|
|
44
|
+
"warning",
|
|
45
|
+
"danger"
|
|
46
|
+
],
|
|
47
|
+
"description": "Icon color — maps to --color-* design tokens. Defaults to undefined (inherits via currentColor)."
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"name": "decorative",
|
|
51
|
+
"label": "Decorative",
|
|
52
|
+
"editor": { "tab": "settings" },
|
|
53
|
+
"type": "boolean",
|
|
54
|
+
"default": false,
|
|
55
|
+
"description": "Whether the icon is purely decorative — hides from assistive tech when true"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"name": "label",
|
|
59
|
+
"label": "Label",
|
|
60
|
+
"editor": { "tab": "settings" },
|
|
61
|
+
"type": "string",
|
|
62
|
+
"description": "Accessible label — required when decorative is false. TypeScript enforces via discriminated union."
|
|
63
|
+
}
|
|
64
|
+
],
|
|
65
|
+
"events": [],
|
|
66
|
+
"tokens": {
|
|
67
|
+
"size": "--icon-size-{size} (via u-icon-{size})",
|
|
68
|
+
"color": "--color-{color} (via data-color attribute)"
|
|
69
|
+
},
|
|
70
|
+
"spacing": {
|
|
71
|
+
"icon-to-label": "--space-xs (lovers — icon and label are inseparable)"
|
|
72
|
+
},
|
|
73
|
+
"constraints": [
|
|
74
|
+
"label is required when decorative is false — enforced at TypeScript level via discriminated union",
|
|
75
|
+
"color defaults to undefined (inherits currentColor) — differs from Text/Heading which default to 'text'",
|
|
76
|
+
"name sets data-icon attribute and resolves SVG from icon sprite registry via <svg><use href='sprite.svg#name'/></svg> when no children are provided (this is the runtime output; static export rewrites it post-hoc to a document-local <use href='#pithy-icon-name'> and inlines the symbol, so the exported markup differs from what the component emits)",
|
|
77
|
+
"Children take precedence over registry (explicit inline SVG always wins over sprite resolution); reactive name is ignored when children are present",
|
|
78
|
+
"Reactive name (function) only re-mounts the SVG when the resolved name actually changes; identical re-runs are a no-op. The effect is disposed on destroy().",
|
|
79
|
+
"Missing icon names render a placeholder (dashed circle + '?') with data-icon-missing attribute and DEV console warning"
|
|
80
|
+
],
|
|
81
|
+
"a11y": [
|
|
82
|
+
"Semantic icons: role='img' + aria-label from label prop",
|
|
83
|
+
"Decorative icons: aria-hidden='true' + role='none' — completely hidden from assistive tech",
|
|
84
|
+
"If decorative=false and label is empty, role='img' is intentionally skipped to avoid broken ARIA state",
|
|
85
|
+
"DEV warning when semantic icon missing label, or decorative icon has unused label"
|
|
86
|
+
],
|
|
87
|
+
"ai": {
|
|
88
|
+
"synonyms": ["icon", "svg icon", "inline icon", "glyph", "symbol"],
|
|
89
|
+
"avoid": [
|
|
90
|
+
"img tag for icons — use <Icon> with inline SVG",
|
|
91
|
+
"aria-hidden on semantic icons — use decorative prop",
|
|
92
|
+
"hardcoded icon sizes — use size prop"
|
|
93
|
+
],
|
|
94
|
+
"patterns": [
|
|
95
|
+
"Semantic icon (inline SVG): <Icon label=\"'Search'\" size=\"'lg'\"><svg>...</svg></Icon>",
|
|
96
|
+
"Semantic icon (sprite): <Icon label=\"'Search'\" name=\"'search'\" size=\"'lg'\" />",
|
|
97
|
+
"Decorative icon: <Icon decorative size=\"'sm'\" name=\"'star'\" />",
|
|
98
|
+
"Colored icon: <Icon label=\"'Warning'\" color=\"'warning'\" name=\"'warning'\" />",
|
|
99
|
+
"Reactive icon (theme toggle): <Icon decorative name=\"() => isDark() ? 'sun' : 'moon'\" />"
|
|
100
|
+
]
|
|
101
|
+
},
|
|
102
|
+
"examples": [
|
|
103
|
+
{
|
|
104
|
+
"id": "semantic",
|
|
105
|
+
"title": "Semantic icon with label",
|
|
106
|
+
"pithy": "<Icon label=\"'Search'\"><svg viewBox=\"0 0 24 24\"><path d=\"M...\"/></svg></Icon>",
|
|
107
|
+
"assertions": ["role='img'", "aria-label='Search'", "has ui-icon class"]
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"id": "decorative",
|
|
111
|
+
"title": "Decorative icon",
|
|
112
|
+
"pithy": "<Icon decorative size=\"'sm'\"><svg viewBox=\"0 0 24 24\"><path d=\"M...\"/></svg></Icon>",
|
|
113
|
+
"assertions": ["aria-hidden='true'", "role='none'", "has u-icon-sm class"]
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"id": "reactive-name",
|
|
117
|
+
"title": "Reactive icon driven by a signal",
|
|
118
|
+
"pithy": "<Icon decorative name=\"() => mode() === 'dark' ? 'sun' : 'moon'\" />",
|
|
119
|
+
"assertions": [
|
|
120
|
+
"data-icon swaps when signal changes",
|
|
121
|
+
"<use href> updates to '#sun' or '#moon'",
|
|
122
|
+
"no SVG rebuild when signal fires with same value",
|
|
123
|
+
"effect disposed on destroy()"
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
]
|
|
127
|
+
}
|