sonance-brand-mcp 1.0.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 (55) hide show
  1. package/dist/assets/BRAND_GUIDELINES.md +361 -0
  2. package/dist/assets/components/accordion.tsx +141 -0
  3. package/dist/assets/components/alert.tsx +78 -0
  4. package/dist/assets/components/autocomplete.tsx +246 -0
  5. package/dist/assets/components/avatar.tsx +105 -0
  6. package/dist/assets/components/badge.tsx +64 -0
  7. package/dist/assets/components/breadcrumbs.tsx +149 -0
  8. package/dist/assets/components/button.tsx +50 -0
  9. package/dist/assets/components/calendar.tsx +145 -0
  10. package/dist/assets/components/card.tsx +70 -0
  11. package/dist/assets/components/checkbox-group.tsx +170 -0
  12. package/dist/assets/components/checkbox.tsx +58 -0
  13. package/dist/assets/components/code.tsx +175 -0
  14. package/dist/assets/components/date-input.tsx +113 -0
  15. package/dist/assets/components/date-picker.tsx +114 -0
  16. package/dist/assets/components/date-range-picker.tsx +133 -0
  17. package/dist/assets/components/dialog.tsx +158 -0
  18. package/dist/assets/components/divider.tsx +50 -0
  19. package/dist/assets/components/drawer.tsx +149 -0
  20. package/dist/assets/components/dropdown.tsx +213 -0
  21. package/dist/assets/components/form.tsx +190 -0
  22. package/dist/assets/components/image.tsx +173 -0
  23. package/dist/assets/components/input-otp.tsx +176 -0
  24. package/dist/assets/components/input.tsx +37 -0
  25. package/dist/assets/components/kbd.tsx +126 -0
  26. package/dist/assets/components/link.tsx +99 -0
  27. package/dist/assets/components/listbox.tsx +174 -0
  28. package/dist/assets/components/navbar.tsx +212 -0
  29. package/dist/assets/components/number-input.tsx +204 -0
  30. package/dist/assets/components/pagination.tsx +191 -0
  31. package/dist/assets/components/popover.tsx +111 -0
  32. package/dist/assets/components/progress.tsx +119 -0
  33. package/dist/assets/components/radio-group.tsx +123 -0
  34. package/dist/assets/components/range-calendar.tsx +206 -0
  35. package/dist/assets/components/scroll-shadow.tsx +131 -0
  36. package/dist/assets/components/select.tsx +183 -0
  37. package/dist/assets/components/skeleton.tsx +114 -0
  38. package/dist/assets/components/slider.tsx +155 -0
  39. package/dist/assets/components/spacer.tsx +72 -0
  40. package/dist/assets/components/spinner.tsx +77 -0
  41. package/dist/assets/components/switch.tsx +64 -0
  42. package/dist/assets/components/table.tsx +122 -0
  43. package/dist/assets/components/tabs.tsx +127 -0
  44. package/dist/assets/components/textarea.tsx +38 -0
  45. package/dist/assets/components/theme-toggle.tsx +81 -0
  46. package/dist/assets/components/time-input.tsx +176 -0
  47. package/dist/assets/components/toast.tsx +193 -0
  48. package/dist/assets/components/tooltip.tsx +92 -0
  49. package/dist/assets/components/user.tsx +171 -0
  50. package/dist/assets/globals.css +483 -0
  51. package/dist/assets/logo-manifest.json +65 -0
  52. package/dist/assets/utils.ts +6 -0
  53. package/dist/index.d.ts +2 -0
  54. package/dist/index.js +447 -0
  55. package/package.json +49 -0
