sonance-brand-mcp 1.3.1 → 1.3.3

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 (136) hide show
  1. package/dist/assets/api/sonance-analyze/route.ts +1116 -0
  2. package/dist/assets/api/sonance-assets/route.ts +113 -0
  3. package/dist/assets/api/sonance-components/route.ts +41 -0
  4. package/dist/assets/api/sonance-inject-id/route.ts +363 -0
  5. package/dist/assets/api/sonance-save-logo/route.ts +426 -0
  6. package/dist/assets/api/sonance-theme/route.ts +106 -0
  7. package/dist/assets/brand-system.ts +1265 -0
  8. package/dist/assets/components/accordion.stories.tsx +26 -26
  9. package/dist/assets/components/accordion.tsx +3 -3
  10. package/dist/assets/components/alert-dialog.stories.tsx +7 -7
  11. package/dist/assets/components/alert-dialog.tsx +2 -1
  12. package/dist/assets/components/alert.stories.tsx +3 -3
  13. package/dist/assets/components/alert.tsx +4 -3
  14. package/dist/assets/components/aspect-ratio.stories.tsx +4 -1
  15. package/dist/assets/components/autocomplete.stories.tsx +9 -9
  16. package/dist/assets/components/autocomplete.tsx +3 -3
  17. package/dist/assets/components/avatar.stories.tsx +5 -5
  18. package/dist/assets/components/avatar.tsx +4 -4
  19. package/dist/assets/components/badge.stories.tsx +10 -10
  20. package/dist/assets/components/badge.tsx +3 -3
  21. package/dist/assets/components/breadcrumbs.stories.tsx +7 -7
  22. package/dist/assets/components/breadcrumbs.tsx +13 -8
  23. package/dist/assets/components/button.stories.tsx +74 -74
  24. package/dist/assets/components/button.tsx +2 -0
  25. package/dist/assets/components/calendar.stories.tsx +11 -11
  26. package/dist/assets/components/calendar.tsx +4 -4
  27. package/dist/assets/components/card.stories.tsx +22 -22
  28. package/dist/assets/components/card.tsx +7 -3
  29. package/dist/assets/components/carousel.stories.tsx +6 -6
  30. package/dist/assets/components/carousel.tsx +10 -8
  31. package/dist/assets/components/chart.tsx +5 -5
  32. package/dist/assets/components/checkbox-group.stories.tsx +6 -6
  33. package/dist/assets/components/checkbox-group.tsx +3 -3
  34. package/dist/assets/components/checkbox.stories.tsx +23 -20
  35. package/dist/assets/components/checkbox.tsx +13 -16
  36. package/dist/assets/components/code.stories.tsx +24 -24
  37. package/dist/assets/components/code.tsx +7 -14
  38. package/dist/assets/components/collapsible.stories.tsx +3 -3
  39. package/dist/assets/components/command.stories.tsx +14 -14
  40. package/dist/assets/components/command.tsx +4 -3
  41. package/dist/assets/components/context-menu.stories.tsx +1 -1
  42. package/dist/assets/components/context-menu.tsx +3 -7
  43. package/dist/assets/components/date-input.stories.tsx +9 -9
  44. package/dist/assets/components/date-input.tsx +2 -2
  45. package/dist/assets/components/date-picker.stories.tsx +9 -9
  46. package/dist/assets/components/date-picker.tsx +3 -3
  47. package/dist/assets/components/date-range-picker.stories.tsx +12 -12
  48. package/dist/assets/components/date-range-picker.tsx +3 -3
  49. package/dist/assets/components/dialog.stories.tsx +40 -40
  50. package/dist/assets/components/dialog.tsx +8 -12
  51. package/dist/assets/components/divider.stories.tsx +30 -30
  52. package/dist/assets/components/divider.tsx +4 -8
  53. package/dist/assets/components/drawer.stories.tsx +32 -31
  54. package/dist/assets/components/drawer.tsx +7 -6
  55. package/dist/assets/components/dropdown-menu.tsx +3 -7
  56. package/dist/assets/components/dropdown.stories.tsx +12 -12
  57. package/dist/assets/components/dropdown.tsx +5 -5
  58. package/dist/assets/components/form.stories.tsx +30 -29
  59. package/dist/assets/components/form.tsx +5 -5
  60. package/dist/assets/components/hover-card.stories.tsx +12 -10
  61. package/dist/assets/components/hover-card.tsx +1 -1
  62. package/dist/assets/components/image.stories.tsx +48 -25
  63. package/dist/assets/components/image.tsx +8 -5
  64. package/dist/assets/components/input-otp.stories.tsx +15 -15
  65. package/dist/assets/components/input-otp.tsx +5 -5
  66. package/dist/assets/components/input.stories.tsx +30 -25
  67. package/dist/assets/components/input.tsx +7 -4
  68. package/dist/assets/components/kbd.stories.tsx +34 -34
  69. package/dist/assets/components/kbd.tsx +5 -5
  70. package/dist/assets/components/link.stories.tsx +36 -36
  71. package/dist/assets/components/link.tsx +4 -0
  72. package/dist/assets/components/listbox.stories.tsx +5 -5
  73. package/dist/assets/components/listbox.tsx +4 -4
  74. package/dist/assets/components/menubar.tsx +3 -7
  75. package/dist/assets/components/navbar.stories.tsx +24 -24
  76. package/dist/assets/components/navbar.tsx +8 -14
  77. package/dist/assets/components/navigation-menu.stories.tsx +11 -9
  78. package/dist/assets/components/navigation-menu.tsx +1 -1
  79. package/dist/assets/components/number-input.stories.tsx +11 -11
  80. package/dist/assets/components/number-input.tsx +3 -3
  81. package/dist/assets/components/pagination.stories.tsx +13 -13
  82. package/dist/assets/components/pagination.tsx +6 -6
  83. package/dist/assets/components/popover.stories.tsx +35 -35
  84. package/dist/assets/components/popover.tsx +98 -15
  85. package/dist/assets/components/progress.stories.tsx +5 -5
  86. package/dist/assets/components/progress.tsx +5 -5
  87. package/dist/assets/components/radio-group.stories.tsx +7 -7
  88. package/dist/assets/components/radio-group.tsx +3 -3
  89. package/dist/assets/components/range-calendar.stories.tsx +18 -18
  90. package/dist/assets/components/range-calendar.tsx +3 -3
  91. package/dist/assets/components/resizable.stories.tsx +23 -23
  92. package/dist/assets/components/resizable.tsx +1 -1
  93. package/dist/assets/components/scroll-area.stories.tsx +15 -15
  94. package/dist/assets/components/scroll-area.tsx +1 -1
  95. package/dist/assets/components/scroll-shadow.stories.tsx +17 -17
  96. package/dist/assets/components/scroll-shadow.tsx +2 -2
  97. package/dist/assets/components/select.stories.tsx +20 -19
  98. package/dist/assets/components/select.tsx +10 -6
  99. package/dist/assets/components/separator.tsx +1 -1
  100. package/dist/assets/components/sheet.tsx +3 -7
  101. package/dist/assets/components/sidebar.stories.tsx +30 -30
  102. package/dist/assets/components/sidebar.tsx +24 -27
  103. package/dist/assets/components/skeleton.stories.tsx +3 -3
  104. package/dist/assets/components/skeleton.tsx +2 -2
  105. package/dist/assets/components/slider.stories.tsx +6 -6
  106. package/dist/assets/components/slider.tsx +3 -3
  107. package/dist/assets/components/spacer.stories.tsx +11 -11
  108. package/dist/assets/components/spacer.tsx +2 -2
  109. package/dist/assets/components/spinner.stories.tsx +8 -8
  110. package/dist/assets/components/spinner.tsx +5 -5
  111. package/dist/assets/components/switch.stories.tsx +24 -20
  112. package/dist/assets/components/switch.tsx +14 -6
  113. package/dist/assets/components/table.stories.tsx +7 -7
  114. package/dist/assets/components/table.tsx +8 -8
  115. package/dist/assets/components/tabs.stories.tsx +37 -37
  116. package/dist/assets/components/tabs.tsx +3 -3
  117. package/dist/assets/components/textarea.stories.tsx +13 -12
  118. package/dist/assets/components/textarea.tsx +3 -3
  119. package/dist/assets/components/theme-toggle.stories.tsx +31 -30
  120. package/dist/assets/components/theme-toggle.tsx +2 -2
  121. package/dist/assets/components/time-input.stories.tsx +16 -16
  122. package/dist/assets/components/time-input.tsx +2 -2
  123. package/dist/assets/components/toast.stories.tsx +8 -5
  124. package/dist/assets/components/toast.tsx +6 -6
  125. package/dist/assets/components/toggle-group.tsx +1 -1
  126. package/dist/assets/components/toggle.tsx +1 -1
  127. package/dist/assets/components/tooltip.stories.tsx +49 -27
  128. package/dist/assets/components/tooltip.tsx +1 -1
  129. package/dist/assets/components/user.stories.tsx +23 -23
  130. package/dist/assets/components/user.tsx +7 -4
  131. package/dist/assets/dev-tools/SonanceDevTools.tsx +4201 -0
  132. package/dist/assets/dev-tools/index.ts +10 -0
  133. package/dist/assets/globals.css +9 -0
  134. package/dist/assets/styles/brand-overrides.css +37 -0
  135. package/dist/index.js +1882 -7
  136. package/package.json +1 -1
