@fragments-sdk/ui 0.3.0 → 0.4.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 (133) hide show
  1. package/fragments.json +1 -1
  2. package/package.json +9 -4
  3. package/src/components/Accordion/Accordion.fragment.tsx +186 -0
  4. package/src/components/Accordion/Accordion.module.scss +111 -0
  5. package/src/components/Accordion/index.tsx +271 -0
  6. package/src/components/Alert/Alert.fragment.tsx +66 -41
  7. package/src/components/Alert/Alert.module.scss +31 -21
  8. package/src/components/Alert/index.tsx +202 -73
  9. package/src/components/AppShell/AppShell.fragment.tsx +315 -0
  10. package/src/components/AppShell/AppShell.module.scss +213 -0
  11. package/src/components/AppShell/index.tsx +398 -0
  12. package/src/components/Avatar/index.tsx +8 -9
  13. package/src/components/Badge/Badge.module.scss +16 -10
  14. package/src/components/Badge/index.tsx +20 -6
  15. package/src/components/Box/Box.fragment.tsx +168 -0
  16. package/src/components/Box/Box.module.scss +84 -0
  17. package/src/components/Box/index.tsx +78 -0
  18. package/src/components/Button/Button.module.scss +42 -0
  19. package/src/components/Button/index.tsx +67 -33
  20. package/src/components/ButtonGroup/ButtonGroup.module.scss +37 -0
  21. package/src/components/ButtonGroup/index.tsx +40 -0
  22. package/src/components/Card/Card.fragment.tsx +51 -25
  23. package/src/components/Card/Card.module.scss +52 -5
  24. package/src/components/Card/index.tsx +154 -53
  25. package/src/components/Checkbox/Checkbox.module.scss +4 -4
  26. package/src/components/Checkbox/index.tsx +3 -4
  27. package/src/components/CodeBlock/CodeBlock.fragment.tsx +201 -0
  28. package/src/components/CodeBlock/CodeBlock.module.scss +224 -0
  29. package/src/components/CodeBlock/index.tsx +385 -0
  30. package/src/components/ColorChip/ColorChip.module.scss +165 -0
  31. package/src/components/ColorChip/index.tsx +157 -0
  32. package/src/components/ColorPicker/ColorPicker.module.scss +109 -0
  33. package/src/components/ColorPicker/index.tsx +107 -0
  34. package/src/components/Dialog/Dialog.fragment.tsx +9 -0
  35. package/src/components/Dialog/Dialog.module.scss +26 -7
  36. package/src/components/Dialog/index.tsx +12 -15
  37. package/src/components/EmptyState/EmptyState.fragment.tsx +54 -71
  38. package/src/components/EmptyState/EmptyState.module.scss +9 -9
  39. package/src/components/EmptyState/index.tsx +104 -69
  40. package/src/components/Field/Field.fragment.tsx +165 -0
  41. package/src/components/Field/Field.module.scss +31 -0
  42. package/src/components/Field/index.tsx +143 -0
  43. package/src/components/Fieldset/Fieldset.fragment.tsx +166 -0
  44. package/src/components/Fieldset/Fieldset.module.scss +22 -0
  45. package/src/components/Fieldset/index.tsx +47 -0
  46. package/src/components/Form/Form.fragment.tsx +286 -0
  47. package/src/components/Form/Form.module.scss +8 -0
  48. package/src/components/Form/index.tsx +53 -0
  49. package/src/components/Grid/Grid.fragment.tsx +17 -17
  50. package/src/components/Grid/index.tsx +6 -1
  51. package/src/components/Header/Header.fragment.tsx +192 -0
  52. package/src/components/Header/Header.module.scss +209 -0
  53. package/src/components/Header/index.tsx +363 -0
  54. package/src/components/Icon/Icon.fragment.tsx +138 -0
  55. package/src/components/Icon/Icon.module.scss +38 -0
  56. package/src/components/Icon/index.tsx +58 -0
  57. package/src/components/Image/Image.fragment.tsx +195 -0
  58. package/src/components/Image/Image.module.scss +77 -0
  59. package/src/components/Image/index.tsx +95 -0
  60. package/src/components/Input/Input.module.scss +75 -2
  61. package/src/components/Input/index.tsx +60 -21
  62. package/src/components/Link/Link.fragment.tsx +132 -0
  63. package/src/components/Link/Link.module.scss +67 -0
  64. package/src/components/Link/index.tsx +57 -0
  65. package/src/components/List/List.fragment.tsx +152 -0
  66. package/src/components/List/List.module.scss +71 -0
  67. package/src/components/List/index.tsx +106 -0
  68. package/src/components/Listbox/Listbox.fragment.tsx +191 -0
  69. package/src/components/Listbox/Listbox.module.scss +97 -0
  70. package/src/components/Listbox/index.tsx +121 -0
  71. package/src/components/Menu/Menu.fragment.tsx +9 -0
  72. package/src/components/Menu/Menu.module.scss +17 -1
  73. package/src/components/Menu/index.tsx +3 -3
  74. package/src/components/Popover/Popover.fragment.tsx +9 -0
  75. package/src/components/Popover/Popover.module.scss +33 -10
  76. package/src/components/Popover/index.tsx +9 -11
  77. package/src/components/Progress/Progress.module.scss +11 -11
  78. package/src/components/Progress/index.tsx +34 -7
  79. package/src/components/Prompt/Prompt.fragment.tsx +231 -0
  80. package/src/components/Prompt/Prompt.module.scss +243 -0
  81. package/src/components/Prompt/index.tsx +439 -0
  82. package/src/components/RadioGroup/RadioGroup.module.scss +3 -3
  83. package/src/components/RadioGroup/index.tsx +3 -4
  84. package/src/components/Select/Select.fragment.tsx +9 -0
  85. package/src/components/Select/index.tsx +6 -7
  86. package/src/components/Separator/index.tsx +7 -3
  87. package/src/components/Sidebar/Sidebar.fragment.tsx +9 -0
  88. package/src/components/Sidebar/Sidebar.module.scss +72 -47
  89. package/src/components/Sidebar/index.tsx +5 -3
  90. package/src/components/Skeleton/Skeleton.fragment.tsx +5 -5
  91. package/src/components/Skeleton/Skeleton.module.scss +11 -0
  92. package/src/components/Slider/Slider.module.scss +87 -0
  93. package/src/components/Slider/index.tsx +88 -0
  94. package/src/components/Stack/Stack.module.scss +120 -0
  95. package/src/components/Stack/index.tsx +148 -0
  96. package/src/components/Table/Table.fragment.tsx +7 -0
  97. package/src/components/Table/Table.module.scss +57 -0
  98. package/src/components/Table/index.tsx +44 -6
  99. package/src/components/Tabs/Tabs.fragment.tsx +9 -0
  100. package/src/components/Tabs/Tabs.module.scss +25 -10
  101. package/src/components/Tabs/index.tsx +11 -8
  102. package/src/components/Text/Text.module.scss +82 -0
  103. package/src/components/Text/index.tsx +58 -0
  104. package/src/components/Textarea/index.tsx +3 -7
  105. package/src/components/Theme/Theme.fragment.tsx +128 -0
  106. package/src/components/Theme/ThemeToggle.module.scss +82 -0
  107. package/src/components/Theme/index.tsx +343 -0
  108. package/src/components/Toast/Toast.fragment.tsx +5 -5
  109. package/src/components/Toast/Toast.module.scss +16 -1
  110. package/src/components/Toast/index.tsx +27 -11
  111. package/src/components/Toggle/Toggle.module.scss +25 -10
  112. package/src/components/Toggle/index.tsx +12 -0
  113. package/src/components/ToggleGroup/ToggleGroup.module.scss +134 -0
  114. package/src/components/ToggleGroup/index.tsx +144 -0
  115. package/src/components/Tooltip/Tooltip.module.scss +4 -4
  116. package/src/components/Tooltip/index.tsx +4 -2
  117. package/src/components/VisuallyHidden/VisuallyHidden.fragment.tsx +134 -0
  118. package/src/components/VisuallyHidden/VisuallyHidden.module.scss +13 -0
  119. package/src/components/VisuallyHidden/index.tsx +29 -0
  120. package/src/index.ts +195 -3
  121. package/src/recipes/AppShell.recipe.ts +175 -0
  122. package/src/recipes/CardGrid.recipe.ts +6 -2
  123. package/src/recipes/ChatInterface.recipe.ts +87 -0
  124. package/src/recipes/CodeExamples.recipe.ts +66 -0
  125. package/src/recipes/DashboardLayout.recipe.ts +46 -12
  126. package/src/recipes/DashboardNav.recipe.ts +183 -0
  127. package/src/recipes/LoginForm.recipe.ts +8 -1
  128. package/src/recipes/SettingsPage.recipe.ts +37 -20
  129. package/src/styles/globals.scss +31 -0
  130. package/src/tokens/_index.scss +3 -0
  131. package/src/tokens/_mixins.scss +54 -1
  132. package/src/tokens/_variables.scss +429 -64
  133. package/src/utils/a11y.tsx +439 -0