@@ -0,0 +1,361 @@
1
+ # Sonance Brand Family - AI Context Guide
2
+
3
+ > **Purpose**: This document is optimized for AI/LLM consumption. Use it to generate brand-compliant UI components, websites, and applications for Sonance, IPORT, and Blaze Audio.
4
+
5
+ ---
6
+
7
+ ## Quick Reference
8
+
9
+ | Brand | Primary Color | Accent Color | Theme |
10
+ |-------|---------------|--------------|-------|
11
+ | **Sonance** | Charcoal `#333F48` | Cyan "The Beam" `#00D3C8` | Light preferred |
12
+ | **Sonance Foundation** | Charcoal `#333F48` | Green `#00B2A9` | Light preferred |
13
+ | **IPORT** | Black `#0E1114` | Orange `#FC4C02` | Dark preferred |
14
+ | **Blaze Audio** | Black `#1A1A1C` | Blue `#00A3E1` | Dark preferred |
15
+
16
+ **Font**: Montserrat (all brands)
17
+
18
+ ---
19
+
20
+ ## Typography
21
+
22
+ ### Font Family
23
+ ```css
24
+ font-family: 'Montserrat', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
25
+ ```
26
+
27
+ **Google Fonts Import**:
28
+ ```css
29
+ @import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;700&display=swap');
30
+ ```
31
+
32
+ ### Font Weights
33
+ | Weight | Name | Usage |
34
+ |--------|------|-------|
35
+ | 300 | Light | Headlines, display text |
36
+ | 400 | Regular | Body text, paragraphs |
37
+ | 500 | Medium | Emphasis, subheadings |
38
+ | 600 | Semibold | Strong emphasis |
39
+ | 700 | Bold | CTAs, buttons (small text only) |
40
+
41
+ ### Typography Rules
42
+
43
+ **Headlines**:
44
+ - Use Light (300) or Medium (500) weight
45
+ - **Never use bold for large display text**
46
+ - Letter-spacing: `-0.02em` for display sizes
47
+ - Line-height: `1.2`
48
+
49
+ **Body Text**:
50
+ - Use Regular (400) weight
51
+ - Line-height: `1.6` to `1.75`
52
+ - Max-width: `65-75` characters per line
53
+
54
+ **All Caps Text**:
55
+ - When using all caps, apply letter-spacing: `0.08em` to `0.1em`
56
+ - Use for: headlines, sub headlines, product names, labels
57
+ - **Never apply tracking to long-form body copy**
58
+
59
+ ---
60
+
61
+ ## Sonance Brand
62
+
63
+ ### Color Palette
64
+
65
+ | Color | Hex | CSS Variable | Tailwind Class | Usage |
66
+ |-------|-----|--------------|----------------|-------|
67
+ | Charcoal | `#333F48` | `--sonance-charcoal` | `bg-sonance-charcoal` | Primary brand color, text |
68
+ | The Beam (Cyan) | `#00D3C8` | `--sonance-blue` | `bg-sonance-blue` | Accent, highlights |
69
+ | Light Gray | `#D9D9D6` | `--sonance-light-gray` | `bg-sonance-light-gray` | Backgrounds, borders |
70
+ | White | `#FFFFFF` | `--sonance-white` | `bg-sonance-white` | Backgrounds |
71
+ | Black | `#000000` | `--sonance-black` | `bg-sonance-black` | Text on light backgrounds |
72
+
73
+ **Print Specifications** (for reference):
74
+ - Charcoal: Pantone 7545 C, CMYK 72/51/39/36, RAL 7024
75
+ - The Beam: Pantone 3262 C, CMYK 79/0/32/0
76
+
77
+ ### Gray Scale (derived from Charcoal)
78
+ | Level | Hex | CSS Variable |
79
+ |-------|-----|--------------|
80
+ | 50 | `#f8f9fa` | `--sonance-gray-50` |
81
+ | 100 | `#f0f2f3` | `--sonance-gray-100` |
82
+ | 200 | `#D9D9D6` | `--sonance-gray-200` |
83
+ | 300 | `#b8bfc4` | `--sonance-gray-300` |
84
+ | 400 | `#8f999f` | `--sonance-gray-400` |
85
+ | 500 | `#6b7780` | `--sonance-gray-500` |
86
+ | 600 | `#515c64` | `--sonance-gray-600` |
87
+ | 700 | `#424c54` | `--sonance-gray-700` |
88
+ | 800 | `#3a444c` | `--sonance-gray-800` |
89
+ | 900 | `#333F48` | `--sonance-gray-900` |
90
+
91
+ ### Brand Pillars
92
+
93
+ 1. **Sound Quality & Heritage** - "40+ years of sonic innovation"
94
+ 2. **Designed to Disappear** - Invisible integration is core
95
+ 3. **Partnership Excellence** - We succeed when our partners succeed
96
+ 4. **Premium Experience** - Every touchpoint feels high-end
97
+
98
+ ### Logo Usage
99
+
100
+ - Use 2-color logo (with cyan Beam) for primary applications
101
+ - Use reverse (white) logo on dark backgrounds
102
+ - Minimum clear space: equal to cap height of logo
103
+ - Minimum width: 120px (digital), 1 inch (print)
104
+
105
+ **Don'ts**:
106
+ - Never colorize, rotate, stretch, or add strokes to the logo
107
+ - Never place on busy backgrounds
108
+ - Never modify proportions
109
+
110
+ ---
111
+
112
+ ## IPORT Brand
113
+
114
+ ### Color Palette
115
+
116
+ | Color | Hex | CSS Variable | Tailwind Class | Usage |
117
+ |-------|-----|--------------|----------------|-------|
118
+ | Orange | `#FC4C02` | `--iport-orange` | `bg-iport-orange` | Primary accent, CTAs |
119
+ | Black | `#0E1114` | `--iport-black` | `bg-iport-black` | Text, headers |
120
+ | Dark | `#0F161D` | `--iport-dark` | `bg-iport-dark` | Page backgrounds |
121
+ | Dark Gray | `#1C1E20` | `--iport-dark-gray` | `bg-iport-dark-gray` | Card backgrounds |
122
+ | Medium Gray | `#25282A` | `--iport-medium-gray` | `bg-iport-medium-gray` | Secondary surfaces |
123
+ | Gray | `#3A3D3F` | `--iport-gray` | `bg-iport-gray` | Borders, dividers |
124
+ | Light Gray | `#CED6DB` | `--iport-light-gray` | `bg-iport-light-gray` | Secondary text |
125
+ | Off White | `#F2F2F2` | `--iport-off-white` | `bg-iport-off-white` | Highlights |
126
+ | White | `#FFFFFF` | `--iport-white` | `bg-iport-white` | Primary text on dark |
127
+
128
+ ### Design Guidelines
129
+
130
+ - **Theme**: Dark UI preferred
131
+ - **Accent**: Orange `#FC4C02` for CTAs, highlights, interactive elements
132
+ - **Text**: White on dark backgrounds
133
+ - **Surfaces**: Use dark grays for layered depth
134
+
135
+ ### Example Dark Theme Setup
136
+ ```css
137
+ body {
138
+ background-color: #0F161D; /* iport-dark */
139
+ color: #FFFFFF; /* iport-white */
140
+ }
141
+ .card {
142
+ background-color: #1C1E20; /* iport-dark-gray */
143
+ border-color: #3A3D3F; /* iport-gray */
144
+ }
145
+ .button-primary {
146
+ background-color: #FC4C02; /* iport-orange */
147
+ color: #FFFFFF;
148
+ }
149
+ ```
150
+
151
+ ---
152
+
153
+ ## Blaze Audio Brand
154
+
155
+ ### Color Palette
156
+
157
+ | Color | Hex | CSS Variable | Tailwind Class | Usage |
158
+ |-------|-----|--------------|----------------|-------|
159
+ | Blue | `#00A3E1` | `--blaze-blue` | `bg-blaze-blue` | Primary accent |
160
+ | Red | `#C02B0A` | `--blaze-red` | `bg-blaze-red` | Secondary accent, alerts |
161
+ | Black | `#1A1A1C` | `--blaze-black` | `bg-blaze-black` | Text |
162
+ | Darkest | `#0A0B0F` | `--blaze-darkest` | `bg-blaze-darkest` | Deep backgrounds |
163
+ | Dark Gray | `#28282B` | `--blaze-dark-gray` | `bg-blaze-dark-gray` | Page backgrounds |
164
+ | Gray | `#313131` | `--blaze-gray` | `bg-blaze-gray` | Cards, surfaces |
165
+ | Medium Gray | `#575757` | `--blaze-medium-gray` | `bg-blaze-medium-gray` | Borders |
166
+ | Light Gray | `#838383` | `--blaze-light-gray` | `bg-blaze-light-gray` | Secondary text |
167
+ | Off White | `#F8F8F8` | `--blaze-off-white` | `bg-blaze-off-white` | Highlights |
168
+ | White | `#FFFFFF` | `--blaze-white` | `bg-blaze-white` | Primary text on dark |
169
+
170
+ ### Design Guidelines
171
+
172
+ - **Theme**: Dark UI preferred (similar to IPORT)
173
+ - **Primary Accent**: Blue `#00A3E1` for CTAs, links, highlights
174
+ - **Secondary Accent**: Red `#C02B0A` for alerts, warnings, emphasis
175
+ - **Text**: White on dark backgrounds
176
+
177
+ ### Example Dark Theme Setup
178
+ ```css
179
+ body {
180
+ background-color: #28282B; /* blaze-dark-gray */
181
+ color: #FFFFFF; /* blaze-white */
182
+ }
183
+ .card {
184
+ background-color: #313131; /* blaze-gray */
185
+ border-color: #575757; /* blaze-medium-gray */
186
+ }
187
+ .button-primary {
188
+ background-color: #00A3E1; /* blaze-blue */
189
+ color: #FFFFFF;
190
+ }
191
+ .alert-error {
192
+ background-color: #C02B0A; /* blaze-red */
193
+ }
194
+ ```
195
+
196
+ ---
197
+
198
+ ## Semantic Theme Variables
199
+
200
+ For building theme-aware components, use these semantic variables:
201
+
202
+ ### Backgrounds
203
+ | Variable | Light Mode | Dark Mode |
204
+ |----------|------------|-----------|
205
+ | `--background` | `#FFFFFF` | `#1a1f24` |
206
+ | `--background-secondary` | `#f8f9fa` | `#242a31` |
207
+ | `--background-tertiary` | `#f0f2f3` | `#2d343c` |
208
+ | `--card` | `#FFFFFF` | `#242a31` |
209
+
210
+ ### Text / Foreground
211
+ | Variable | Light Mode | Dark Mode |
212
+ |----------|------------|-----------|
213
+ | `--foreground` | `#333F48` | `#FFFFFF` |
214
+ | `--foreground-secondary` | `#515c64` | `#D9D9D6` |
215
+ | `--foreground-muted` | `#6b7780` | `#8f999f` |
216
+
217
+ ### Interactive
218
+ | Variable | Light Mode | Dark Mode |
219
+ |----------|------------|-----------|
220
+ | `--primary` | `#333F48` | `#FFFFFF` |
221
+ | `--primary-foreground` | `#FFFFFF` | `#333F48` |
222
+ | `--border` | `#D9D9D6` | `#3a444c` |
223
+
224
+ ### Feedback
225
+ | Variable | Usage |
226
+ |----------|-------|
227
+ | `--success` | Success states, confirmations |
228
+ | `--error` | Errors, destructive actions |
229
+ | `--warning` | Warnings, caution states |
230
+ | `--info` | Informational messages |
231
+
232
+ ---
233
+
234
+ ## Component Patterns
235
+
236
+ ### Buttons
237
+
238
+ **Primary Button** (Sonance):
239
+ ```jsx
240
+ <button className="bg-sonance-charcoal text-white px-6 py-3 text-sm font-medium tracking-wide uppercase hover:bg-sonance-gray-800 transition-colors">
241
+ Learn More
242
+ </button>
243
+ ```
244
+
245
+ **Primary Button** (IPORT):
246
+ ```jsx
247
+ <button className="bg-iport-orange text-white px-6 py-3 text-sm font-medium tracking-wide uppercase hover:opacity-90 transition-opacity">
248
+ Shop Now
249
+ </button>
250
+ ```
251
+
252
+ **Primary Button** (Blaze):
253
+ ```jsx
254
+ <button className="bg-blaze-blue text-white px-6 py-3 text-sm font-medium tracking-wide uppercase hover:opacity-90 transition-opacity">
255
+ Explore
256
+ </button>
257
+ ```
258
+
259
+ ### Cards
260
+
261
+ **Light Theme Card**:
262
+ ```jsx
263
+ <div className="bg-white border border-sonance-light-gray rounded-sm p-6 shadow-sm">
264
+ <h3 className="text-lg font-medium text-sonance-charcoal">Card Title</h3>
265
+ <p className="text-foreground-secondary mt-2">Card content...</p>
266
+ </div>
267
+ ```
268
+
269
+ **Dark Theme Card** (IPORT/Blaze):
270
+ ```jsx
271
+ <div className="bg-iport-dark-gray border border-iport-gray rounded-sm p-6">
272
+ <h3 className="text-lg font-medium text-white">Card Title</h3>
273
+ <p className="text-iport-light-gray mt-2">Card content...</p>
274
+ </div>
275
+ ```
276
+
277
+ ### Section Headers
278
+
279
+ ```jsx
280
+ <div className="mb-8">
281
+ <p className="text-xs font-medium uppercase tracking-widest text-foreground-muted mb-2">
282
+ Category Label
283
+ </p>
284
+ <h2 className="text-4xl font-light text-foreground tracking-tight">
285
+ Section Headline
286
+ </h2>
287
+ <p className="text-lg text-foreground-secondary mt-4 max-w-2xl">
288
+ Supporting description text that provides context.
289
+ </p>
290
+ </div>
291
+ ```
292
+
293
+ ---
294
+
295
+ ## Photography Guidelines
296
+
297
+ ### Product Photography
298
+ - Clean, simple backgrounds (white or gradient)
299
+ - Products should "float" in space
300
+ - Show shadow and depth but avoid clutter
301
+ - Use natural light aesthetic
302
+
303
+ ### Lifestyle Photography
304
+ - Rich, inviting scenes
305
+ - Premium architectural settings
306
+ - Products integrated naturally (invisible aesthetic)
307
+ - Warm, sophisticated color grading
308
+
309
+ **Don'ts**:
310
+ - No cartoons, illustrations, or non-photographic imagery
311
+ - No busy or cluttered backgrounds
312
+ - No artificial-looking lighting
313
+ - No low-resolution images
314
+
315
+ ---
316
+
317
+ ## Quick CSS Copy-Paste
318
+
319
+ ### Minimal Brand Setup
320
+ ```css
321
+ :root {
322
+ /* Sonance Core */
323
+ --sonance-charcoal: #333F48;
324
+ --sonance-blue: #00D3C8;
325
+ --sonance-light-gray: #D9D9D6;
326
+
327
+ /* IPORT Core */
328
+ --iport-orange: #FC4C02;
329
+ --iport-black: #0E1114;
330
+ --iport-dark: #0F161D;
331
+
332
+ /* Blaze Core */
333
+ --blaze-blue: #00A3E1;
334
+ --blaze-red: #C02B0A;
335
+ --blaze-dark-gray: #28282B;
336
+
337
+ /* Typography */
338
+ --font-primary: 'Montserrat', sans-serif;
339
+ }
340
+ ```
341
+
342
+ ---
343
+
344
+ ## AI Instructions Summary
345
+
346
+ When generating UI for these brands:
347
+
348
+ 1. **Always use Montserrat** font family
349
+ 2. **Check which brand** is being targeted (Sonance, IPORT, or Blaze)
350
+ 3. **Apply the correct theme**:
351
+ - Sonance: Light theme, charcoal + cyan accents
352
+ - IPORT: Dark theme, dark grays + orange accents
353
+ - Blaze: Dark theme, dark grays + blue/red accents
354
+ 4. **Use semantic classes** (`bg-primary`, `text-foreground`) when possible
355
+ 5. **Headlines**: Light weight, negative tracking
356
+ 6. **Body text**: Regular weight, generous line height
357
+ 7. **All caps**: Apply letter-spacing (tracking)
358
+ 8. **Buttons**: Uppercase, medium weight, tracked
359
+ 9. **Cards**: Minimal borders, subtle shadows
360
+ 10. **Spacing**: Generous whitespace, "breathable" layouts
361
+
@@ -0,0 +1,141 @@
1
+ "use client";
2
+
3
+ import { forwardRef, createContext, useContext, useState } from "react";
4
+ import { ChevronDown } from "lucide-react";
5
+ import { cn } from "@/lib/utils";
6
+
7
+ interface AccordionContextValue {
8
+ openItems: string[];
9
+ toggleItem: (value: string) => void;
10
+ type: "single" | "multiple";
11
+ }
12
+
13
+ const AccordionContext = createContext<AccordionContextValue | null>(null);
14
+
15
+ interface AccordionProps {
16
+ type?: "single" | "multiple";
17
+ defaultValue?: string | string[];
18
+ className?: string;
19
+ children: React.ReactNode;
20
+ }
21
+
22
+ export function Accordion({
23
+ type = "single",
24
+ defaultValue,
25
+ className,
26
+ children,
27
+ }: AccordionProps) {
28
+ const [openItems, setOpenItems] = useState<string[]>(() => {
29
+ if (!defaultValue) return [];
30
+ return Array.isArray(defaultValue) ? defaultValue : [defaultValue];
31
+ });
32
+
33
+ const toggleItem = (value: string) => {
34
+ setOpenItems((prev) => {
35
+ if (type === "single") {
36
+ return prev.includes(value) ? [] : [value];
37
+ }
38
+ return prev.includes(value)
39
+ ? prev.filter((v) => v !== value)
40
+ : [...prev, value];
41
+ });
42
+ };
43
+
44
+ return (
45
+ <AccordionContext.Provider value={{ openItems, toggleItem, type }}>
46
+ <div className={cn("w-full divide-y divide-border border-y border-border", className)}>
47
+ {children}
48
+ </div>
49
+ </AccordionContext.Provider>
50
+ );
51
+ }
52
+
53
+ interface AccordionItemProps {
54
+ value: string;
55
+ className?: string;
56
+ children: React.ReactNode;
57
+ }
58
+
59
+ const AccordionItemContext = createContext<{ value: string; isOpen: boolean } | null>(null);
60
+
61
+ export function AccordionItem({ value, className, children }: AccordionItemProps) {
62
+ const context = useContext(AccordionContext);
63
+ if (!context) throw new Error("AccordionItem must be used within Accordion");
64
+
65
+ const isOpen = context.openItems.includes(value);
66
+
67
+ return (
68
+ <AccordionItemContext.Provider value={{ value, isOpen }}>
69
+ <div className={cn("", className)} data-state={isOpen ? "open" : "closed"}>
70
+ {children}
71
+ </div>
72
+ </AccordionItemContext.Provider>
73
+ );
74
+ }
75
+
76
+ export const AccordionTrigger = forwardRef<
77
+ HTMLButtonElement,
78
+ React.ButtonHTMLAttributes<HTMLButtonElement>
79
+ >(({ className, children, ...props }, ref) => {
80
+ const accordionContext = useContext(AccordionContext);
81
+ const itemContext = useContext(AccordionItemContext);
82
+
83
+ if (!accordionContext || !itemContext) {
84
+ throw new Error("AccordionTrigger must be used within AccordionItem");
85
+ }
86
+
87
+ return (
88
+ <button
89
+ ref={ref}
90
+ type="button"
91
+ onClick={() => accordionContext.toggleItem(itemContext.value)}
92
+ className={cn(
93
+ "flex w-full items-center justify-between py-4 text-left font-medium text-foreground",
94
+ "transition-colors hover:text-foreground-secondary",
95
+ className
96
+ )}
97
+ aria-expanded={itemContext.isOpen}
98
+ {...props}
99
+ >
100
+ {children}
101
+ <ChevronDown
102
+ className={cn(
103
+ "h-4 w-4 shrink-0 text-foreground-muted transition-transform duration-200",
104
+ itemContext.isOpen && "rotate-180"
105
+ )}
106
+ />
107
+ </button>
108
+ );
109
+ });
110
+
111
+ AccordionTrigger.displayName = "AccordionTrigger";
112
+
113
+ export const AccordionContent = forwardRef<
114
+ HTMLDivElement,
115
+ React.HTMLAttributes<HTMLDivElement>
116
+ >(({ className, children, ...props }, ref) => {
117
+ const itemContext = useContext(AccordionItemContext);
118
+
119
+ if (!itemContext) {
120
+ throw new Error("AccordionContent must be used within AccordionItem");
121
+ }
122
+
123
+ return (
124
+ <div
125
+ ref={ref}
126
+ className={cn(
127
+ "overflow-hidden text-sm text-foreground-secondary",
128
+ "transition-all duration-200",
129
+ itemContext.isOpen ? "pb-4" : "h-0",
130
+ className
131
+ )}
132
+ aria-hidden={!itemContext.isOpen}
133
+ {...props}
134
+ >
135
+ {itemContext.isOpen && children}
136
+ </div>
137
+ );
138
+ });
139
+
140
+ AccordionContent.displayName = "AccordionContent";
141
+
@@ -0,0 +1,78 @@
1
+ import { forwardRef } from "react";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import { AlertCircle, CheckCircle, Info, AlertTriangle, X } from "lucide-react";
4
+ import { cn } from "@/lib/utils";
5
+
6
+ const alertVariants = cva(
7
+ "relative flex w-full items-start gap-4 border p-4 transition-colors",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: "border-border bg-background-secondary text-foreground",
12
+ success: "border-success/30 bg-success-light text-success",
13
+ error: "border-error/30 bg-error-light text-error",
14
+ warning: "border-warning/30 bg-warning-light text-warning",
15
+ info: "border-info/30 bg-info-light text-info",
16
+ },
17
+ },
18
+ defaultVariants: {
19
+ variant: "default",
20
+ },
21
+ }
22
+ );
23
+
24
+ const iconMap = {
25
+ default: Info,
26
+ success: CheckCircle,
27
+ error: AlertCircle,
28
+ warning: AlertTriangle,
29
+ info: Info,
30
+ };
31
+
32
+ interface AlertProps
33
+ extends React.HTMLAttributes<HTMLDivElement>,
34
+ VariantProps<typeof alertVariants> {
35
+ title?: string;
36
+ onClose?: () => void;
37
+ }
38
+
39
+ export const Alert = forwardRef<HTMLDivElement, AlertProps>(
40
+ ({ className, variant = "default", title, children, onClose, ...props }, ref) => {
41
+ const Icon = iconMap[variant || "default"];
42
+
43
+ return (
44
+ <div
45
+ ref={ref}
46
+ role="alert"
47
+ className={cn(alertVariants({ variant }), className)}
48
+ {...props}
49
+ >
50
+ <Icon className="h-5 w-5 shrink-0" />
51
+ <div className="flex-1">
52
+ {title && (
53
+ <h5 className="mb-1 font-medium leading-none tracking-tight">
54
+ {title}
55
+ </h5>
56
+ )}
57
+ {children && (
58
+ <div className={cn("text-sm", variant === "default" && "text-foreground-secondary")}>
59
+ {children}
60
+ </div>
61
+ )}
62
+ </div>
63
+ {onClose && (
64
+ <button
65
+ onClick={onClose}
66
+ className="shrink-0 rounded-sm p-1 opacity-70 transition-opacity hover:opacity-100"
67
+ >
68
+ <X className="h-4 w-4" />
69
+ <span className="sr-only">Close</span>
70
+ </button>
71
+ )}
72
+ </div>
73
+ );
74
+ }
75
+ );
76
+
77
+ Alert.displayName = "Alert";
78
+