@@ -96,20 +96,23 @@ export const Password: Story = {
96
96
  export const AllStates: Story = {
97
97
  render: () => (
98
98
  <div className="space-y-6 w-80">
99
- <Input placeholder="Default input" />
100
- <Input label="With Label" placeholder="Enter text..." />
99
+ <Input id="all-states-input-default-input" placeholder="Default input" />
100
+ <Input id="all-states-input-enter-text" label="With Label" placeholder="Enter text..." />
101
101
  <Input
102
+ id="all-states-input-enter-email"
102
103
  label="With Error"
103
104
  placeholder="Enter email..."
104
105
  error="This field is required"
105
106
  defaultValue="invalid"
106
107
  />
107
108
  <Input
109
+ id="all-states-input-cannot-edit"
108
110
  label="Disabled"
109
111
  placeholder="Cannot edit..."
110
112
  disabled
111
113
  />
112
114
  <Input
115
+ id="all-states-input"
113
116
  label="With Value"
114
117
  defaultValue="Pre-filled value"
115
118
  />
@@ -121,10 +124,10 @@ export const AllStates: Story = {
121
124
  export const FormExample: Story = {
122
125
  render: () => (
123
126
  <form className="space-y-4 w-80">
124
- <Input label="First Name" placeholder="John" />
125
- <Input label="Last Name" placeholder="Doe" />
126
- <Input label="Email" type="email" placeholder="john@example.com" />
127
- <Input label="Phone" type="tel" placeholder="+1 (555) 000-0000" />
127
+ <Input id="form-example-input-john" label="First Name" placeholder="John" />
128
+ <Input id="form-example-input-doe" label="Last Name" placeholder="Doe" />
129
+ <Input id="form-example-input-johnexamplecom" label="Email" type="email" placeholder="john@example.com" />
130
+ <Input id="form-example-input-1-555-0000000" label="Phone" type="tel" placeholder="+1 (555) 000-0000" />
128
131
  </form>
129
132
  ),
130
133
  };
@@ -135,13 +138,14 @@ export const StateMatrix: Story = {
135
138
  const states: InputState[] = ['default', 'hover', 'focus', 'error', 'disabled'];
136
139
 
137
140
  return (
138
- <div className="space-y-6 w-96">
139
- <h3 className="text-sm font-medium text-foreground-muted">Input States</h3>
141
+ <div data-sonance-name="input.stories" className="space-y-6 w-96">
142
+ <h3 id="state-matrix-h3-input-states" className="text-sm font-medium text-foreground-muted">Input States</h3>
140
143
  <div className="grid grid-cols-1 gap-4">
141
144
  {states.map((state) => (
142
145
  <div key={state} className="flex items-center gap-4">
143
- <span className="text-xs font-medium text-foreground-muted uppercase w-20">{state}</span>
146
+ <span id="state-matrix-span-state" className="text-xs font-medium text-foreground-muted uppercase w-20">{state}</span>
144
147
  <Input
148
+ id="state-matrix-input"
145
149
  state={state}
146
150
  placeholder={`${state} state`}
147
151
  defaultValue={state !== 'default' ? 'Input value' : ''}
@@ -161,18 +165,19 @@ export const LabeledStateMatrix: Story = {
161
165
 
162
166
  return (
163
167
  <div className="space-y-6">
164
- <h3 className="text-sm font-medium text-foreground-muted">Input with Label - All States</h3>
168
+ <h3 id="labeled-state-matrix-h3-input-with-label-all" className="text-sm font-medium text-foreground-muted">Input with Label - All States</h3>
165
169
  <div className="grid grid-cols-5 gap-4">
166
170
  {states.map((state) => (
167
171
  <div key={state} className="text-center">
168
172
  <Input
173
+ id="labeled-state-matrix-input-nameexamplecom"
169
174
  label="Email"
170
175
  state={state}
171
176
  placeholder="name@example.com"
172
177
  defaultValue={state !== 'default' ? 'user@email.com' : ''}
173
178
  error={state === 'error' ? 'Invalid email' : undefined}
174
179
  />
175
- <p className="text-xs text-foreground-muted mt-2 uppercase">{state}</p>
180
+ <p id="labeled-state-matrix-p-state" className="text-xs text-foreground-muted mt-2 uppercase">{state}</p>
176
181
  </div>
177
182
  ))}
178
183
  </div>
@@ -187,34 +192,34 @@ export const ResponsiveMatrix: Story = {
187
192
  <div className="space-y-8">
188
193
  {/* Mobile */}
189
194
  <div>
190
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
195
+ <h4 id="responsive-matrix-h4-mobile-375px" className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
191
196
  <div className="w-[375px] border border-dashed border-border p-4 space-y-4">
192
- <Input label="Email" placeholder="name@example.com" />
193
- <Input label="Password" type="password" placeholder="Enter password" />
194
- <Input label="Error State" error="This field is required" defaultValue="Invalid" />
197
+ <Input id="responsive-matrix-input-nameexamplecom" label="Email" placeholder="name@example.com" />
198
+ <Input id="responsive-matrix-input-enter-password" label="Password" type="password" placeholder="Enter password" />
199
+ <Input id="responsive-matrix-input" label="Error State" error="This field is required" defaultValue="Invalid" />
195
200
  </div>
196
201
  </div>
197
202
  {/* Tablet */}
198
203
  <div>
199
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
204
+ <h4 id="responsive-matrix-h4-tablet-768px" className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
200
205
  <div className="w-[768px] border border-dashed border-border p-4">
201
206
  <div className="grid grid-cols-2 gap-4">
202
- <Input label="First Name" placeholder="John" />
203
- <Input label="Last Name" placeholder="Doe" />
204
- <Input label="Email" type="email" placeholder="john@example.com" />
205
- <Input label="Phone" type="tel" placeholder="+1 (555) 000-0000" />
207
+ <Input id="responsive-matrix-input-john" label="First Name" placeholder="John" />
208
+ <Input id="responsive-matrix-input-doe" label="Last Name" placeholder="Doe" />
209
+ <Input id="responsive-matrix-input-johnexamplecom" label="Email" type="email" placeholder="john@example.com" />
210
+ <Input id="responsive-matrix-input-1-555-0000000" label="Phone" type="tel" placeholder="+1 (555) 000-0000" />
206
211
  </div>
207
212
  </div>
208
213
  </div>
209
214
  {/* Desktop */}
210
215
  <div>
211
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
216
+ <h4 id="responsive-matrix-h4-desktop-1280px" className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
212
217
  <div className="w-[1280px] border border-dashed border-border p-4">
213
218
  <div className="grid grid-cols-4 gap-4">
214
- <Input label="First Name" placeholder="John" />
215
- <Input label="Last Name" placeholder="Doe" />
216
- <Input label="Email" type="email" placeholder="john@example.com" />
217
- <Input label="Phone" type="tel" placeholder="+1 (555) 000-0000" />
219
+ <Input id="responsive-matrix-input-john" label="First Name" placeholder="John" />
220
+ <Input id="responsive-matrix-input-doe" label="Last Name" placeholder="Doe" />
221
+ <Input id="responsive-matrix-input-johnexamplecom" label="Email" type="email" placeholder="john@example.com" />
222
+ <Input id="responsive-matrix-input-1-555-0000000" label="Phone" type="tel" placeholder="+1 (555) 000-0000" />
218
223
  </div>
219
224
  </div>
220
225
  </div>
@@ -8,6 +8,8 @@ interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
8
8
  error?: string;
9
9
  /** Visual state for Storybook/Figma documentation */
10
10
  state?: InputState;
11
+ /** Container style for the wrapper div */
12
+ wrapperStyle?: React.CSSProperties;
11
13
  }
12
14
 
13
15
  // State styles for Storybook/Figma visualization
@@ -25,12 +27,12 @@ const getStateStyles = (state?: InputState) => {
25
27
  };
26
28
 
27
29
  export const Input = forwardRef<HTMLInputElement, InputProps>(
28
- ({ className, label, error, state, disabled, ...props }, ref) => {
30
+ ({ className, label, error, state, disabled, style, wrapperStyle, ...props }, ref) => {
29
31
  const isDisabled = disabled || state === "disabled";
30
32
  const hasError = error || state === "error";
31
33
 
32
34
  return (
33
- <div className="w-full">
35
+ <div data-sonance-name="input" className="w-full" style={wrapperStyle}>
34
36
  {label && (
35
37
  <label className="mb-2 block text-xs font-medium uppercase tracking-widest text-foreground-muted">
36
38
  {label}
@@ -39,6 +41,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
39
41
  <input
40
42
  ref={ref}
41
43
  disabled={isDisabled}
44
+ style={style}
42
45
  className={cn(
43
46
  "w-full border border-input-border bg-input px-4 py-3",
44
47
  "text-foreground placeholder:text-input-placeholder",
@@ -49,10 +52,10 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
49
52
  hasError && "border-error",
50
53
  getStateStyles(state),
51
54
  className
52
- )}
55
+ )} data-sonance-name="input"
53
56
  {...props}
54
57
  />
55
- {error && <p className="mt-1 text-sm text-error">{error}</p>}
58
+ {error && <p id="input-p-error" className="mt-1 text-sm text-error">{error}</p>}
56
59
  </div>
57
60
  );
58
61
  }
@@ -50,27 +50,27 @@ export const CommonShortcuts: Story = {
50
50
  render: () => (
51
51
  <div className="space-y-3">
52
52
  <div className="flex items-center justify-between">
53
- <span className="text-foreground-secondary">Copy</span>
53
+ <span id="common-shortcuts-span-copy" className="text-foreground-secondary">Copy</span>
54
54
  <Kbd keys={shortcuts.copy} />
55
55
  </div>
56
56
  <div className="flex items-center justify-between">
57
- <span className="text-foreground-secondary">Paste</span>
57
+ <span id="common-shortcuts-span-paste" className="text-foreground-secondary">Paste</span>
58
58
  <Kbd keys={shortcuts.paste} />
59
59
  </div>
60
60
  <div className="flex items-center justify-between">
61
- <span className="text-foreground-secondary">Undo</span>
61
+ <span id="common-shortcuts-span-undo" className="text-foreground-secondary">Undo</span>
62
62
  <Kbd keys={shortcuts.undo} />
63
63
  </div>
64
64
  <div className="flex items-center justify-between">
65
- <span className="text-foreground-secondary">Redo</span>
65
+ <span id="common-shortcuts-span-redo" className="text-foreground-secondary">Redo</span>
66
66
  <Kbd keys={shortcuts.redo} />
67
67
  </div>
68
68
  <div className="flex items-center justify-between">
69
- <span className="text-foreground-secondary">Save</span>
69
+ <span id="common-shortcuts-span-save" className="text-foreground-secondary">Save</span>
70
70
  <Kbd keys={shortcuts.save} />
71
71
  </div>
72
72
  <div className="flex items-center justify-between">
73
- <span className="text-foreground-secondary">Find</span>
73
+ <span id="common-shortcuts-span-find" className="text-foreground-secondary">Find</span>
74
74
  <Kbd keys={shortcuts.find} />
75
75
  </div>
76
76
  </div>
@@ -81,15 +81,15 @@ export const Variants: Story = {
81
81
  render: () => (
82
82
  <div className="space-y-4">
83
83
  <div className="flex items-center gap-4">
84
- <span className="text-xs text-foreground-muted w-20">Default</span>
84
+ <span id="variants-span-default" className="text-xs text-foreground-muted w-20">Default</span>
85
85
  <Kbd variant="default" keys={['cmd', 'shift', 'p']} />
86
86
  </div>
87
87
  <div className="flex items-center gap-4">
88
- <span className="text-xs text-foreground-muted w-20">Outline</span>
88
+ <span id="variants-span-outline" className="text-xs text-foreground-muted w-20">Outline</span>
89
89
  <Kbd variant="outline" keys={['cmd', 'shift', 'p']} />
90
90
  </div>
91
91
  <div className="flex items-center gap-4">
92
- <span className="text-xs text-foreground-muted w-20">Flat</span>
92
+ <span id="variants-span-flat" className="text-xs text-foreground-muted w-20">Flat</span>
93
93
  <Kbd variant="flat" keys={['cmd', 'shift', 'p']} />
94
94
  </div>
95
95
  </div>
@@ -100,15 +100,15 @@ export const Sizes: Story = {
100
100
  render: () => (
101
101
  <div className="space-y-4">
102
102
  <div className="flex items-center gap-4">
103
- <span className="text-xs text-foreground-muted w-20">Small</span>
103
+ <span id="sizes-span-small" className="text-xs text-foreground-muted w-20">Small</span>
104
104
  <Kbd size="sm" keys={['cmd', 'k']} />
105
105
  </div>
106
106
  <div className="flex items-center gap-4">
107
- <span className="text-xs text-foreground-muted w-20">Medium</span>
107
+ <span id="sizes-span-medium" className="text-xs text-foreground-muted w-20">Medium</span>
108
108
  <Kbd size="md" keys={['cmd', 'k']} />
109
109
  </div>
110
110
  <div className="flex items-center gap-4">
111
- <span className="text-xs text-foreground-muted w-20">Large</span>
111
+ <span id="sizes-span-large" className="text-xs text-foreground-muted w-20">Large</span>
112
112
  <Kbd size="lg" keys={['cmd', 'k']} />
113
113
  </div>
114
114
  </div>
@@ -118,48 +118,48 @@ export const Sizes: Story = {
118
118
  export const KeyMapping: Story = {
119
119
  render: () => (
120
120
  <div className="space-y-3">
121
- <p className="text-xs text-foreground-muted mb-4">
121
+ <p id="key-mapping-p-common-key-names-are" className="text-xs text-foreground-muted mb-4">
122
122
  Common key names are automatically mapped to symbols:
123
123
  </p>
124
124
  <div className="grid grid-cols-2 gap-4">
125
125
  <div className="flex items-center gap-2">
126
- <span className="text-foreground-secondary text-sm">cmd</span>
127
- <span>→</span>
126
+ <span id="key-mapping-span-cmd" className="text-foreground-secondary text-sm">cmd</span>
127
+ <span id="key-mapping-span">→</span>
128
128
  <Kbd>cmd</Kbd>
129
129
  </div>
130
130
  <div className="flex items-center gap-2">
131
- <span className="text-foreground-secondary text-sm">ctrl</span>
132
- <span>→</span>
131
+ <span id="key-mapping-span-ctrl" className="text-foreground-secondary text-sm">ctrl</span>
132
+ <span id="key-mapping-span">→</span>
133
133
  <Kbd>ctrl</Kbd>
134
134
  </div>
135
135
  <div className="flex items-center gap-2">
136
- <span className="text-foreground-secondary text-sm">alt</span>
137
- <span>→</span>
136
+ <span id="key-mapping-span-alt" className="text-foreground-secondary text-sm">alt</span>
137
+ <span id="key-mapping-span">→</span>
138
138
  <Kbd>alt</Kbd>
139
139
  </div>
140
140
  <div className="flex items-center gap-2">
141
- <span className="text-foreground-secondary text-sm">shift</span>
142
- <span>→</span>
141
+ <span id="key-mapping-span-shift" className="text-foreground-secondary text-sm">shift</span>
142
+ <span id="key-mapping-span">→</span>
143
143
  <Kbd>shift</Kbd>
144
144
  </div>
145
145
  <div className="flex items-center gap-2">
146
- <span className="text-foreground-secondary text-sm">enter</span>
147
- <span>→</span>
146
+ <span id="key-mapping-span-enter" className="text-foreground-secondary text-sm">enter</span>
147
+ <span id="key-mapping-span">→</span>
148
148
  <Kbd>enter</Kbd>
149
149
  </div>
150
150
  <div className="flex items-center gap-2">
151
- <span className="text-foreground-secondary text-sm">escape</span>
152
- <span>→</span>
151
+ <span id="key-mapping-span-escape" className="text-foreground-secondary text-sm">escape</span>
152
+ <span id="key-mapping-span">→</span>
153
153
  <Kbd>escape</Kbd>
154
154
  </div>
155
155
  <div className="flex items-center gap-2">
156
- <span className="text-foreground-secondary text-sm">backspace</span>
157
- <span>→</span>
156
+ <span id="key-mapping-span-backspace" className="text-foreground-secondary text-sm">backspace</span>
157
+ <span id="key-mapping-span">→</span>
158
158
  <Kbd>backspace</Kbd>
159
159
  </div>
160
160
  <div className="flex items-center gap-2">
161
- <span className="text-foreground-secondary text-sm">up</span>
162
- <span>→</span>
161
+ <span id="key-mapping-span-up" className="text-foreground-secondary text-sm">up</span>
162
+ <span id="key-mapping-span">→</span>
163
163
  <Kbd>up</Kbd>
164
164
  </div>
165
165
  </div>
@@ -194,7 +194,7 @@ export const InContext: Story = {
194
194
  key={item.label}
195
195
  className="w-full flex items-center justify-between px-3 py-2 text-sm text-foreground hover:bg-secondary-hover"
196
196
  >
197
- <span>{item.label}</span>
197
+ <span id="in-context-span-itemlabel">{item.label}</span>
198
198
  <Kbd keys={item.keys} size="sm" variant="flat" />
199
199
  </button>
200
200
  ))}
@@ -224,17 +224,17 @@ export const ResponsiveMatrix: Story = {
224
224
  <div className="space-y-8">
225
225
  {/* Mobile */}
226
226
  <div>
227
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
227
+ <h4 id="responsive-matrix-h4-mobile-375px" className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
228
228
  <div className="w-[375px] border border-dashed border-border p-4">
229
229
  <div className="flex items-center justify-between">
230
- <span className="text-foreground-secondary text-sm">Save</span>
230
+ <span id="responsive-matrix-span-save" className="text-foreground-secondary text-sm">Save</span>
231
231
  <Kbd keys={['cmd', 's']} size="sm" />
232
232
  </div>
233
233
  </div>
234
234
  </div>
235
235
  {/* Tablet */}
236
236
  <div>
237
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
237
+ <h4 id="responsive-matrix-h4-tablet-768px" className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
238
238
  <div className="w-[768px] border border-dashed border-border p-4">
239
239
  <div className="grid grid-cols-2 gap-4">
240
240
  <KeyboardShortcut label="Copy" keys={['cmd', 'c']} />
@@ -246,7 +246,7 @@ export const ResponsiveMatrix: Story = {
246
246
  </div>
247
247
  {/* Desktop */}
248
248
  <div>
249
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
249
+ <h4 id="responsive-matrix-h4-desktop-1280px" className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
250
250
  <div className="w-[1280px] border border-dashed border-border p-4">
251
251
  <div className="flex items-center justify-between">
252
252
  <div className="flex items-center gap-8">
@@ -36,12 +36,12 @@ export const Kbd = forwardRef<HTMLElement, KbdProps>(
36
36
  if (keys) {
37
37
  const keyArray = Array.isArray(keys) ? keys : [keys];
38
38
  return (
39
- <span className="inline-flex items-center gap-1">
39
+ <span id="kbd-span" data-sonance-name="kbd" className="inline-flex items-center gap-1">
40
40
  {keyArray.map((key, index) => (
41
41
  <kbd
42
42
  key={index}
43
43
  ref={index === 0 ? ref : undefined}
44
- className={cn(kbdVariants({ variant, size }), className)}
44
+ className={cn(kbdVariants({ variant, size }), className)} data-sonance-name="kbd"
45
45
  {...props}
46
46
  >
47
47
  {formatKey(key)}
@@ -54,7 +54,7 @@ export const Kbd = forwardRef<HTMLElement, KbdProps>(
54
54
  return (
55
55
  <kbd
56
56
  ref={ref}
57
- className={cn(kbdVariants({ variant, size }), className)}
57
+ className={cn(kbdVariants({ variant, size }), className)} data-sonance-name="kbd"
58
58
  {...props}
59
59
  >
60
60
  {children}
@@ -103,8 +103,8 @@ interface KeyboardShortcutProps {
103
103
 
104
104
  export function KeyboardShortcut({ keys, label, className }: KeyboardShortcutProps) {
105
105
  return (
106
- <span className={cn("inline-flex items-center gap-1.5 sm:gap-2 flex-wrap", className)}>
107
- {label && <span className="text-xs sm:text-sm text-foreground-muted">{label}</span>}
106
+ <span id="keyboard-shortcut-span" data-sonance-name="kbd" className={cn("inline-flex items-center gap-1.5 sm:gap-2 flex-wrap", className)}>
107
+ {label && <span id="keyboard-shortcut-span-label" className="text-xs sm:text-sm text-foreground-muted">{label}</span>}
108
108
  <Kbd keys={keys} />
109
109
  </span>
110
110
  );
@@ -86,9 +86,9 @@ export const ExternalWithoutIcon: Story = {
86
86
  export const Sizes: Story = {
87
87
  render: () => (
88
88
  <div className="flex items-center gap-4">
89
- <Link href="#" size="sm">Small</Link>
90
- <Link href="#" size="md">Medium</Link>
91
- <Link href="#" size="lg">Large</Link>
89
+ <Link id="sizes-link" href="#" size="sm">Small</Link>
90
+ <Link id="sizes-link" href="#" size="md">Medium</Link>
91
+ <Link id="sizes-link" href="#" size="lg">Large</Link>
92
92
  </div>
93
93
  ),
94
94
  };
@@ -97,28 +97,28 @@ export const AllVariants: Story = {
97
97
  render: () => (
98
98
  <div className="space-y-4">
99
99
  <div>
100
- <p className="text-xs text-foreground-muted mb-1">Default</p>
101
- <Link href="#">Click here to learn more</Link>
100
+ <p id="all-variants-p-default" className="text-xs text-foreground-muted mb-1">Default</p>
101
+ <Link id="all-variants-link" href="#">Click here to learn more</Link>
102
102
  </div>
103
103
  <div>
104
- <p className="text-xs text-foreground-muted mb-1">Primary</p>
105
- <Link href="#" variant="primary">View documentation</Link>
104
+ <p id="all-variants-p-primary" className="text-xs text-foreground-muted mb-1">Primary</p>
105
+ <Link id="all-variants-link-primary" href="#" variant="primary">View documentation</Link>
106
106
  </div>
107
107
  <div>
108
- <p className="text-xs text-foreground-muted mb-1">Muted</p>
109
- <Link href="#" variant="muted">Terms of service</Link>
108
+ <p id="all-variants-p-muted" className="text-xs text-foreground-muted mb-1">Muted</p>
109
+ <Link id="all-variants-link-muted" href="#" variant="muted">Terms of service</Link>
110
110
  </div>
111
111
  <div>
112
- <p className="text-xs text-foreground-muted mb-1">Subtle</p>
113
- <Link href="#" variant="subtle">Privacy policy</Link>
112
+ <p id="all-variants-p-subtle" className="text-xs text-foreground-muted mb-1">Subtle</p>
113
+ <Link id="all-variants-link-subtle" href="#" variant="subtle">Privacy policy</Link>
114
114
  </div>
115
115
  <div>
116
- <p className="text-xs text-foreground-muted mb-1">Nav</p>
117
- <Link href="#" variant="nav">Navigation item</Link>
116
+ <p id="all-variants-p-nav" className="text-xs text-foreground-muted mb-1">Nav</p>
117
+ <Link id="all-variants-link-nav" href="#" variant="nav">Navigation item</Link>
118
118
  </div>
119
119
  <div>
120
- <p className="text-xs text-foreground-muted mb-1">External</p>
121
- <Link href="https://sonance.com">Visit Sonance</Link>
120
+ <p id="all-variants-p-external" className="text-xs text-foreground-muted mb-1">External</p>
121
+ <Link id="all-variants-link" href="https://sonance.com">Visit Sonance</Link>
122
122
  </div>
123
123
  </div>
124
124
  ),
@@ -126,10 +126,10 @@ export const AllVariants: Story = {
126
126
 
127
127
  export const InParagraph: Story = {
128
128
  render: () => (
129
- <p className="text-foreground-secondary max-w-md">
129
+ <p id="in-paragraph-p" className="text-foreground-secondary max-w-md">
130
130
  Sonance has been creating premium audio solutions for over 35 years.{' '}
131
- <Link href="#">Learn more about our history</Link> or{' '}
132
- <Link href="https://sonance.com" variant="primary">visit our website</Link>{' '}
131
+ <Link id="in-paragraph-link" href="#">Learn more about our history</Link> or{' '}
132
+ <Link id="in-paragraph-link-primary" href="https://sonance.com" variant="primary">visit our website</Link>{' '}
133
133
  for the latest products.
134
134
  </p>
135
135
  ),
@@ -139,10 +139,10 @@ export const AnchorExample: Story = {
139
139
  render: () => (
140
140
  <Anchor href="#" className="group">
141
141
  <div className="p-4 border border-border hover:border-primary transition-colors">
142
- <h3 className="font-medium text-foreground group-hover:text-primary transition-colors">
142
+ <h3 id="anchor-example-h3-clickable-card" className="font-medium text-foreground group-hover:text-primary transition-colors">
143
143
  Clickable Card
144
144
  </h3>
145
- <p className="text-sm text-foreground-muted">
145
+ <p id="anchor-example-p-the-anchor-component" className="text-sm text-foreground-muted">
146
146
  The Anchor component wraps any element in an unstyled link.
147
147
  </p>
148
148
  </div>
@@ -156,40 +156,40 @@ export const ResponsiveMatrix: Story = {
156
156
  <div className="space-y-8">
157
157
  {/* Mobile */}
158
158
  <div>
159
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
159
+ <h4 id="responsive-matrix-h4-mobile-375px" className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
160
160
  <div className="w-[375px] border border-dashed border-border p-4">
161
- <p className="text-foreground-secondary text-sm">
162
- Visit <Link href="#">our products</Link> or <Link href="https://sonance.com">sonance.com</Link>.
161
+ <p id="responsive-matrix-p" className="text-foreground-secondary text-sm">
162
+ Visit <Link id="responsive-matrix-link" href="#">our products</Link> or <Link id="responsive-matrix-link" href="https://sonance.com">sonance.com</Link>.
163
163
  </p>
164
164
  </div>
165
165
  </div>
166
166
  {/* Tablet */}
167
167
  <div>
168
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
168
+ <h4 id="responsive-matrix-h4-tablet-768px" className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
169
169
  <div className="w-[768px] border border-dashed border-border p-4">
170
170
  <div className="flex items-center gap-6">
171
- <Link href="#" variant="nav">Home</Link>
172
- <Link href="#" variant="nav">Products</Link>
173
- <Link href="#" variant="nav">About</Link>
174
- <Link href="#" variant="nav">Contact</Link>
171
+ <Link id="responsive-matrix-link-nav" href="#" variant="nav">Home</Link>
172
+ <Link id="responsive-matrix-link-nav" href="#" variant="nav">Products</Link>
173
+ <Link id="responsive-matrix-link-nav" href="#" variant="nav">About</Link>
174
+ <Link id="responsive-matrix-link-nav" href="#" variant="nav">Contact</Link>
175
175
  </div>
176
176
  </div>
177
177
  </div>
178
178
  {/* Desktop */}
179
179
  <div>
180
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
180
+ <h4 id="responsive-matrix-h4-desktop-1280px" className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
181
181
  <div className="w-[1280px] border border-dashed border-border p-4">
182
182
  <div className="flex items-center justify-between">
183
183
  <div className="flex items-center gap-6">
184
- <Link href="#" variant="nav">Home</Link>
185
- <Link href="#" variant="nav">Products</Link>
186
- <Link href="#" variant="nav">Support</Link>
187
- <Link href="#" variant="nav">About</Link>
184
+ <Link id="responsive-matrix-link-nav" href="#" variant="nav">Home</Link>
185
+ <Link id="responsive-matrix-link-nav" href="#" variant="nav">Products</Link>
186
+ <Link id="responsive-matrix-link-nav" href="#" variant="nav">Support</Link>
187
+ <Link id="responsive-matrix-link-nav" href="#" variant="nav">About</Link>
188
188
  </div>
189
189
  <div className="flex items-center gap-4">
190
- <Link href="#" variant="muted" size="sm">Terms</Link>
191
- <Link href="#" variant="muted" size="sm">Privacy</Link>
192
- <Link href="https://sonance.com" variant="primary">Visit Sonance</Link>
190
+ <Link id="responsive-matrix-link-muted" href="#" variant="muted" size="sm">Terms</Link>
191
+ <Link id="responsive-matrix-link-muted" href="#" variant="muted" size="sm">Privacy</Link>
192
+ <Link id="responsive-matrix-link-primary" href="https://sonance.com" variant="primary">Visit Sonance</Link>
193
193
  </div>
194
194
  </div>
195
195
  </div>
@@ -97,6 +97,8 @@ export const Link = forwardRef<HTMLAnchorElement, LinkProps>(
97
97
 
98
98
  return (
99
99
  <a
100
+ id="link-a"
101
+ data-sonance-name="link"
100
102
  ref={ref}
101
103
  href={href}
102
104
  className={cn(
@@ -130,8 +132,10 @@ export const Anchor = forwardRef<
130
132
 
131
133
  return (
132
134
  <a
135
+ id="anchor-a"
133
136
  ref={ref}
134
137
  href={href}
138
+ data-sonance-name="link"
135
139
  className={cn("focus:outline-none focus-visible:ring-2 focus-visible:ring-border-focus", className)}
136
140
  {...(isExternal && {
137
141
  target: "_blank",
@@ -208,8 +208,8 @@ export const StateMatrix: Story = {
208
208
  render: () => {
209
209
  const states: ListboxItemState[] = ['default', 'hover', 'focus', 'selected', 'disabled'];
210
210
  return (
211
- <div className="space-y-6 w-64">
212
- <h3 className="text-sm font-medium text-foreground-muted">ListboxItem States</h3>
211
+ <div data-sonance-name="listbox.stories" className="space-y-6 w-64">
212
+ <h3 id="state-matrix-h3-listboxitem-states" className="text-sm font-medium text-foreground-muted">ListboxItem States</h3>
213
213
  <Listbox>
214
214
  {states.map((state) => (
215
215
  <ListboxItem key={state} value={state} state={state}>
@@ -228,7 +228,7 @@ export const ResponsiveMatrix: Story = {
228
228
  <div className="space-y-8">
229
229
  {/* Mobile */}
230
230
  <div>
231
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
231
+ <h4 id="responsive-matrix-h4-mobile-375px" className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
232
232
  <div className="w-[375px] border border-dashed border-border p-4">
233
233
  <Listbox label="Select Speaker" defaultValue="speaker1">
234
234
  <ListboxItem value="speaker1">VP66 TL</ListboxItem>
@@ -239,7 +239,7 @@ export const ResponsiveMatrix: Story = {
239
239
  </div>
240
240
  {/* Tablet */}
241
241
  <div>
242
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
242
+ <h4 id="responsive-matrix-h4-tablet-768px" className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
243
243
  <div className="w-[768px] border border-dashed border-border p-4">
244
244
  <div className="grid grid-cols-2 gap-4">
245
245
  <Listbox label="Product Category" defaultValue="inwall">
@@ -257,7 +257,7 @@ export const ResponsiveMatrix: Story = {
257
257
  </div>
258
258
  {/* Desktop */}
259
259
  <div>
260
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
260
+ <h4 id="responsive-matrix-h4-desktop-1280px" className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
261
261
  <div className="w-[1280px] border border-dashed border-border p-4">
262
262
  <div className="grid grid-cols-3 gap-4">
263
263
  <Listbox label="Select Product" defaultValue="vp66">
@@ -129,7 +129,7 @@ export const ListboxItem = forwardRef<HTMLLIElement, ListboxItemProps>(
129
129
  const finalSelected = isSelected || isSelectedState;
130
130
 
131
131
  return (
132
- <li
132
+ <li data-sonance-name="listbox"
133
133
  ref={ref}
134
134
  role="option"
135
135
  aria-selected={finalSelected}
@@ -147,7 +147,7 @@ export const ListboxItem = forwardRef<HTMLLIElement, ListboxItemProps>(
147
147
  )}
148
148
  >
149
149
  {startContent && (
150
- <span className="shrink-0">{startContent}</span>
150
+ <span id="listbox-item-span-startcontent" className="shrink-0">{startContent}</span>
151
151
  )}
152
152
  <div className="flex-1 min-w-0">
153
153
  <div className="text-sm font-medium truncate">{children}</div>
@@ -163,7 +163,7 @@ export const ListboxItem = forwardRef<HTMLLIElement, ListboxItemProps>(
163
163
  )}
164
164
  </div>
165
165
  {endContent && (
166
- <span className="shrink-0">{endContent}</span>
166
+ <span id="listbox-item-span-endcontent" className="shrink-0">{endContent}</span>
167
167
  )}
168
168
  {finalSelected && !endContent && (
169
169
  <Check className="h-4 w-4 shrink-0" />
@@ -184,7 +184,7 @@ interface ListboxSectionProps {
184
184
 
185
185
  export function ListboxSection({ title, children, className }: ListboxSectionProps) {
186
186
  return (
187
- <li className={className}>
187
+ <li data-sonance-name="listbox" className={className}>
188
188
  {title && (
189
189
  <div className="px-4 py-2 text-xs font-medium uppercase tracking-widest text-foreground-muted bg-background-secondary">
190
190
  {title}