@fragments-sdk/ui 0.17.0 → 0.18.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 (84) hide show
  1. package/dist/assets/ui.css +320 -183
  2. package/dist/components/Header/Header.module.scss.cjs +42 -21
  3. package/dist/components/Header/Header.module.scss.js +42 -21
  4. package/dist/components/Header/index.cjs +121 -3
  5. package/dist/components/Header/index.d.ts +26 -3
  6. package/dist/components/Header/index.d.ts.map +1 -1
  7. package/dist/components/Header/index.js +122 -4
  8. package/dist/components/Sidebar/Sidebar.module.scss.cjs +42 -42
  9. package/dist/components/Sidebar/Sidebar.module.scss.js +42 -42
  10. package/dist/index.d.ts +1 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/fragments.json +1 -1
  13. package/package.json +3 -3
  14. package/src/components/Accordion/Accordion.contract.json +169 -0
  15. package/src/components/Alert/Alert.contract.json +157 -0
  16. package/src/components/AppShell/AppShell.contract.json +155 -0
  17. package/src/components/Avatar/Avatar.contract.json +189 -0
  18. package/src/components/Badge/Badge.contract.json +187 -0
  19. package/src/components/BentoGrid/BentoGrid.contract.json +135 -0
  20. package/src/components/Box/Box.contract.json +423 -0
  21. package/src/components/Breadcrumbs/Breadcrumbs.contract.json +143 -0
  22. package/src/components/Button/Button.contract.json +205 -0
  23. package/src/components/ButtonGroup/ButtonGroup.contract.json +140 -0
  24. package/src/components/Card/Card.contract.json +185 -0
  25. package/src/components/Chart/Chart.contract.json +129 -0
  26. package/src/components/Checkbox/Checkbox.contract.json +246 -0
  27. package/src/components/Chip/Chip.contract.json +212 -0
  28. package/src/components/CodeBlock/CodeBlock.contract.json +388 -0
  29. package/src/components/Collapsible/Collapsible.contract.json +154 -0
  30. package/src/components/ColorPicker/ColorPicker.contract.json +212 -0
  31. package/src/components/Combobox/Combobox.contract.json +297 -0
  32. package/src/components/Command/Command.contract.json +165 -0
  33. package/src/components/ConversationList/ConversationList.contract.json +151 -0
  34. package/src/components/DataTable/DataTable.contract.json +302 -0
  35. package/src/components/DatePicker/DatePicker.contract.json +288 -0
  36. package/src/components/Dialog/Dialog.contract.json +159 -0
  37. package/src/components/Drawer/Drawer.contract.json +160 -0
  38. package/src/components/Editor/Editor.contract.json +263 -0
  39. package/src/components/EmptyState/EmptyState.contract.json +133 -0
  40. package/src/components/Field/Field.contract.json +157 -0
  41. package/src/components/Fieldset/Fieldset.contract.json +117 -0
  42. package/src/components/Form/Form.contract.json +145 -0
  43. package/src/components/Grid/Grid.contract.json +195 -0
  44. package/src/components/Header/Header.contract.json +194 -0
  45. package/src/components/Header/Header.module.scss +99 -0
  46. package/src/components/Header/index.tsx +191 -10
  47. package/src/components/Icon/Icon.contract.json +194 -0
  48. package/src/components/Image/Image.contract.json +209 -0
  49. package/src/components/Input/Input.contract.json +344 -0
  50. package/src/components/Link/Link.contract.json +180 -0
  51. package/src/components/List/List.contract.json +154 -0
  52. package/src/components/Listbox/Listbox.contract.json +158 -0
  53. package/src/components/Loading/Loading.contract.json +167 -0
  54. package/src/components/Markdown/Markdown.contract.json +127 -0
  55. package/src/components/Menu/Menu.contract.json +177 -0
  56. package/src/components/Message/Message.contract.json +183 -0
  57. package/src/components/NavigationMenu/NavigationMenu.contract.json +203 -0
  58. package/src/components/Pagination/Pagination.contract.json +163 -0
  59. package/src/components/Popover/Popover.contract.json +163 -0
  60. package/src/components/Progress/Progress.contract.json +176 -0
  61. package/src/components/Prompt/Prompt.contract.json +211 -0
  62. package/src/components/RadioGroup/RadioGroup.contract.json +226 -0
  63. package/src/components/ScrollArea/ScrollArea.contract.json +131 -0
  64. package/src/components/Select/Select.contract.json +269 -0
  65. package/src/components/Separator/Separator.contract.json +143 -0
  66. package/src/components/Sidebar/Sidebar.contract.json +258 -0
  67. package/src/components/Sidebar/Sidebar.module.scss +6 -4
  68. package/src/components/Skeleton/Skeleton.contract.json +166 -0
  69. package/src/components/Slider/Slider.contract.json +248 -0
  70. package/src/components/Stack/Stack.contract.json +220 -0
  71. package/src/components/Table/Table.contract.json +171 -0
  72. package/src/components/TableOfContents/TableOfContents.contract.json +145 -0
  73. package/src/components/Tabs/Tabs.contract.json +159 -0
  74. package/src/components/Text/Text.contract.json +239 -0
  75. package/src/components/Textarea/Textarea.contract.json +308 -0
  76. package/src/components/Theme/Theme.contract.json +152 -0
  77. package/src/components/ThinkingIndicator/ThinkingIndicator.contract.json +165 -0
  78. package/src/components/Toast/Toast.contract.json +181 -0
  79. package/src/components/Toggle/Toggle.contract.json +231 -0
  80. package/src/components/ToggleGroup/ToggleGroup.contract.json +206 -0
  81. package/src/components/Tooltip/Tooltip.contract.json +214 -0
  82. package/src/components/VisuallyHidden/VisuallyHidden.contract.json +116 -0
  83. package/src/index.ts +1 -0
  84. package/src/tokens/_derive.scss +4 -1