@@ -0,0 +1,192 @@
1
+ import React from 'react';
2
+ import { defineSegment } from '@fragments/core';
3
+ import { Header } from './index.js';
4
+ import { ThemeToggle, ThemeProvider } from '../Theme/index.js';
5
+ import { Button } from '../Button/index.js';
6
+ import { Input } from '../Input/index.js';
7
+
8
+ function SearchIcon() {
9
+ return (
10
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 256 256" fill="currentColor">
11
+ <path d="M229.66,218.34l-50.07-50.06a88.11,88.11,0,1,0-11.31,11.31l50.06,50.07a8,8,0,0,0,11.32-11.32ZM40,112a72,72,0,1,1,72,72A72.08,72.08,0,0,1,40,112Z" />
12
+ </svg>
13
+ );
14
+ }
15
+
16
+ export default defineSegment({
17
+ component: Header,
18
+
19
+ meta: {
20
+ name: 'Header',
21
+ description: 'Composable header with slots for brand, navigation, search, and actions. Designed for use within AppShell with responsive mobile support.',
22
+ category: 'navigation',
23
+ status: 'stable',
24
+ tags: ['header', 'navigation', 'navbar', 'brand', 'layout'],
25
+ since: '0.5.0',
26
+ },
27
+
28
+ usage: {
29
+ when: [
30
+ 'Primary site or app header inside AppShell',
31
+ 'Navigation bar with branding (stacked layout)',
32
+ 'Search and actions bar (sidebar-inset layout)',
33
+ 'Header with responsive mobile menu trigger',
34
+ ],
35
+ whenNot: [
36
+ 'Simple page titles (use heading elements)',
37
+ 'Footer navigation (use Footer component)',
38
+ 'Standalone sidebar navigation (use Sidebar directly)',
39
+ ],
40
+ guidelines: [
41
+ 'Use Header.SkipLink for accessibility (skip to main content)',
42
+ 'In stacked layout: include Header.Brand for logo',
43
+ 'In sidebar-inset layout: omit Header.Brand (logo in sidebar)',
44
+ 'Header.Trigger integrates with SidebarProvider for mobile menus',
45
+ 'Header.Nav is hidden on mobile; use sidebar for mobile navigation',
46
+ 'Use Header.Spacer to push items apart',
47
+ ],
48
+ accessibility: [
49
+ 'Include Header.SkipLink for keyboard users',
50
+ 'Navigation has aria-label for screen readers',
51
+ 'Active nav items use aria-current="page"',
52
+ 'Mobile trigger has aria-expanded state',
53
+ ],
54
+ },
55
+
56
+ props: {
57
+ children: {
58
+ type: 'node',
59
+ description: 'Header content (use Header.Brand, Header.Nav, etc.)',
60
+ required: true,
61
+ },
62
+ height: {
63
+ type: 'string',
64
+ description: 'Header height',
65
+ default: '56px',
66
+ },
67
+ position: {
68
+ type: 'enum',
69
+ description: 'Position behavior (usually controlled by AppShell)',
70
+ values: ['static', 'fixed', 'sticky'],
71
+ default: 'static',
72
+ },
73
+ },
74
+
75
+ relations: [
76
+ { component: 'AppShell', relationship: 'parent', note: 'Header is typically used inside AppShell.Header' },
77
+ { component: 'Sidebar', relationship: 'sibling', note: 'Header.Trigger toggles Sidebar on mobile' },
78
+ { component: 'ThemeToggle', relationship: 'child', note: 'ThemeToggle is commonly placed in Header.Actions' },
79
+ ],
80
+
81
+ ai: {
82
+ compositionPattern: 'compound',
83
+ subComponents: ['SkipLink', 'Trigger', 'Brand', 'Nav', 'NavItem', 'Search', 'Spacer', 'Actions'],
84
+ commonPatterns: [
85
+ '<Header><Header.Brand href="/">{appName}</Header.Brand><Header.Nav><Header.NavItem href="/home" active>Home</Header.NavItem></Header.Nav><Header.Spacer /><Header.Actions>{actions}</Header.Actions></Header>',
86
+ ],
87
+ },
88
+
89
+ variants: [
90
+ {
91
+ name: 'For Stacked Layout',
92
+ description: 'Header with brand, nav, and actions. Use with AppShell layout="stacked".',
93
+ render: () => (
94
+ <ThemeProvider defaultMode="light">
95
+ <Header>
96
+ <Header.SkipLink />
97
+ <Header.Trigger />
98
+ <Header.Brand href="/">MyApp</Header.Brand>
99
+ <Header.Nav>
100
+ <Header.NavItem href="/dashboard" active>Dashboard</Header.NavItem>
101
+ <Header.NavItem href="/projects">Projects</Header.NavItem>
102
+ <Header.NavItem href="/settings">Settings</Header.NavItem>
103
+ </Header.Nav>
104
+ <Header.Spacer />
105
+ <Header.Actions>
106
+ <ThemeToggle size="md" />
107
+ <Button variant="secondary" size="sm">Sign In</Button>
108
+ </Header.Actions>
109
+ </Header>
110
+ </ThemeProvider>
111
+ ),
112
+ },
113
+ {
114
+ name: 'For Sidebar Inset Layout',
115
+ description: 'Header without brand (logo is in sidebar). Use with AppShell layout="sidebar-inset".',
116
+ render: () => (
117
+ <ThemeProvider defaultMode="light">
118
+ <Header>
119
+ <Header.SkipLink />
120
+ <Header.Trigger />
121
+ <Header.Search>
122
+ <Input placeholder="Search..." style={{ width: '240px' }} />
123
+ </Header.Search>
124
+ <Header.Spacer />
125
+ <Header.Actions>
126
+ <ThemeToggle size="md" />
127
+ </Header.Actions>
128
+ </Header>
129
+ </ThemeProvider>
130
+ ),
131
+ },
132
+ {
133
+ name: 'Minimal',
134
+ description: 'Clean header with just trigger and actions',
135
+ render: () => (
136
+ <ThemeProvider defaultMode="light">
137
+ <Header>
138
+ <Header.Trigger />
139
+ <Header.Spacer />
140
+ <Header.Actions>
141
+ <ThemeToggle size="md" />
142
+ </Header.Actions>
143
+ </Header>
144
+ </ThemeProvider>
145
+ ),
146
+ },
147
+ {
148
+ name: 'With Search',
149
+ description: 'Header featuring a prominent search input',
150
+ render: () => (
151
+ <ThemeProvider defaultMode="light">
152
+ <Header>
153
+ <Header.Brand>Docs</Header.Brand>
154
+ <Header.Search>
155
+ <div style={{
156
+ display: 'flex',
157
+ alignItems: 'center',
158
+ gap: '8px',
159
+ padding: '8px 12px',
160
+ background: 'var(--fui-bg-secondary)',
161
+ borderRadius: '6px',
162
+ color: 'var(--fui-text-tertiary)',
163
+ fontSize: '14px',
164
+ width: '280px'
165
+ }}>
166
+ <SearchIcon /> Search documentation...
167
+ </div>
168
+ </Header.Search>
169
+ <Header.Spacer />
170
+ <Header.Actions>
171
+ <ThemeToggle size="md" />
172
+ </Header.Actions>
173
+ </Header>
174
+ </ThemeProvider>
175
+ ),
176
+ },
177
+ {
178
+ name: 'With Skip Link',
179
+ description: 'Accessible header with skip link for keyboard navigation',
180
+ render: () => (
181
+ <Header>
182
+ <Header.SkipLink href="#main-content">Skip to content</Header.SkipLink>
183
+ <Header.Brand>Accessible App</Header.Brand>
184
+ <Header.Nav>
185
+ <Header.NavItem href="/" active>Home</Header.NavItem>
186
+ <Header.NavItem href="/about">About</Header.NavItem>
187
+ </Header.Nav>
188
+ </Header>
189
+ ),
190
+ },
191
+ ],
192
+ });
@@ -0,0 +1,209 @@
1
+ @use '../../tokens/variables' as *;
2
+ @use '../../tokens/mixins' as *;
3
+
4
+ // ============================================
5
+ // Header Root
6
+ // ============================================
7
+
8
+ .header {
9
+ display: flex;
10
+ align-items: center;
11
+ height: var(--header-height, 56px);
12
+ min-height: var(--header-height, 56px);
13
+ background-color: var(--fui-bg-primary, $fui-bg-primary);
14
+ padding: 0 var(--fui-space-4, $fui-space-4);
15
+ z-index: var(--fui-header-z-index, 40);
16
+ }
17
+
18
+ .fixed {
19
+ position: fixed;
20
+ top: 0;
21
+ left: 0;
22
+ right: 0;
23
+ }
24
+
25
+ .sticky {
26
+ position: sticky;
27
+ top: 0;
28
+ }
29
+
30
+ .container {
31
+ display: flex;
32
+ align-items: center;
33
+ gap: var(--fui-space-4, $fui-space-4);
34
+ width: 100%;
35
+ max-width: 100%;
36
+ }
37
+
38
+ // ============================================
39
+ // Brand
40
+ // ============================================
41
+
42
+ .brand {
43
+ display: flex;
44
+ align-items: center;
45
+ gap: var(--fui-space-2, $fui-space-2);
46
+ text-decoration: none;
47
+ color: var(--fui-text-primary, $fui-text-primary);
48
+ font-weight: var(--fui-font-weight-semibold, $fui-font-weight-semibold);
49
+ font-size: var(--fui-font-size-base, $fui-font-size-base);
50
+ flex-shrink: 0;
51
+
52
+ &:hover {
53
+ color: var(--fui-text-primary, $fui-text-primary);
54
+ }
55
+
56
+ img,
57
+ svg {
58
+ height: 32px;
59
+ width: auto;
60
+ }
61
+ }
62
+
63
+ // ============================================
64
+ // Navigation
65
+ // ============================================
66
+
67
+ .nav {
68
+ display: none;
69
+
70
+ @include breakpoint-md {
71
+ display: flex;
72
+ align-items: center;
73
+ }
74
+ }
75
+
76
+ .navList {
77
+ display: flex;
78
+ align-items: center;
79
+ gap: var(--fui-space-1, $fui-space-1);
80
+ list-style: none;
81
+ margin: 0;
82
+ padding: 0;
83
+ }
84
+
85
+ .navItem {
86
+ @include button-reset;
87
+ @include interactive-base;
88
+ @include text-base;
89
+
90
+ display: flex;
91
+ align-items: center;
92
+ padding: var(--fui-space-1, $fui-space-1) var(--fui-space-3, $fui-space-3);
93
+ border-radius: var(--fui-radius-md, $fui-radius-md);
94
+ color: var(--fui-text-secondary, $fui-text-secondary);
95
+ text-decoration: none;
96
+ white-space: nowrap;
97
+ min-height: 36px;
98
+
99
+ &:hover {
100
+ background-color: var(--fui-bg-hover, $fui-bg-hover);
101
+ color: var(--fui-text-primary, $fui-text-primary);
102
+ }
103
+
104
+ &:active {
105
+ background-color: var(--fui-bg-active, $fui-bg-active);
106
+ }
107
+ }
108
+
109
+ .navItemActive {
110
+ color: var(--fui-text-primary, $fui-text-primary);
111
+ font-weight: var(--fui-font-weight-medium, $fui-font-weight-medium);
112
+ background-color: var(--fui-bg-hover, $fui-bg-hover);
113
+ }
114
+
115
+ // ============================================
116
+ // Search
117
+ // ============================================
118
+
119
+ .search {
120
+ display: none;
121
+
122
+ @include breakpoint-md {
123
+ display: flex;
124
+ align-items: center;
125
+ }
126
+ }
127
+
128
+ .searchExpandable {
129
+ display: flex;
130
+ }
131
+
132
+ // ============================================
133
+ // Actions
134
+ // ============================================
135
+
136
+ .actions {
137
+ display: flex;
138
+ align-items: center;
139
+ gap: var(--fui-space-2, $fui-space-2);
140
+ margin-left: auto;
141
+ }
142
+
143
+ // ============================================
144
+ // Trigger (Mobile menu button)
145
+ // ============================================
146
+
147
+ .trigger {
148
+ @include button-reset;
149
+ @include interactive-base;
150
+
151
+ display: flex;
152
+ align-items: center;
153
+ justify-content: center;
154
+ width: 44px;
155
+ height: 44px;
156
+ border-radius: var(--fui-radius-md, $fui-radius-md);
157
+ color: var(--fui-text-primary, $fui-text-primary);
158
+ flex-shrink: 0;
159
+
160
+ &:hover {
161
+ background-color: var(--fui-bg-hover, $fui-bg-hover);
162
+ }
163
+
164
+ svg {
165
+ width: 24px;
166
+ height: 24px;
167
+ }
168
+ }
169
+
170
+ // ============================================
171
+ // Spacer
172
+ // ============================================
173
+
174
+ .spacer {
175
+ flex: 1;
176
+ }
177
+
178
+ // ============================================
179
+ // Skip Link (Accessibility)
180
+ // ============================================
181
+
182
+ .skipLink {
183
+ @include visually-hidden;
184
+
185
+ position: absolute;
186
+ top: var(--fui-space-2, $fui-space-2);
187
+ left: var(--fui-space-2, $fui-space-2);
188
+ z-index: 100;
189
+ padding: var(--fui-space-2, $fui-space-2) var(--fui-space-4, $fui-space-4);
190
+ background-color: var(--fui-bg-primary, $fui-bg-primary);
191
+ color: var(--fui-text-primary, $fui-text-primary);
192
+ border: 2px solid var(--fui-color-accent, $fui-color-accent);
193
+ border-radius: var(--fui-radius-md, $fui-radius-md);
194
+ text-decoration: none;
195
+ font-weight: var(--fui-font-weight-medium, $fui-font-weight-medium);
196
+
197
+ &:focus {
198
+ position: absolute;
199
+ width: auto;
200
+ height: auto;
201
+ clip: auto;
202
+ white-space: normal;
203
+ margin: 0;
204
+ padding: var(--fui-space-2, $fui-space-2) var(--fui-space-4, $fui-space-4);
205
+ overflow: visible;
206
+
207
+ @include focus-ring;
208
+ }
209
+ }