@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,183 @@
1
+ import { defineRecipe } from '@fragments/core';
2
+
3
+ export default defineRecipe({
4
+ name: 'Dashboard Navigation',
5
+ description: 'Sidebar navigation pattern for dashboard applications with user profile, sections, and nested menus',
6
+ category: 'navigation',
7
+ components: ['Sidebar', 'Avatar'],
8
+ tags: ['navigation', 'sidebar', 'dashboard', 'admin', 'menu'],
9
+ code: `
10
+ // Dashboard Navigation with User Profile
11
+ // A complete sidebar navigation for admin/dashboard interfaces
12
+
13
+ function DashboardNav({ user, currentPath }) {
14
+ const [collapsed, setCollapsed] = React.useState(false);
15
+ const [projectsExpanded, setProjectsExpanded] = React.useState(false);
16
+
17
+ return (
18
+ <Sidebar
19
+ collapsed={collapsed}
20
+ onCollapsedChange={setCollapsed}
21
+ >
22
+ {/* Brand header */}
23
+ <Sidebar.Header>
24
+ <svg width="32" height="32" viewBox="0 0 256 256" fill="var(--fui-color-accent)">
25
+ <path d="M208,32H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32Zm0,176H48V48H208V208Z" />
26
+ </svg>
27
+ {!collapsed && (
28
+ <span style={{ fontWeight: 600, fontSize: '16px' }}>Dashboard</span>
29
+ )}
30
+ <Sidebar.CollapseToggle />
31
+ </Sidebar.Header>
32
+
33
+ {/* Main navigation */}
34
+ <Sidebar.Nav aria-label="Dashboard navigation">
35
+ {/* Primary section */}
36
+ <Sidebar.Section>
37
+ <Sidebar.Item
38
+ icon={<HomeIcon />}
39
+ href="/dashboard"
40
+ active={currentPath === '/dashboard'}
41
+ >
42
+ Overview
43
+ </Sidebar.Item>
44
+ <Sidebar.Item
45
+ icon={<ChartIcon />}
46
+ href="/analytics"
47
+ active={currentPath === '/analytics'}
48
+ badge="New"
49
+ >
50
+ Analytics
51
+ </Sidebar.Item>
52
+ <Sidebar.Item
53
+ icon={<InboxIcon />}
54
+ href="/inbox"
55
+ active={currentPath === '/inbox'}
56
+ badge="5"
57
+ >
58
+ Inbox
59
+ </Sidebar.Item>
60
+ </Sidebar.Section>
61
+
62
+ {/* Projects section with nested items */}
63
+ <Sidebar.Section label="Workspace">
64
+ <Sidebar.Item
65
+ icon={<FolderIcon />}
66
+ hasSubmenu
67
+ expanded={projectsExpanded}
68
+ onExpandedChange={setProjectsExpanded}
69
+ >
70
+ Projects
71
+ </Sidebar.Item>
72
+ <Sidebar.Submenu>
73
+ <Sidebar.SubItem
74
+ href="/projects/website"
75
+ active={currentPath === '/projects/website'}
76
+ >
77
+ Website Redesign
78
+ </Sidebar.SubItem>
79
+ <Sidebar.SubItem
80
+ href="/projects/mobile"
81
+ active={currentPath === '/projects/mobile'}
82
+ >
83
+ Mobile App
84
+ </Sidebar.SubItem>
85
+ <Sidebar.SubItem
86
+ href="/projects/api"
87
+ active={currentPath === '/projects/api'}
88
+ >
89
+ API Integration
90
+ </Sidebar.SubItem>
91
+ </Sidebar.Submenu>
92
+ <Sidebar.Item
93
+ icon={<UsersIcon />}
94
+ href="/team"
95
+ active={currentPath === '/team'}
96
+ >
97
+ Team Members
98
+ </Sidebar.Item>
99
+ <Sidebar.Item
100
+ icon={<CalendarIcon />}
101
+ href="/calendar"
102
+ active={currentPath === '/calendar'}
103
+ >
104
+ Calendar
105
+ </Sidebar.Item>
106
+ </Sidebar.Section>
107
+
108
+ {/* Settings section */}
109
+ <Sidebar.Section label="Account">
110
+ <Sidebar.Item
111
+ icon={<GearIcon />}
112
+ href="/settings"
113
+ active={currentPath === '/settings'}
114
+ >
115
+ Settings
116
+ </Sidebar.Item>
117
+ <Sidebar.Item
118
+ icon={<HelpIcon />}
119
+ href="/help"
120
+ active={currentPath === '/help'}
121
+ >
122
+ Help & Support
123
+ </Sidebar.Item>
124
+ </Sidebar.Section>
125
+ </Sidebar.Nav>
126
+
127
+ {/* Footer with user profile */}
128
+ <Sidebar.Footer>
129
+ <div style={{
130
+ display: 'flex',
131
+ alignItems: 'center',
132
+ gap: '12px',
133
+ padding: collapsed ? '0' : '8px',
134
+ borderRadius: '8px',
135
+ cursor: 'pointer',
136
+ }}>
137
+ <Avatar
138
+ src={user.avatar}
139
+ name={user.name}
140
+ size="sm"
141
+ />
142
+ {!collapsed && (
143
+ <div style={{ flex: 1, minWidth: 0 }}>
144
+ <div style={{
145
+ fontWeight: 500,
146
+ fontSize: '14px',
147
+ whiteSpace: 'nowrap',
148
+ overflow: 'hidden',
149
+ textOverflow: 'ellipsis',
150
+ }}>
151
+ {user.name}
152
+ </div>
153
+ <div style={{
154
+ fontSize: '12px',
155
+ color: 'var(--fui-text-secondary)',
156
+ whiteSpace: 'nowrap',
157
+ overflow: 'hidden',
158
+ textOverflow: 'ellipsis',
159
+ }}>
160
+ {user.email}
161
+ </div>
162
+ </div>
163
+ )}
164
+ </div>
165
+ </Sidebar.Footer>
166
+ </Sidebar>
167
+ );
168
+ }
169
+
170
+ // Usage example:
171
+ // <DashboardNav
172
+ // user={{ name: 'Jane Doe', email: 'jane@example.com', avatar: '/avatar.jpg' }}
173
+ // currentPath="/dashboard"
174
+ // />
175
+
176
+ // Icon components (use your preferred icon library)
177
+ const HomeIcon = () => (
178
+ <svg width="20" height="20" viewBox="0 0 256 256" fill="currentColor">
179
+ <path d="M219.31,108.68l-80-80a16,16,0,0,0-22.62,0l-80,80A15.87,15.87,0,0,0,32,120v96a8,8,0,0,0,8,8H96a8,8,0,0,0,8-8V160h48v56a8,8,0,0,0,8,8h56a8,8,0,0,0,8-8V120A15.87,15.87,0,0,0,219.31,108.68Z" />
180
+ </svg>
181
+ );
182
+ `.trim(),
183
+ });
@@ -14,6 +14,13 @@ export default defineRecipe({
14
14
  <Input type="password" />
15
15
  </FormField>
16
16
  <Button type="submit" variant="primary">Sign in</Button>
17
- {error && <Alert variant="danger">{error}</Alert>}
17
+ {error && (
18
+ <Alert severity="error">
19
+ <Alert.Icon />
20
+ <Alert.Body>
21
+ <Alert.Content>{error}</Alert.Content>
22
+ </Alert.Body>
23
+ </Alert>
24
+ )}
18
25
  `.trim(),
19
26
  });
@@ -8,30 +8,47 @@ export default defineRecipe({
8
8
  tags: ['settings', 'preferences', 'form', 'toggle', 'layout'],
9
9
  code: `
10
10
  <Grid columns={1} gap="lg">
11
- <Card title="Profile" description="Your public profile information">
12
- <Grid columns={2} gap="md">
13
- <Input label="Display Name" defaultValue={user.name} />
14
- <Input label="Email" type="email" defaultValue={user.email} />
15
- <Grid.Item colSpan="full">
16
- <Input label="Website" type="url" defaultValue={user.website} />
17
- </Grid.Item>
18
- </Grid>
11
+ <Card>
12
+ <Card.Header>
13
+ <Card.Title>Profile</Card.Title>
14
+ <Card.Description>Your public profile information</Card.Description>
15
+ </Card.Header>
16
+ <Card.Body>
17
+ <Grid columns={2} gap="md">
18
+ <Input label="Display Name" defaultValue={user.name} />
19
+ <Input label="Email" type="email" defaultValue={user.email} />
20
+ <Grid.Item colSpan="full">
21
+ <Input label="Website" type="url" defaultValue={user.website} />
22
+ </Grid.Item>
23
+ </Grid>
24
+ </Card.Body>
19
25
  </Card>
20
26
 
21
- <Card title="Notifications" description="Choose what you get notified about">
22
- <Grid columns={1} gap="sm">
23
- <Toggle label="Email notifications" checked={prefs.emailNotifs} onChange={onToggle('emailNotifs')} />
24
- <Toggle label="Push notifications" checked={prefs.pushNotifs} onChange={onToggle('pushNotifs')} />
25
- <Toggle label="Weekly digest" checked={prefs.digest} onChange={onToggle('digest')} />
26
- </Grid>
27
+ <Card>
28
+ <Card.Header>
29
+ <Card.Title>Notifications</Card.Title>
30
+ <Card.Description>Choose what you get notified about</Card.Description>
31
+ </Card.Header>
32
+ <Card.Body>
33
+ <Grid columns={1} gap="sm">
34
+ <Toggle label="Email notifications" checked={prefs.emailNotifs} onChange={onToggle('emailNotifs')} />
35
+ <Toggle label="Push notifications" checked={prefs.pushNotifs} onChange={onToggle('pushNotifs')} />
36
+ <Toggle label="Weekly digest" checked={prefs.digest} onChange={onToggle('digest')} />
37
+ </Grid>
38
+ </Card.Body>
27
39
  </Card>
28
40
 
29
- <Card title="Appearance">
30
- <Select label="Theme" value={prefs.theme} onChange={onThemeChange}>
31
- <Select.Item value="light">Light</Select.Item>
32
- <Select.Item value="dark">Dark</Select.Item>
33
- <Select.Item value="system">System</Select.Item>
34
- </Select>
41
+ <Card>
42
+ <Card.Header>
43
+ <Card.Title>Appearance</Card.Title>
44
+ </Card.Header>
45
+ <Card.Body>
46
+ <Select label="Theme" value={prefs.theme} onChange={onThemeChange}>
47
+ <Select.Item value="light">Light</Select.Item>
48
+ <Select.Item value="dark">Dark</Select.Item>
49
+ <Select.Item value="system">System</Select.Item>
50
+ </Select>
51
+ </Card.Body>
35
52
  </Card>
36
53
 
37
54
  <Separator />
@@ -15,3 +15,34 @@
15
15
 
16
16
  // Output all CSS custom properties
17
17
  @include fui-css-variables;
18
+
19
+ // ============================================
20
+ // Accessibility: Reduced Motion
21
+ // ============================================
22
+ // Respects user's system preference for reduced motion.
23
+ // Disables animations and transitions globally when enabled.
24
+
25
+ @media (prefers-reduced-motion: reduce) {
26
+ *,
27
+ *::before,
28
+ *::after {
29
+ animation-duration: 0.01ms !important;
30
+ animation-iteration-count: 1 !important;
31
+ transition-duration: 0.01ms !important;
32
+ scroll-behavior: auto !important;
33
+ }
34
+ }
35
+
36
+ // ============================================
37
+ // Accessibility: Focus Visible
38
+ // ============================================
39
+ // Ensures focus indicators are only shown for keyboard navigation
40
+
41
+ :focus:not(:focus-visible) {
42
+ outline: none;
43
+ }
44
+
45
+ :focus-visible {
46
+ outline: var(--fui-focus-ring-width, 2px) solid var(--fui-focus-ring-color, #18181b);
47
+ outline-offset: var(--fui-focus-ring-offset, 2px);
48
+ }
@@ -0,0 +1,3 @@
1
+ // Forward both variables and mixins
2
+ @forward 'variables';
3
+ @forward 'mixins';
@@ -79,12 +79,19 @@
79
79
  border-radius: var(--fui-radius-lg, #{$fui-radius-lg});
80
80
  }
81
81
 
82
- // Responsive breakpoint mixins (mobile-first)
82
+ // Responsive breakpoint mixins (mobile-first, min-width)
83
83
  @mixin breakpoint-sm { @media (min-width: $fui-breakpoint-sm) { @content; } }
84
84
  @mixin breakpoint-md { @media (min-width: $fui-breakpoint-md) { @content; } }
85
85
  @mixin breakpoint-lg { @media (min-width: $fui-breakpoint-lg) { @content; } }
86
86
  @mixin breakpoint-xl { @media (min-width: $fui-breakpoint-xl) { @content; } }
87
87
 
88
+ // Responsive breakpoint mixins (desktop-first, max-width)
89
+ // Use for hiding/changing elements below a breakpoint
90
+ @mixin below-sm { @media (max-width: #{$fui-breakpoint-sm - 1px}) { @content; } }
91
+ @mixin below-md { @media (max-width: #{$fui-breakpoint-md - 1px}) { @content; } }
92
+ @mixin below-lg { @media (max-width: #{$fui-breakpoint-lg - 1px}) { @content; } }
93
+ @mixin below-xl { @media (max-width: #{$fui-breakpoint-xl - 1px}) { @content; } }
94
+
88
95
  // Visually hidden (for screen readers)
89
96
  @mixin visually-hidden {
90
97
  position: absolute;
@@ -97,3 +104,49 @@
97
104
  white-space: nowrap;
98
105
  border: 0;
99
106
  }
107
+
108
+ // ============================================
109
+ // Accessibility Mixins
110
+ // ============================================
111
+
112
+ // Reduced motion preference - respects user's system setting
113
+ @mixin prefers-reduced-motion {
114
+ @media (prefers-reduced-motion: reduce) {
115
+ @content;
116
+ }
117
+ }
118
+
119
+ // High contrast mode support - for users who need more visual contrast
120
+ @mixin prefers-contrast {
121
+ @media (prefers-contrast: more) {
122
+ @content;
123
+ }
124
+ }
125
+
126
+ // Safe transition - respects reduced motion preference
127
+ // Usage: @include safe-transition(background-color 150ms ease, border-color 150ms ease);
128
+ @mixin safe-transition($transitions...) {
129
+ transition: $transitions;
130
+
131
+ @media (prefers-reduced-motion: reduce) {
132
+ transition: none;
133
+ }
134
+ }
135
+
136
+ // Safe animation - respects reduced motion preference
137
+ // Usage: @include safe-animation(fadeIn 200ms ease);
138
+ @mixin safe-animation($animation) {
139
+ animation: $animation;
140
+
141
+ @media (prefers-reduced-motion: reduce) {
142
+ animation: none;
143
+ }
144
+ }
145
+
146
+ // Focus ring for error states (e.g., invalid form fields)
147
+ @mixin focus-ring-error {
148
+ outline: none;
149
+ box-shadow:
150
+ 0 0 0 var(--fui-focus-ring-offset, #{$fui-focus-ring-offset}) var(--fui-bg-primary, #{$fui-bg-primary}),
151
+ 0 0 0 calc(var(--fui-focus-ring-offset, #{$fui-focus-ring-offset}) + var(--fui-focus-ring-width, #{$fui-focus-ring-width})) var(--fui-color-danger, #{$fui-color-danger});
152
+ }