@@ -0,0 +1,263 @@
1
+ {
2
+ "$schema": "https://usefragments.com/schemas/contract.v1.json",
3
+ "name": "Editor",
4
+ "description": "Rich text editor with formatting toolbar, auto-save, and word count",
5
+ "category": "forms",
6
+ "tags": [
7
+ "editor",
8
+ "rich-text",
9
+ "markdown",
10
+ "toolbar",
11
+ "writing",
12
+ "blog",
13
+ "content"
14
+ ],
15
+ "status": "stable",
16
+ "sourcePath": "src/components/Editor/index.tsx",
17
+ "exportName": "Editor",
18
+ "propsSummary": [
19
+ "children: node",
20
+ "value: string",
21
+ "defaultValue: string (default: )",
22
+ "onValueChange: function",
23
+ "placeholder: string (default: Start typing...)",
24
+ "disabled: boolean (default: false)",
25
+ "readOnly: boolean (default: false)",
26
+ "formats: array",
27
+ "toolbar: boolean (default: true)",
28
+ "statusBar: boolean (default: true)",
29
+ "onAutoSave: function",
30
+ "autoSaveInterval: number (default: 30000)",
31
+ "size: sm|md|lg (default: md)",
32
+ "maxLength: number",
33
+ "toolbarIcons: object"
34
+ ],
35
+ "props": {
36
+ "children": {
37
+ "type": "node",
38
+ "description": "",
39
+ "required": false
40
+ },
41
+ "value": {
42
+ "type": "string",
43
+ "description": "Controlled editor value",
44
+ "required": false
45
+ },
46
+ "defaultValue": {
47
+ "type": "string",
48
+ "description": "Default value for uncontrolled usage",
49
+ "default": "",
50
+ "required": false
51
+ },
52
+ "onValueChange": {
53
+ "type": "function",
54
+ "description": "Called when content changes",
55
+ "required": false
56
+ },
57
+ "placeholder": {
58
+ "type": "string",
59
+ "description": "Placeholder text shown when empty",
60
+ "default": "Start typing...",
61
+ "required": false
62
+ },
63
+ "disabled": {
64
+ "type": "boolean",
65
+ "description": "Disable the editor",
66
+ "default": "false",
67
+ "required": false
68
+ },
69
+ "readOnly": {
70
+ "type": "boolean",
71
+ "description": "Make the editor read-only",
72
+ "default": "false",
73
+ "required": false
74
+ },
75
+ "formats": {
76
+ "type": "array",
77
+ "description": "Which format buttons to show in the toolbar",
78
+ "default": "[\"bold\", \"italic\", \"strikethrough\", \"link\", \"code\", \"bulletList\"]",
79
+ "required": false
80
+ },
81
+ "toolbar": {
82
+ "type": "boolean",
83
+ "description": "Show default toolbar",
84
+ "default": "true",
85
+ "required": false
86
+ },
87
+ "statusBar": {
88
+ "type": "boolean",
89
+ "description": "Show default status bar with word/character counts",
90
+ "default": "true",
91
+ "required": false
92
+ },
93
+ "onAutoSave": {
94
+ "type": "function",
95
+ "description": "Auto-save callback, called at autoSaveInterval (may be async)",
96
+ "required": false
97
+ },
98
+ "autoSaveInterval": {
99
+ "type": "number",
100
+ "description": "Auto-save interval in milliseconds",
101
+ "default": "30000",
102
+ "required": false
103
+ },
104
+ "size": {
105
+ "type": "enum",
106
+ "description": "Editor size preset: \"sm\" (120px), \"md\" (200px), or \"lg\" (400px)",
107
+ "default": "md",
108
+ "required": false,
109
+ "values": [
110
+ "sm",
111
+ "md",
112
+ "lg"
113
+ ]
114
+ },
115
+ "maxLength": {
116
+ "type": "number",
117
+ "description": "Maximum character count. Shows counter in status bar with warning (90%) and error (over limit) states",
118
+ "required": false
119
+ },
120
+ "toolbarIcons": {
121
+ "type": "object",
122
+ "description": "Optional toolbar icon overrides keyed by format/action (e.g. bold, italic, undo). Values can be React nodes or render functions.",
123
+ "required": false
124
+ }
125
+ },
126
+ "usage": {
127
+ "when": [
128
+ "Building blog post or article editors",
129
+ "Need rich text editing with formatting controls",
130
+ "Content management with auto-save and word count",
131
+ "Markdown or WYSIWYG editing interfaces"
132
+ ],
133
+ "whenNot": [
134
+ "Simple single-line text input (use Input)",
135
+ "Plain multi-line without formatting (use Textarea)",
136
+ "Chat/AI prompt input (use Prompt)",
137
+ "Code editing (use CodeBlock)"
138
+ ],
139
+ "guidelines": [
140
+ "Use compound pattern for custom toolbar layouts",
141
+ "Install @tiptap/react, @tiptap/starter-kit, and @tiptap/extension-link for rich text mode",
142
+ "Falls back to a markdown-aware textarea when TipTap is not installed",
143
+ "Use onAutoSave for periodic content persistence",
144
+ "onAutoSave may return a Promise; save status updates after the async save resolves/rejects",
145
+ "Use size prop (sm/md/lg) to control editor height",
146
+ "Use maxLength to show character limit with visual warning states",
147
+ "New formats (headings, blockquote, orderedList, undo, redo) are opt-in via the formats prop",
148
+ "Use toolbarIcons to swap in icons from any icon package without changing toolbar button labels or accessibility behavior"
149
+ ],
150
+ "accessibility": [
151
+ "Toolbar has role=\"toolbar\" with aria-label",
152
+ "Format buttons use aria-pressed to indicate active state",
153
+ "Action buttons (undo/redo) omit aria-pressed since they are not toggles",
154
+ "Status indicator uses aria-live=\"polite\" for save status announcements",
155
+ "Keyboard shortcuts match standard text editor conventions (Ctrl+B, Ctrl+I, etc.)",
156
+ "Global shortcuts (e.g., Sidebar Ctrl+B) automatically yield when focus is inside the Editor",
157
+ "Focus ring appears on the editor container when any child element is focused"
158
+ ]
159
+ },
160
+ "examples": [
161
+ {
162
+ "name": "Default",
163
+ "description": "Editor with default toolbar and status bar",
164
+ "code": "<Editor placeholder=\"Start typing your masterpiece here...\" />"
165
+ },
166
+ {
167
+ "name": "With Auto-Save and Publish",
168
+ "description": "Full compound usage with custom toolbar, auto-save indicator, and publish button",
169
+ "code": "<Editor\n placeholder=\"Start typing your masterpiece here...\"\n onValueChange={(v) => console.log(v)}\n>\n <Editor.Toolbar>\n <Editor.ToolbarGroup aria-label=\"Text formatting\">\n <Editor.ToolbarButton format=\"bold\" />\n <Editor.ToolbarButton format=\"italic\" />\n <Editor.ToolbarButton format=\"strikethrough\" />\n <Editor.ToolbarButton format=\"link\" />\n <Editor.ToolbarButton format=\"code\" />\n <Editor.ToolbarButton format=\"bulletList\" />\n </Editor.ToolbarGroup>\n <Editor.ToolbarGroup aria-label=\"Actions\">\n <Editor.StatusIndicator status=\"saved\" />\n <Button variant=\"accent\" size=\"sm\">\n Publish\n </Button>\n </Editor.ToolbarGroup>\n </Editor.Toolbar>\n <Editor.Content />\n <Editor.StatusBar showWordCount showCharCount />\n</Editor>"
170
+ },
171
+ {
172
+ "name": "Minimal",
173
+ "description": "Editor with subset of formats",
174
+ "code": "<Editor\n placeholder=\"Quick note...\"\n formats={['bold', 'italic', 'code']}\n/>"
175
+ },
176
+ {
177
+ "name": "Disabled",
178
+ "description": "Non-interactive editor with content",
179
+ "code": "<Editor\n defaultValue=\"This content cannot be edited.\"\n disabled\n/>"
180
+ },
181
+ {
182
+ "name": "Read Only",
183
+ "description": "Visible content, non-editable",
184
+ "code": "<Editor\n defaultValue=\"This content is read-only. You can select and copy text but cannot modify it.\"\n readOnly\n/>"
185
+ },
186
+ {
187
+ "name": "Custom Toolbar",
188
+ "description": "Compound usage with custom toolbar layout and separator",
189
+ "code": "<Editor\n placeholder=\"Write your blog post...\"\n onValueChange={(v) => console.log(v)}\n>\n <Editor.Toolbar>\n <Editor.ToolbarGroup aria-label=\"Basic formatting\">\n <Editor.ToolbarButton format=\"bold\" />\n <Editor.ToolbarButton format=\"italic\" />\n </Editor.ToolbarGroup>\n <Editor.Separator />\n <Editor.ToolbarGroup aria-label=\"Advanced formatting\">\n <Editor.ToolbarButton format=\"link\" />\n <Editor.ToolbarButton format=\"code\" />\n <Editor.ToolbarButton format=\"bulletList\" />\n </Editor.ToolbarGroup>\n <Editor.ToolbarGroup aria-label=\"Status\">\n <Editor.StatusIndicator status=\"saving\" />\n </Editor.ToolbarGroup>\n </Editor.Toolbar>\n <Editor.Content />\n <Editor.StatusBar showWordCount showCharCount />\n</Editor>"
190
+ },
191
+ {
192
+ "name": "Full Formatting",
193
+ "description": "All format options including headings, blockquote, ordered list, undo/redo",
194
+ "code": "<Editor\n placeholder=\"Write an article...\"\n size=\"lg\"\n>\n <Editor.Toolbar>\n <Editor.ToolbarGroup aria-label=\"History\">\n <Editor.ToolbarButton format=\"undo\" />\n <Editor.ToolbarButton format=\"redo\" />\n </Editor.ToolbarGroup>\n <Editor.Separator />\n <Editor.ToolbarGroup aria-label=\"Headings\">\n <Editor.ToolbarButton format=\"heading1\" />\n <Editor.ToolbarButton format=\"heading2\" />\n <Editor.ToolbarButton format=\"heading3\" />\n </Editor.ToolbarGroup>\n <Editor.Separator />\n <Editor.ToolbarGroup aria-label=\"Text formatting\">\n <Editor.ToolbarButton format=\"bold\" />\n <Editor.ToolbarButton format=\"italic\" />\n <Editor.ToolbarButton format=\"strikethrough\" />\n <Editor.ToolbarButton format=\"code\" />\n </Editor.ToolbarGroup>\n <Editor.Separator />\n <Editor.ToolbarGroup aria-label=\"Block formatting\">\n <Editor.ToolbarButton format=\"bulletList\" />\n <Editor.ToolbarButton format=\"orderedList\" />\n <Editor.ToolbarButton format=\"blockquote\" />\n <Editor.ToolbarButton format=\"link\" />\n </Editor.ToolbarGroup>\n </Editor.Toolbar>\n <Editor.Content />\n <Editor.StatusBar showWordCount showCharCount />\n</Editor>"
195
+ },
196
+ {
197
+ "name": "With Character Limit",
198
+ "description": "Editor with maxLength showing character count indicator with warning and error states",
199
+ "code": "<Editor\n placeholder=\"Write a tweet-sized message...\"\n maxLength={280}\n size=\"sm\"\n formats={['bold', 'italic', 'link']}\n/>"
200
+ },
201
+ {
202
+ "name": "Small Size",
203
+ "description": "Compact editor with sm size preset (120px min-height)",
204
+ "code": "<Editor\n placeholder=\"Quick note...\"\n size=\"sm\"\n formats={['bold', 'italic', 'code']}\n/>"
205
+ }
206
+ ],
207
+ "relations": [
208
+ {
209
+ "component": "Prompt",
210
+ "relationship": "sibling",
211
+ "note": "Prompt is for AI/chat input; Editor is for long-form content editing"
212
+ },
213
+ {
214
+ "component": "Textarea",
215
+ "relationship": "alternative",
216
+ "note": "Use Textarea for simple multi-line input without formatting toolbar"
217
+ },
218
+ {
219
+ "component": "Input",
220
+ "relationship": "alternative",
221
+ "note": "Use Input for single-line text input"
222
+ }
223
+ ],
224
+ "contract": {
225
+ "propsSummary": [
226
+ "value: string - controlled editor value",
227
+ "onValueChange: (value: string) => void - change callback",
228
+ "placeholder: string - hint text (default: \"Start typing...\")",
229
+ "disabled: boolean - disables interaction",
230
+ "readOnly: boolean - prevents editing",
231
+ "formats: EditorFormat[] - toolbar buttons (default: all 6)",
232
+ "toolbar: boolean - show default toolbar (default: true)",
233
+ "statusBar: boolean - show word/char counts (default: true)",
234
+ "onAutoSave: (value: string) => void | Promise<void> - auto-save handler",
235
+ "size: \"sm\" | \"md\" | \"lg\" - editor height preset (default: \"md\")",
236
+ "maxLength: number - character limit with visual indicator",
237
+ "toolbarIcons: Partial<Record<EditorFormat, ReactNode | (state) => ReactNode>> - custom toolbar icons"
238
+ ],
239
+ "a11yRules": [
240
+ "A11Y_TOOLBAR_ROLE",
241
+ "A11Y_BUTTON_LABEL",
242
+ "A11Y_LIVE_REGION"
243
+ ]
244
+ },
245
+ "ai": {
246
+ "compositionPattern": "compound",
247
+ "subComponents": [
248
+ "Toolbar",
249
+ "ToolbarGroup",
250
+ "ToolbarButton",
251
+ "Separator",
252
+ "StatusIndicator",
253
+ "Content",
254
+ "StatusBar"
255
+ ]
256
+ },
257
+ "provenance": {
258
+ "source": "migrated",
259
+ "verified": false,
260
+ "frameworkSupport": "native",
261
+ "extractedAt": "2026-03-13T23:18:56.748Z"
262
+ }
263
+ }
@@ -0,0 +1,133 @@
1
+ {
2
+ "$schema": "https://usefragments.com/schemas/contract.v1.json",
3
+ "name": "EmptyState",
4
+ "description": "Placeholder for empty content areas. Provides context, guidance, and actions when no data is available.",
5
+ "category": "feedback",
6
+ "tags": [
7
+ "empty",
8
+ "placeholder",
9
+ "no-data",
10
+ "zero-state",
11
+ "blank-slate"
12
+ ],
13
+ "status": "stable",
14
+ "sourcePath": "src/components/EmptyState/index.tsx",
15
+ "exportName": "EmptyState",
16
+ "propsSummary": [
17
+ "children: node (required)",
18
+ "size: sm|md|lg (default: md)"
19
+ ],
20
+ "props": {
21
+ "children": {
22
+ "type": "node",
23
+ "description": "EmptyState content - use EmptyState.Icon, EmptyState.Title, EmptyState.Description, EmptyState.Actions sub-components",
24
+ "required": true
25
+ },
26
+ "size": {
27
+ "type": "enum",
28
+ "description": "Size variant",
29
+ "default": "md",
30
+ "required": false,
31
+ "values": [
32
+ "sm",
33
+ "md",
34
+ "lg"
35
+ ]
36
+ }
37
+ },
38
+ "usage": {
39
+ "when": [
40
+ "Empty lists, tables, or search results",
41
+ "New user onboarding (no content yet)",
42
+ "Filtered views with no matches",
43
+ "Error states where content failed to load"
44
+ ],
45
+ "whenNot": [
46
+ "Loading states (use skeleton or spinner)",
47
+ "Error messages with retry (use Alert)",
48
+ "Temporary messages (use Toast)"
49
+ ],
50
+ "guidelines": [
51
+ "Always explain why the area is empty",
52
+ "Provide a clear action to resolve the empty state",
53
+ "Use appropriate icons to reinforce the message",
54
+ "Keep messaging positive and actionable",
55
+ "EmptyState compound sub-components forward DOM props for ids, aria attributes, and handlers"
56
+ ],
57
+ "accessibility": [
58
+ "Empty state content is accessible to screen readers",
59
+ "Action buttons follow button accessibility guidelines"
60
+ ]
61
+ },
62
+ "examples": [
63
+ {
64
+ "name": "Default",
65
+ "description": "Basic empty state with action",
66
+ "code": "<EmptyState>\n <EmptyState.Icon><FolderIcon /></EmptyState.Icon>\n <EmptyState.Title>No projects yet</EmptyState.Title>\n <EmptyState.Description>Get started by creating your first project.</EmptyState.Description>\n <EmptyState.Actions>\n <Button>Create Project</Button>\n </EmptyState.Actions>\n</EmptyState>"
67
+ },
68
+ {
69
+ "name": "No Results",
70
+ "description": "Empty search results",
71
+ "code": "<EmptyState>\n <EmptyState.Icon><SearchIcon /></EmptyState.Icon>\n <EmptyState.Title>No results found</EmptyState.Title>\n <EmptyState.Description>Try adjusting your search terms or filters.</EmptyState.Description>\n <EmptyState.Actions>\n <Button variant=\"secondary\">Clear Filters</Button>\n </EmptyState.Actions>\n</EmptyState>"
72
+ },
73
+ {
74
+ "name": "With Secondary Action",
75
+ "description": "Empty state with two actions",
76
+ "code": "<EmptyState>\n <EmptyState.Icon><InboxIcon /></EmptyState.Icon>\n <EmptyState.Title>Inbox is empty</EmptyState.Title>\n <EmptyState.Description>You have no new messages.</EmptyState.Description>\n <EmptyState.Actions>\n <Button>Compose Message</Button>\n <Button variant=\"secondary\">View Archive</Button>\n </EmptyState.Actions>\n</EmptyState>"
77
+ },
78
+ {
79
+ "name": "Small",
80
+ "description": "Compact empty state for inline use",
81
+ "code": "<EmptyState size=\"sm\">\n <EmptyState.Title>No items</EmptyState.Title>\n <EmptyState.Description>Add items to see them here.</EmptyState.Description>\n</EmptyState>"
82
+ },
83
+ {
84
+ "name": "Large",
85
+ "description": "Prominent empty state for full-page use",
86
+ "code": "<EmptyState size=\"lg\">\n <EmptyState.Icon><FolderIcon /></EmptyState.Icon>\n <EmptyState.Title>Welcome to your workspace</EmptyState.Title>\n <EmptyState.Description>\n This is where your projects will appear. Create your first project to get started.\n </EmptyState.Description>\n <EmptyState.Actions>\n <Button>Create Your First Project</Button>\n </EmptyState.Actions>\n</EmptyState>"
87
+ }
88
+ ],
89
+ "relations": [
90
+ {
91
+ "component": "Alert",
92
+ "relationship": "alternative",
93
+ "note": "Use Alert for error states with retry"
94
+ },
95
+ {
96
+ "component": "Progress",
97
+ "relationship": "alternative",
98
+ "note": "Use Progress/Spinner for loading states"
99
+ }
100
+ ],
101
+ "contract": {
102
+ "propsSummary": [
103
+ "size: sm|md|lg (default: md)",
104
+ "Sub-components: EmptyState.Icon, EmptyState.Title, EmptyState.Description, EmptyState.Actions",
105
+ "Compound sub-components forward DOM props to their rendered elements"
106
+ ],
107
+ "a11yRules": [
108
+ "A11Y_EMPTY_STATE_CONTENT"
109
+ ]
110
+ },
111
+ "ai": {
112
+ "compositionPattern": "compound",
113
+ "subComponents": [
114
+ "Icon",
115
+ "Title",
116
+ "Description",
117
+ "Actions"
118
+ ],
119
+ "requiredChildren": [
120
+ "Title"
121
+ ],
122
+ "commonPatterns": [
123
+ "<EmptyState><EmptyState.Title>{title}</EmptyState.Title><EmptyState.Description>{description}</EmptyState.Description></EmptyState>",
124
+ "<EmptyState><EmptyState.Icon>{icon}</EmptyState.Icon><EmptyState.Title>{title}</EmptyState.Title><EmptyState.Description>{description}</EmptyState.Description><EmptyState.Actions><Button>{action}</Button></EmptyState.Actions></EmptyState>"
125
+ ]
126
+ },
127
+ "provenance": {
128
+ "source": "migrated",
129
+ "verified": false,
130
+ "frameworkSupport": "native",
131
+ "extractedAt": "2026-03-13T23:18:56.922Z"
132
+ }
133
+ }
@@ -0,0 +1,157 @@
1
+ {
2
+ "$schema": "https://usefragments.com/schemas/contract.v1.json",
3
+ "name": "Field",
4
+ "description": "Compositional form field wrapper providing validation, labels, descriptions, and error messages. Use for advanced form needs beyond baked-in Input/Textarea props.",
5
+ "category": "forms",
6
+ "tags": [
7
+ "form",
8
+ "field",
9
+ "validation",
10
+ "label",
11
+ "error",
12
+ "input",
13
+ "accessible"
14
+ ],
15
+ "status": "stable",
16
+ "sourcePath": "src/components/Field/index.tsx",
17
+ "exportName": "Field",
18
+ "propsSummary": [
19
+ "children: node (required)",
20
+ "name: string",
21
+ "disabled: boolean",
22
+ "invalid: boolean",
23
+ "validate: function",
24
+ "validationMode: onSubmit|onBlur|onChange",
25
+ "validationDebounceTime: number"
26
+ ],
27
+ "props": {
28
+ "children": {
29
+ "type": "node",
30
+ "description": "Field content (Label, Control, Description, Error)",
31
+ "required": true
32
+ },
33
+ "name": {
34
+ "type": "string",
35
+ "description": "Field name, used for error distribution from Form",
36
+ "required": false
37
+ },
38
+ "disabled": {
39
+ "type": "boolean",
40
+ "description": "Disables the field and its control",
41
+ "required": false
42
+ },
43
+ "invalid": {
44
+ "type": "boolean",
45
+ "description": "Marks the field as invalid",
46
+ "required": false
47
+ },
48
+ "validate": {
49
+ "type": "function",
50
+ "description": "Custom validation function returning error string(s) or null",
51
+ "required": false
52
+ },
53
+ "validationMode": {
54
+ "type": "enum",
55
+ "description": "When to trigger validation",
56
+ "required": false,
57
+ "values": [
58
+ "onSubmit",
59
+ "onBlur",
60
+ "onChange"
61
+ ]
62
+ },
63
+ "validationDebounceTime": {
64
+ "type": "number",
65
+ "description": "Debounce time in ms for onChange validation",
66
+ "required": false
67
+ }
68
+ },
69
+ "usage": {
70
+ "when": [
71
+ "You need granular validation with match-based error messages",
72
+ "Custom form controls need accessible labels and descriptions",
73
+ "Server-side errors need to be distributed to specific fields",
74
+ "You need dirty/touched tracking or custom validation logic"
75
+ ],
76
+ "whenNot": [
77
+ "Simple inputs with basic label and helper text (use Input with label prop)",
78
+ "Standalone selects or textareas with built-in error display"
79
+ ],
80
+ "guidelines": [
81
+ "Always provide a Field.Label for accessibility",
82
+ "Wrap any form control in Field.Control to connect it to the field context",
83
+ "Use match prop on Field.Error for granular native validation messages",
84
+ "Wrap in Form to enable server-side error distribution by field name"
85
+ ],
86
+ "accessibility": [
87
+ "Label automatically linked to control via aria-labelledby",
88
+ "Description linked via aria-describedby",
89
+ "Error messages announced to screen readers",
90
+ "Supports data-disabled and data-invalid attributes for styling"
91
+ ]
92
+ },
93
+ "examples": [
94
+ {
95
+ "name": "Single field",
96
+ "description": "A single field with label, control, and description",
97
+ "code": "<Field name=\"email\">\n <Field.Label>Email address</Field.Label>\n <Field.Control>\n <Input type=\"email\" placeholder=\"jane@example.com\" />\n </Field.Control>\n <Field.Description>We will never share your email.</Field.Description>\n</Field>"
98
+ },
99
+ {
100
+ "name": "Two-column layout",
101
+ "description": "Fields arranged in a two-column grid",
102
+ "code": "<Grid columns={2} gap=\"md\">\n <Field name=\"firstName\">\n <Field.Label>First Name</Field.Label>\n <Field.Control>\n <Input placeholder=\"Jane\" />\n </Field.Control>\n </Field>\n <Field name=\"lastName\">\n <Field.Label>Last Name</Field.Label>\n <Field.Control>\n <Input placeholder=\"Doe\" />\n </Field.Control>\n </Field>\n <Grid.Item colSpan=\"full\">\n <Field name=\"email\">\n <Field.Label>Email</Field.Label>\n <Field.Control>\n <Input type=\"email\" placeholder=\"jane@example.com\" />\n </Field.Control>\n <Field.Error match=\"typeMismatch\">Enter a valid email address</Field.Error>\n </Field>\n </Grid.Item>\n</Grid>"
103
+ },
104
+ {
105
+ "name": "Custom validation",
106
+ "description": "Field with custom validate function",
107
+ "code": "<Field\n name=\"age\"\n validate={(value) => {\n const num = Number(value);\n if (isNaN(num) || num < 18) return 'Must be 18 or older';\n return null;\n }}\n validationMode=\"onChange\"\n validationDebounceTime={500}\n>\n <Field.Label>Age</Field.Label>\n <Field.Control>\n <Input type=\"number\" placeholder=\"18\" />\n </Field.Control>\n <Field.Description>You must be at least 18 years old.</Field.Description>\n <Field.Error match=\"customError\" />\n</Field>"
108
+ }
109
+ ],
110
+ "relations": [
111
+ {
112
+ "component": "Input",
113
+ "relationship": "alternative",
114
+ "note": "Use Input for simple fields with built-in label/error"
115
+ },
116
+ {
117
+ "component": "Form",
118
+ "relationship": "parent",
119
+ "note": "Wrap in Form for server-side error distribution"
120
+ },
121
+ {
122
+ "component": "Fieldset",
123
+ "relationship": "sibling",
124
+ "note": "Use Fieldset to group related fields"
125
+ }
126
+ ],
127
+ "contract": {
128
+ "propsSummary": [
129
+ "name: string - field name for error distribution",
130
+ "validate: (value) => string | null - custom validation",
131
+ "validationMode: onSubmit|onBlur|onChange - validation trigger",
132
+ "Field.Control: wraps any form component (Input, Textarea, etc.)",
133
+ "Field.Error match: valueMissing|typeMismatch|customError|... - granular errors"
134
+ ],
135
+ "a11yRules": [
136
+ "A11Y_FIELD_LABEL",
137
+ "A11Y_FIELD_ERROR",
138
+ "A11Y_FIELD_DESCRIPTION"
139
+ ]
140
+ },
141
+ "ai": {
142
+ "compositionPattern": "compound",
143
+ "subComponents": [
144
+ "Label",
145
+ "Control",
146
+ "Description",
147
+ "Error",
148
+ "Validity"
149
+ ]
150
+ },
151
+ "provenance": {
152
+ "source": "migrated",
153
+ "verified": false,
154
+ "frameworkSupport": "native",
155
+ "extractedAt": "2026-03-13T23:18:59.733Z"
156
+ }
157
+ }