@nationaldesignstudio/react 0.6.0 → 0.7.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.
- package/dist/accordion/index.d.ts +95 -0
- package/dist/accordion/index.js +143 -0
- package/dist/accordion/index.js.map +1 -0
- package/dist/background/index.d.ts +149 -0
- package/dist/background/index.js +200 -0
- package/dist/background/index.js.map +1 -0
- package/dist/banner/index.d.ts +101 -0
- package/dist/banner/index.js +81 -0
- package/dist/banner/index.js.map +1 -0
- package/dist/blurred-video-backdrop/index.d.ts +233 -0
- package/dist/blurred-video-backdrop/index.js +266 -0
- package/dist/blurred-video-backdrop/index.js.map +1 -0
- package/dist/button/index.d.ts +180 -0
- package/dist/button/index.js +169 -0
- package/dist/button/index.js.map +1 -0
- package/dist/button-B2g5fH9b.d.ts +152 -0
- package/dist/card/index.d.ts +406 -0
- package/dist/card/index.js +219 -0
- package/dist/card/index.js.map +1 -0
- package/dist/card-grid/index.d.ts +90 -0
- package/dist/card-grid/index.js +74 -0
- package/dist/card-grid/index.js.map +1 -0
- package/dist/component-registry.md +136 -2
- package/dist/dev-toolbar/index.d.ts +8 -0
- package/dist/dev-toolbar/index.js +206 -0
- package/dist/dev-toolbar/index.js.map +1 -0
- package/dist/dialog/index.d.ts +268 -0
- package/dist/dialog/index.js +288 -0
- package/dist/dialog/index.js.map +1 -0
- package/dist/faq-section/index.d.ts +47 -0
- package/dist/faq-section/index.js +152 -0
- package/dist/faq-section/index.js.map +1 -0
- package/dist/grid-overlay/index.d.ts +10 -0
- package/dist/grid-overlay/index.js +38 -0
- package/dist/grid-overlay/index.js.map +1 -0
- package/dist/hero/index.d.ts +462 -0
- package/dist/hero/index.js +494 -0
- package/dist/hero/index.js.map +1 -0
- package/dist/hooks/index.d.ts +150 -0
- package/dist/hooks/index.js +339 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/index.d.ts +46 -5339
- package/dist/index.js +157 -4080
- package/dist/index.js.map +1 -1
- package/dist/input/index.d.ts +404 -0
- package/dist/input/index.js +393 -0
- package/dist/input/index.js.map +1 -0
- package/dist/navbar/index.d.ts +68 -0
- package/dist/navbar/index.js +227 -0
- package/dist/navbar/index.js.map +1 -0
- package/dist/ndstudio-footer/index.d.ts +32 -0
- package/dist/ndstudio-footer/index.js +35 -0
- package/dist/ndstudio-footer/index.js.map +1 -0
- package/dist/pager-control/index.d.ts +173 -0
- package/dist/pager-control/index.js +267 -0
- package/dist/pager-control/index.js.map +1 -0
- package/dist/popover/index.d.ts +200 -0
- package/dist/popover/index.js +290 -0
- package/dist/popover/index.js.map +1 -0
- package/dist/prose/index.d.ts +39 -0
- package/dist/prose/index.js +56 -0
- package/dist/prose/index.js.map +1 -0
- package/dist/quote-block/index.d.ts +156 -0
- package/dist/quote-block/index.js +321 -0
- package/dist/quote-block/index.js.map +1 -0
- package/dist/river/index.d.ts +100 -0
- package/dist/river/index.js +107 -0
- package/dist/river/index.js.map +1 -0
- package/dist/select/index.d.ts +188 -0
- package/dist/select/index.js +295 -0
- package/dist/select/index.js.map +1 -0
- package/dist/theme/index.d.ts +149 -0
- package/dist/theme/index.js +211 -0
- package/dist/theme/index.js.map +1 -0
- package/dist/theme-CzBPUlh_.d.ts +332 -0
- package/dist/tooltip/index.d.ts +166 -0
- package/dist/tooltip/index.js +200 -0
- package/dist/tooltip/index.js.map +1 -0
- package/dist/tout/index.d.ts +157 -0
- package/dist/tout/index.js +315 -0
- package/dist/tout/index.js.map +1 -0
- package/dist/two-column-section/index.d.ts +122 -0
- package/dist/two-column-section/index.js +121 -0
- package/dist/two-column-section/index.js.map +1 -0
- package/dist/us-gov-banner/index.d.ts +141 -0
- package/dist/us-gov-banner/index.js +74 -0
- package/dist/us-gov-banner/index.js.map +1 -0
- package/dist/use-captions-AkKlJhov.d.ts +71 -0
- package/dist/utils/index.d.ts +7 -0
- package/dist/utils/index.js +12 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/video-dialog/index.d.ts +106 -0
- package/dist/video-dialog/index.js +1305 -0
- package/dist/video-dialog/index.js.map +1 -0
- package/dist/video-player/index.d.ts +115 -0
- package/dist/video-player/index.js +879 -0
- package/dist/video-player/index.js.map +1 -0
- package/dist/video-player-qxf-BURH.d.ts +236 -0
- package/dist/video-with-backdrop/index.d.ts +267 -0
- package/dist/video-with-backdrop/index.js +1284 -0
- package/dist/video-with-backdrop/index.js.map +1 -0
- package/package.json +13 -2
- package/src/components/organisms/us-gov-banner/us-gov-banner.tsx +5 -27
- package/src/theme/hooks.ts +2 -0
- package/src/theme/index.ts +2 -0
- package/src/theme/theme-provider.tsx +2 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/utils.ts","../../src/components/atoms/blurred-video-backdrop/blurred-video-backdrop.tsx"],"names":["twMerge"],"mappings":"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","file":"index.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { cnBase as twMerge } from \"tailwind-variants\";\n\nexport { twMerge };\n\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\nimport { cn } from \"@/lib/utils\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\ntype BlurIntensity = \"low\" | \"medium\" | \"high\" | \"extreme\";\ntype OverlayType = \"none\" | \"vignette\" | \"top-bottom\";\n\n// ============================================================================\n// Variant Definitions\n// ============================================================================\n\n/**\n * Blurred video backdrop wrapper variants.\n *\n * The wrapper extends beyond its bounds (inset: -120px) to cover\n * blur artifacts at the edges.\n */\nconst blurredVideoBackdropVariants = tv({\n\tbase: [\n\t\t\"absolute\",\n\t\t\"pointer-events-none\",\n\t\t\"select-none\",\n\t\t\"will-change-contents\",\n\t\t\"transform-gpu\",\n\t],\n\tvariants: {\n\t\t/**\n\t\t * Blur intensity level.\n\t\t * Higher values provide more diffused backgrounds.\n\t\t */\n\t\tblur: {\n\t\t\tlow: \"\",\n\t\t\tmedium: \"\",\n\t\t\thigh: \"\",\n\t\t\textreme: \"\",\n\t\t},\n\t\t/**\n\t\t * Gradient overlay for visual depth.\n\t\t */\n\t\toverlay: {\n\t\t\tnone: \"\",\n\t\t\tvignette: \"\",\n\t\t\t\"top-bottom\": \"\",\n\t\t},\n\t},\n\tdefaultVariants: {\n\t\tblur: \"high\",\n\t\toverlay: \"none\",\n\t},\n});\n\n/**\n * Canvas element styles.\n */\nconst canvasVariants = tv({\n\tbase: [\"w-full\", \"h-full\", \"object-cover\"],\n});\n\n/**\n * Gradient overlay base styles.\n * Gradient backgrounds are applied via inline styles to avoid arbitrary values.\n */\nconst gradientOverlayVariants = tv({\n\tbase: [\"absolute\", \"inset-0\", \"pointer-events-none\"],\n});\n\n/**\n * Gradient overlay background styles.\n * Using inline styles to maintain token compliance (no arbitrary values in Tailwind).\n */\nconst OVERLAY_GRADIENTS: Record<Exclude<OverlayType, \"none\">, string> = {\n\tvignette:\n\t\t\"radial-gradient(ellipse at center, transparent 40%, rgba(0, 0, 0, 0.4) 100%)\",\n\t\"top-bottom\":\n\t\t\"linear-gradient(180deg, rgba(0, 0, 0, 0.4) 0%, transparent 30%, transparent 70%, rgba(0, 0, 0, 0.4) 100%)\",\n};\n\n// ============================================================================\n// Blur amount mapping\n// ============================================================================\n\nconst BLUR_AMOUNTS: Record<BlurIntensity, number> = {\n\tlow: 40,\n\tmedium: 80,\n\thigh: 100,\n\textreme: 120,\n};\n\n// ============================================================================\n// useCanvasBlur Hook\n// ============================================================================\n\ninterface UseCanvasBlurOptions {\n\t/** Ref to the source video element */\n\tvideoRef: React.RefObject<HTMLVideoElement | null>;\n\t/** Blur amount in pixels */\n\tblurAmount: number;\n\t/** Whether rendering is enabled */\n\tenabled?: boolean;\n\t/** Target FPS (lower = better performance, default: 30) */\n\ttargetFps?: number;\n\t/** Canvas scale factor (lower = better performance, default: 0.5) */\n\tscale?: number;\n}\n\ninterface UseCanvasBlurReturn {\n\t/** Ref to attach to the canvas element */\n\tcanvasRef: React.RefObject<HTMLCanvasElement | null>;\n\t/** Whether the canvas is currently rendering */\n\tisRendering: boolean;\n\t/** Performance metrics */\n\tmetrics: {\n\t\tfps: number;\n\t\tframeTime: number;\n\t};\n}\n\n/**\n * Hook for rendering a blurred video to canvas.\n *\n * Performance optimizations:\n * - Renders at reduced resolution (scale factor)\n * - Throttled to target FPS\n * - Uses CSS scale to fill container\n * - Single video decoder (no sync needed)\n */\nfunction useCanvasBlur({\n\tvideoRef,\n\tblurAmount,\n\tenabled = true,\n\ttargetFps = 30,\n\tscale = 0.5,\n}: UseCanvasBlurOptions): UseCanvasBlurReturn {\n\tconst canvasRef = React.useRef<HTMLCanvasElement | null>(null);\n\tconst ctxRef = React.useRef<CanvasRenderingContext2D | null>(null);\n\tconst [isRendering, setIsRendering] = React.useState(false);\n\tconst [metrics, setMetrics] = React.useState({ fps: 0, frameTime: 0 });\n\n\t// Track when video ref is ready (it populates after mount)\n\tconst [videoReady, setVideoReady] = React.useState(false);\n\n\t// Performance tracking refs\n\tconst lastFrameTimeRef = React.useRef(0);\n\tconst frameCountRef = React.useRef(0);\n\tconst fpsIntervalRef = React.useRef(0);\n\n\t// Frame interval based on target FPS\n\tconst frameInterval = 1000 / targetFps;\n\n\t// Poll for video ref to be ready (refs populate after initial render)\n\tReact.useEffect(() => {\n\t\tif (!enabled) return;\n\n\t\tconst checkRef = () => {\n\t\t\tif (videoRef.current && canvasRef.current) {\n\t\t\t\tsetVideoReady(true);\n\t\t\t}\n\t\t};\n\n\t\t// Check immediately\n\t\tcheckRef();\n\n\t\t// Also check on next frames in case elements mount after this effect\n\t\tconst frameId = requestAnimationFrame(checkRef);\n\t\tconst timeoutId = setTimeout(checkRef, 100);\n\t\tconst timeoutId2 = setTimeout(checkRef, 500);\n\n\t\treturn () => {\n\t\t\tcancelAnimationFrame(frameId);\n\t\t\tclearTimeout(timeoutId);\n\t\t\tclearTimeout(timeoutId2);\n\t\t};\n\t}, [enabled, videoRef]);\n\n\t// Main rendering effect\n\tReact.useEffect(() => {\n\t\tif (!enabled || !videoReady) return;\n\n\t\tconst video = videoRef.current;\n\t\tconst canvas = canvasRef.current;\n\n\t\tif (!video || !canvas) return;\n\n\t\t// Initialize canvas context\n\t\tconst ctx = canvas.getContext(\"2d\", {\n\t\t\talpha: false,\n\t\t\tdesynchronized: true, // Reduces latency\n\t\t});\n\n\t\tif (!ctx) return;\n\t\tctxRef.current = ctx;\n\n\t\tlet animationFrameId: number;\n\t\tlet isActive = true;\n\n\t\t// Set canvas size based on video dimensions with scale factor\n\t\tconst updateCanvasSize = () => {\n\t\t\tif (video.videoWidth && video.videoHeight) {\n\t\t\t\tcanvas.width = Math.floor(video.videoWidth * scale);\n\t\t\t\tcanvas.height = Math.floor(video.videoHeight * scale);\n\t\t\t}\n\t\t};\n\n\t\tconst render = (timestamp: number) => {\n\t\t\tif (!isActive || !video || !ctx) return;\n\n\t\t\t// Throttle to target FPS\n\t\t\tconst elapsed = timestamp - lastFrameTimeRef.current;\n\n\t\t\tif (elapsed >= frameInterval) {\n\t\t\t\tconst frameStart = performance.now();\n\n\t\t\t\t// Update canvas size if needed\n\t\t\t\tif (canvas.width === 0 || canvas.height === 0) {\n\t\t\t\t\tupdateCanvasSize();\n\t\t\t\t}\n\n\t\t\t\t// Only draw if video has data and is playing\n\t\t\t\tif (video.readyState >= 2 && !video.paused) {\n\t\t\t\t\t// Apply blur filter and draw\n\t\t\t\t\tctx.filter = `blur(${blurAmount * scale}px)`;\n\t\t\t\t\tctx.drawImage(video, 0, 0, canvas.width, canvas.height);\n\n\t\t\t\t\tsetIsRendering(true);\n\t\t\t\t}\n\n\t\t\t\t// Track frame time\n\t\t\t\tconst frameTime = performance.now() - frameStart;\n\t\t\t\tframeCountRef.current++;\n\n\t\t\t\t// Update FPS every second\n\t\t\t\tif (timestamp - fpsIntervalRef.current >= 1000) {\n\t\t\t\t\tsetMetrics({\n\t\t\t\t\t\tfps: frameCountRef.current,\n\t\t\t\t\t\tframeTime: Math.round(frameTime * 100) / 100,\n\t\t\t\t\t});\n\t\t\t\t\tframeCountRef.current = 0;\n\t\t\t\t\tfpsIntervalRef.current = timestamp;\n\t\t\t\t}\n\n\t\t\t\tlastFrameTimeRef.current = timestamp - (elapsed % frameInterval);\n\t\t\t}\n\n\t\t\tanimationFrameId = requestAnimationFrame(render);\n\t\t};\n\n\t\t// Handle video events\n\t\tconst handleLoadedMetadata = () => {\n\t\t\tupdateCanvasSize();\n\t\t};\n\n\t\tconst handlePlay = () => {\n\t\t\tif (isActive) {\n\t\t\t\tanimationFrameId = requestAnimationFrame(render);\n\t\t\t}\n\t\t};\n\n\t\tconst handlePause = () => {\n\t\t\tsetIsRendering(false);\n\t\t};\n\n\t\t// Add event listeners\n\t\tvideo.addEventListener(\"loadedmetadata\", handleLoadedMetadata);\n\t\tvideo.addEventListener(\"play\", handlePlay);\n\t\tvideo.addEventListener(\"pause\", handlePause);\n\n\t\t// Initialize size if video is already loaded\n\t\tif (video.readyState >= 1) {\n\t\t\tupdateCanvasSize();\n\t\t}\n\n\t\t// Start render loop if video is playing\n\t\tif (!video.paused) {\n\t\t\tanimationFrameId = requestAnimationFrame(render);\n\t\t}\n\n\t\treturn () => {\n\t\t\tisActive = false;\n\t\t\tcancelAnimationFrame(animationFrameId);\n\t\t\tvideo.removeEventListener(\"loadedmetadata\", handleLoadedMetadata);\n\t\t\tvideo.removeEventListener(\"play\", handlePlay);\n\t\t\tvideo.removeEventListener(\"pause\", handlePause);\n\t\t\tsetIsRendering(false);\n\t\t};\n\t}, [enabled, videoReady, videoRef, blurAmount, frameInterval, scale]);\n\n\treturn {\n\t\tcanvasRef,\n\t\tisRendering,\n\t\tmetrics,\n\t};\n}\n\n// ============================================================================\n// BlurredVideoBackdrop Component\n// ============================================================================\n\nexport interface BlurredVideoBackdropProps\n\textends Omit<React.HTMLAttributes<HTMLDivElement>, \"children\">,\n\t\tVariantProps<typeof blurredVideoBackdropVariants> {\n\t/** Ref to the primary video element to create backdrop from (required) */\n\tvideoRef: React.RefObject<HTMLVideoElement | null>;\n\t/** Opacity of the backdrop (0-1, default: 0.6) */\n\topacity?: number;\n\t/** Extension amount in pixels to cover blur artifacts (default: 120) */\n\textension?: number;\n\t/** Target FPS for canvas rendering (default: 30) */\n\ttargetFps?: number;\n\t/** Canvas scale factor - lower = better performance (default: 0.5) */\n\tscale?: number;\n\t/** Whether to show debug metrics */\n\tshowMetrics?: boolean;\n}\n\n/**\n * BlurredVideoBackdrop - A high-performance blurred video backdrop using canvas.\n *\n * Renders a blurred copy of a video element to create an ambient backdrop effect.\n * Uses canvas rendering for optimal performance - no video sync needed.\n *\n * Performance features:\n * - Single video decoder (draws from existing video element)\n * - Reduced resolution rendering (configurable scale)\n * - Throttled frame rate (configurable FPS)\n * - GPU-accelerated canvas scaling\n *\n * @example\n * ```tsx\n * const videoRef = useRef<HTMLVideoElement>(null);\n *\n * <div className=\"relative\">\n * <BlurredVideoBackdrop\n * videoRef={videoRef}\n * blur=\"high\"\n * overlay=\"vignette\"\n * />\n * <VideoPlayer videoRef={videoRef} src=\"/video.mp4\" />\n * </div>\n * ```\n */\nconst BlurredVideoBackdrop = React.forwardRef<\n\tHTMLDivElement,\n\tBlurredVideoBackdropProps\n>(\n\t(\n\t\t{\n\t\t\tclassName,\n\t\t\tvideoRef,\n\t\t\tblur = \"high\",\n\t\t\toverlay = \"none\",\n\t\t\topacity = 0.6,\n\t\t\textension = 120,\n\t\t\ttargetFps = 30,\n\t\t\tscale = 0.5,\n\t\t\tshowMetrics = false,\n\t\t\tstyle,\n\t\t\t...props\n\t\t},\n\t\tref,\n\t) => {\n\t\tconst blurAmount = BLUR_AMOUNTS[blur ?? \"high\"];\n\n\t\tconst { canvasRef, isRendering, metrics } = useCanvasBlur({\n\t\t\tvideoRef,\n\t\t\tblurAmount,\n\t\t\tenabled: true,\n\t\t\ttargetFps,\n\t\t\tscale,\n\t\t});\n\n\t\treturn (\n\t\t\t<div\n\t\t\t\tref={ref}\n\t\t\t\tclassName={cn(\n\t\t\t\t\tblurredVideoBackdropVariants({ blur, overlay }),\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tstyle={{\n\t\t\t\t\tinset: `-${extension}px`,\n\t\t\t\t\topacity,\n\t\t\t\t\tcontain: \"layout style paint\",\n\t\t\t\t\t...style,\n\t\t\t\t}}\n\t\t\t\tdata-blur={blur ?? \"high\"}\n\t\t\t\tdata-overlay={overlay ?? \"none\"}\n\t\t\t\tdata-rendering={isRendering}\n\t\t\t\taria-hidden=\"true\"\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t<canvas\n\t\t\t\t\tref={canvasRef}\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\tcanvasVariants(),\n\t\t\t\t\t\t// Scale up the low-res canvas to fill container\n\t\t\t\t\t\t\"scale-[2]\", // Inverse of 0.5 scale factor\n\t\t\t\t\t\t\"origin-center\",\n\t\t\t\t\t)}\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\t// Additional CSS blur for smoother appearance\n\t\t\t\t\t\tfilter: `blur(${blurAmount * 0.3}px)`,\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t\t{overlay && overlay !== \"none\" && (\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName={gradientOverlayVariants()}\n\t\t\t\t\t\tstyle={{ background: OVERLAY_GRADIENTS[overlay] }}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t\t{showMetrics && (\n\t\t\t\t\t<div className=\"absolute bottom-8 left-8 z-10 bg-black/80 text-white typography-caption px-8 py-4 rounded-4 font-mono\">\n\t\t\t\t\t\t<div>FPS: {metrics.fps}</div>\n\t\t\t\t\t\t<div>Frame: {metrics.frameTime}ms</div>\n\t\t\t\t\t\t<div>Scale: {scale}x</div>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t);\n\t},\n);\n\nBlurredVideoBackdrop.displayName = \"BlurredVideoBackdrop\";\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport {\n\tBlurredVideoBackdrop,\n\tblurredVideoBackdropVariants,\n\tcanvasVariants,\n\tgradientOverlayVariants,\n\tuseCanvasBlur,\n\tBLUR_AMOUNTS,\n};\n\nexport type {\n\tUseCanvasBlurOptions,\n\tUseCanvasBlurReturn,\n\tBlurIntensity,\n\tOverlayType,\n};\n"]}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
export { a as Button, B as ButtonProps, b as buttonVariants } from '../button-B2g5fH9b.js';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import * as tailwind_variants from 'tailwind-variants';
|
|
4
|
+
import { VariantProps } from 'tailwind-variants';
|
|
5
|
+
import * as tailwind_variants_dist_config_js from 'tailwind-variants/dist/config.js';
|
|
6
|
+
import { useRender } from '@base-ui-components/react/use-render';
|
|
7
|
+
import '@base-ui-components/react/button';
|
|
8
|
+
import '../theme-CzBPUlh_.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* IconButton component based on Figma Button component (icon-only variant)
|
|
12
|
+
*
|
|
13
|
+
* **IMPORTANT: Accessibility Requirement**
|
|
14
|
+
* Icon-only buttons MUST have an accessible label. Provide one of:
|
|
15
|
+
* - `aria-label`: A text description of the button's action (recommended)
|
|
16
|
+
* - `aria-labelledby`: Reference to an element containing the label
|
|
17
|
+
* - `title`: Tooltip text (less preferred, but provides a label)
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```tsx
|
|
21
|
+
* // Correct usage with aria-label
|
|
22
|
+
* <IconButton aria-label="Close menu">
|
|
23
|
+
* <CloseIcon />
|
|
24
|
+
* </IconButton>
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* Variants (matches Figma):
|
|
28
|
+
* - primary: Filled brand button (indigo background)
|
|
29
|
+
* - primary-outline: Outlined brand button (indigo border/text)
|
|
30
|
+
* - secondary: Filled neutral button (white background, for dark backgrounds)
|
|
31
|
+
* - secondary-outline: Outlined neutral button (white border/text, for dark backgrounds)
|
|
32
|
+
* - ghost: Transparent button with subtle hover (for light backgrounds)
|
|
33
|
+
* - ghost-inverse: Transparent button with subtle hover (for dark backgrounds)
|
|
34
|
+
*
|
|
35
|
+
* Sizes:
|
|
36
|
+
* - sm: Small (28x28)
|
|
37
|
+
* - md: Medium (40x40) - default
|
|
38
|
+
* - lg: Large (56x56)
|
|
39
|
+
*
|
|
40
|
+
* Rounded:
|
|
41
|
+
* - default: Standard border radius (matches size)
|
|
42
|
+
* - full: Fully circular
|
|
43
|
+
*/
|
|
44
|
+
declare const iconButtonVariants: tailwind_variants.TVReturnType<{
|
|
45
|
+
variant: {
|
|
46
|
+
primary: string;
|
|
47
|
+
"primary-outline": string;
|
|
48
|
+
secondary: string;
|
|
49
|
+
"secondary-outline": string;
|
|
50
|
+
ghost: string;
|
|
51
|
+
"ghost-inverse": string;
|
|
52
|
+
};
|
|
53
|
+
size: {
|
|
54
|
+
sm: string;
|
|
55
|
+
md: string;
|
|
56
|
+
lg: string;
|
|
57
|
+
};
|
|
58
|
+
rounded: {
|
|
59
|
+
default: string;
|
|
60
|
+
full: string;
|
|
61
|
+
};
|
|
62
|
+
}, undefined, "inline-flex items-center justify-center whitespace-nowrap transition-colors duration-150 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&>svg]:shrink-0", tailwind_variants_dist_config_js.TVConfig<{
|
|
63
|
+
variant: {
|
|
64
|
+
primary: string;
|
|
65
|
+
"primary-outline": string;
|
|
66
|
+
secondary: string;
|
|
67
|
+
"secondary-outline": string;
|
|
68
|
+
ghost: string;
|
|
69
|
+
"ghost-inverse": string;
|
|
70
|
+
};
|
|
71
|
+
size: {
|
|
72
|
+
sm: string;
|
|
73
|
+
md: string;
|
|
74
|
+
lg: string;
|
|
75
|
+
};
|
|
76
|
+
rounded: {
|
|
77
|
+
default: string;
|
|
78
|
+
full: string;
|
|
79
|
+
};
|
|
80
|
+
}, {
|
|
81
|
+
variant: {
|
|
82
|
+
primary: string;
|
|
83
|
+
"primary-outline": string;
|
|
84
|
+
secondary: string;
|
|
85
|
+
"secondary-outline": string;
|
|
86
|
+
ghost: string;
|
|
87
|
+
"ghost-inverse": string;
|
|
88
|
+
};
|
|
89
|
+
size: {
|
|
90
|
+
sm: string;
|
|
91
|
+
md: string;
|
|
92
|
+
lg: string;
|
|
93
|
+
};
|
|
94
|
+
rounded: {
|
|
95
|
+
default: string;
|
|
96
|
+
full: string;
|
|
97
|
+
};
|
|
98
|
+
}>, {
|
|
99
|
+
variant: {
|
|
100
|
+
primary: string;
|
|
101
|
+
"primary-outline": string;
|
|
102
|
+
secondary: string;
|
|
103
|
+
"secondary-outline": string;
|
|
104
|
+
ghost: string;
|
|
105
|
+
"ghost-inverse": string;
|
|
106
|
+
};
|
|
107
|
+
size: {
|
|
108
|
+
sm: string;
|
|
109
|
+
md: string;
|
|
110
|
+
lg: string;
|
|
111
|
+
};
|
|
112
|
+
rounded: {
|
|
113
|
+
default: string;
|
|
114
|
+
full: string;
|
|
115
|
+
};
|
|
116
|
+
}, undefined, tailwind_variants.TVReturnType<{
|
|
117
|
+
variant: {
|
|
118
|
+
primary: string;
|
|
119
|
+
"primary-outline": string;
|
|
120
|
+
secondary: string;
|
|
121
|
+
"secondary-outline": string;
|
|
122
|
+
ghost: string;
|
|
123
|
+
"ghost-inverse": string;
|
|
124
|
+
};
|
|
125
|
+
size: {
|
|
126
|
+
sm: string;
|
|
127
|
+
md: string;
|
|
128
|
+
lg: string;
|
|
129
|
+
};
|
|
130
|
+
rounded: {
|
|
131
|
+
default: string;
|
|
132
|
+
full: string;
|
|
133
|
+
};
|
|
134
|
+
}, undefined, "inline-flex items-center justify-center whitespace-nowrap transition-colors duration-150 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&>svg]:shrink-0", tailwind_variants_dist_config_js.TVConfig<{
|
|
135
|
+
variant: {
|
|
136
|
+
primary: string;
|
|
137
|
+
"primary-outline": string;
|
|
138
|
+
secondary: string;
|
|
139
|
+
"secondary-outline": string;
|
|
140
|
+
ghost: string;
|
|
141
|
+
"ghost-inverse": string;
|
|
142
|
+
};
|
|
143
|
+
size: {
|
|
144
|
+
sm: string;
|
|
145
|
+
md: string;
|
|
146
|
+
lg: string;
|
|
147
|
+
};
|
|
148
|
+
rounded: {
|
|
149
|
+
default: string;
|
|
150
|
+
full: string;
|
|
151
|
+
};
|
|
152
|
+
}, {
|
|
153
|
+
variant: {
|
|
154
|
+
primary: string;
|
|
155
|
+
"primary-outline": string;
|
|
156
|
+
secondary: string;
|
|
157
|
+
"secondary-outline": string;
|
|
158
|
+
ghost: string;
|
|
159
|
+
"ghost-inverse": string;
|
|
160
|
+
};
|
|
161
|
+
size: {
|
|
162
|
+
sm: string;
|
|
163
|
+
md: string;
|
|
164
|
+
lg: string;
|
|
165
|
+
};
|
|
166
|
+
rounded: {
|
|
167
|
+
default: string;
|
|
168
|
+
full: string;
|
|
169
|
+
};
|
|
170
|
+
}>, unknown, unknown, undefined>>;
|
|
171
|
+
interface IconButtonState extends Record<string, unknown> {
|
|
172
|
+
variant: "primary" | "primary-outline" | "secondary" | "secondary-outline" | "ghost" | "ghost-inverse";
|
|
173
|
+
size: "sm" | "md" | "lg";
|
|
174
|
+
rounded: "default" | "full";
|
|
175
|
+
}
|
|
176
|
+
interface IconButtonProps extends useRender.ComponentProps<"button", IconButtonState>, VariantProps<typeof iconButtonVariants> {
|
|
177
|
+
}
|
|
178
|
+
declare function IconButton(props: IconButtonProps): React.ReactElement<unknown, string | React.JSXElementConstructor<any>>;
|
|
179
|
+
|
|
180
|
+
export { IconButton, type IconButtonProps, iconButtonVariants };
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Button as Button$1 } from '@base-ui-components/react/button';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { tv } from 'tailwind-variants';
|
|
5
|
+
import { jsx } from 'react/jsx-runtime';
|
|
6
|
+
import { useRender } from '@base-ui-components/react/use-render';
|
|
7
|
+
|
|
8
|
+
// src/components/atoms/button/button.tsx
|
|
9
|
+
|
|
10
|
+
// src/lib/theme.ts
|
|
11
|
+
function colorToVar(token) {
|
|
12
|
+
return `var(--color-${token})`;
|
|
13
|
+
}
|
|
14
|
+
function radiusToVar(token) {
|
|
15
|
+
return `var(--${token})`;
|
|
16
|
+
}
|
|
17
|
+
function buttonThemeToStyleVars(theme) {
|
|
18
|
+
if (!theme) return {};
|
|
19
|
+
const vars = {};
|
|
20
|
+
if (theme.bg) vars["--btn-bg"] = colorToVar(theme.bg);
|
|
21
|
+
if (theme.bgHover) vars["--btn-bg-hover"] = colorToVar(theme.bgHover);
|
|
22
|
+
if (theme.bgActive) vars["--btn-bg-active"] = colorToVar(theme.bgActive);
|
|
23
|
+
if (theme.text) vars["--btn-text"] = colorToVar(theme.text);
|
|
24
|
+
if (theme.borderColor)
|
|
25
|
+
vars["--btn-border-color"] = colorToVar(theme.borderColor);
|
|
26
|
+
if (theme.borderWidth !== void 0)
|
|
27
|
+
vars["--btn-border-width"] = `${theme.borderWidth}px`;
|
|
28
|
+
if (theme.radius) vars["--btn-radius"] = radiusToVar(theme.radius);
|
|
29
|
+
return vars;
|
|
30
|
+
}
|
|
31
|
+
var buttonVariants = tv({
|
|
32
|
+
base: "inline-flex items-center justify-center gap-spatial-ui-button-gap-icon-text whitespace-nowrap transition-colors duration-150 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-1 disabled:pointer-events-none disabled:opacity-50",
|
|
33
|
+
variants: {
|
|
34
|
+
variant: {
|
|
35
|
+
// Primary - blue filled button
|
|
36
|
+
primary: "bg-button-primary-bg text-button-primary-text hover:bg-button-primary-bg-hover active:bg-button-primary-bg-active border-transparent",
|
|
37
|
+
// Default - dark filled button
|
|
38
|
+
default: "bg-button-default-bg text-button-default-text hover:bg-button-default-bg-hover active:bg-button-default-bg-active border-transparent",
|
|
39
|
+
// Secondary - light gray filled with subtle border
|
|
40
|
+
secondary: "bg-button-secondary-bg text-button-secondary-text hover:bg-button-secondary-bg-hover active:bg-button-secondary-bg-active border border-button-secondary-border",
|
|
41
|
+
// Destructive - red filled button
|
|
42
|
+
destructive: "bg-button-destructive-bg text-button-destructive-text hover:bg-button-destructive-bg-hover active:bg-button-destructive-bg-active border-transparent",
|
|
43
|
+
// Outline - bordered with transparent background
|
|
44
|
+
outline: "bg-button-outline-bg text-button-outline-text hover:bg-button-outline-bg-hover active:bg-button-outline-bg-active border border-button-outline-border hover:border-button-outline-border-hover",
|
|
45
|
+
// Ghost - transparent with subtle hover
|
|
46
|
+
ghost: "bg-button-ghost-bg text-button-ghost-text hover:bg-button-ghost-bg-hover active:bg-button-ghost-bg-active border-transparent",
|
|
47
|
+
// Link - text only with underline on hover
|
|
48
|
+
link: "bg-transparent text-button-link-text hover:text-button-link-text-hover hover:underline active:text-button-link-text-hover border-transparent underline-offset-4",
|
|
49
|
+
// Themed - uses CSS custom properties for styling
|
|
50
|
+
themed: "[background:var(--btn-bg)] [color:var(--btn-text)] [border-color:var(--btn-border-color,transparent)] hover:[background:var(--btn-bg-hover,var(--btn-bg))] active:[background:var(--btn-bg-active,var(--btn-bg-hover,var(--btn-bg)))]"
|
|
51
|
+
},
|
|
52
|
+
size: {
|
|
53
|
+
sm: "typography-ui-text-xs h-spatial-ui-button-height-small px-spatial-ui-button-padding-x-small py-spatial-ui-button-padding-y-small rounded-surface-button-small",
|
|
54
|
+
default: "typography-ui-text-sm h-spatial-ui-button-height-medium px-spatial-ui-button-padding-x-medium py-spatial-ui-button-padding-y-medium rounded-surface-button-medium",
|
|
55
|
+
lg: "typography-ui-text-md h-spatial-ui-button-height-large px-spatial-ui-button-padding-x-large py-spatial-ui-button-padding-y-large rounded-surface-button-large"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
defaultVariants: {
|
|
59
|
+
variant: "default",
|
|
60
|
+
size: "default"
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
function hasThemeValues(theme) {
|
|
64
|
+
if (!theme) return false;
|
|
65
|
+
return Object.values(theme).some((v) => v !== void 0 && v !== null);
|
|
66
|
+
}
|
|
67
|
+
var Button = React.forwardRef(
|
|
68
|
+
({ className, variant, size, render, nativeButton, theme, style, ...props }, ref) => {
|
|
69
|
+
const isNativeButton = nativeButton ?? render === void 0;
|
|
70
|
+
const hasTheme = hasThemeValues(theme);
|
|
71
|
+
const effectiveVariant = hasTheme ? "themed" : variant;
|
|
72
|
+
const themeStyles = buttonThemeToStyleVars(theme);
|
|
73
|
+
const combinedStyles = hasTheme ? { ...themeStyles, ...style } : style;
|
|
74
|
+
const resolvedVariant = effectiveVariant ?? "default";
|
|
75
|
+
const resolvedSize = size ?? "default";
|
|
76
|
+
return /* @__PURE__ */ jsx(
|
|
77
|
+
Button$1,
|
|
78
|
+
{
|
|
79
|
+
className: buttonVariants({
|
|
80
|
+
variant: effectiveVariant,
|
|
81
|
+
size,
|
|
82
|
+
class: className
|
|
83
|
+
}),
|
|
84
|
+
ref,
|
|
85
|
+
render,
|
|
86
|
+
nativeButton: isNativeButton,
|
|
87
|
+
style: combinedStyles,
|
|
88
|
+
"data-variant": resolvedVariant,
|
|
89
|
+
"data-size": resolvedSize,
|
|
90
|
+
...props
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
Button.displayName = "Button";
|
|
96
|
+
var iconButtonVariants = tv({
|
|
97
|
+
base: "inline-flex items-center justify-center whitespace-nowrap transition-colors duration-150 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&>svg]:shrink-0",
|
|
98
|
+
variants: {
|
|
99
|
+
variant: {
|
|
100
|
+
// Primary - filled brand button
|
|
101
|
+
primary: "bg-button-primary-bg text-button-primary-text hover:bg-button-primary-bg-hover hover:text-button-primary-text-hover border-transparent focus-visible:ring-button-primary-bg",
|
|
102
|
+
// Primary Outline - outlined brand button
|
|
103
|
+
"primary-outline": "bg-button-primary-outline-bg text-button-primary-outline-text border border-button-primary-outline-border hover:bg-button-primary-outline-bg-hover hover:text-button-primary-outline-text-hover hover:border-button-primary-outline-border-hover focus-visible:ring-button-primary-outline-border",
|
|
104
|
+
// Secondary - filled neutral button (for dark backgrounds)
|
|
105
|
+
secondary: "bg-button-secondary-bg text-button-secondary-text hover:bg-button-secondary-bg-hover hover:text-button-secondary-text-hover border-transparent focus-visible:ring-button-secondary-bg focus-visible:ring-offset-gray-1000",
|
|
106
|
+
// Secondary Outline - outlined neutral button (for dark backgrounds)
|
|
107
|
+
"secondary-outline": "bg-button-secondary-outline-bg text-button-secondary-outline-text border border-button-secondary-outline-border hover:bg-button-secondary-outline-bg-hover hover:text-button-secondary-outline-text-hover hover:border-button-secondary-outline-border-hover focus-visible:ring-button-secondary-outline-border focus-visible:ring-offset-gray-1000",
|
|
108
|
+
// Ghost - transparent button (for light backgrounds)
|
|
109
|
+
ghost: "bg-button-ghost-bg text-button-ghost-text hover:bg-button-ghost-bg-hover hover:text-button-ghost-text-hover border-transparent focus-visible:ring-gray-1000",
|
|
110
|
+
// Ghost Inverse - transparent button (for dark backgrounds)
|
|
111
|
+
"ghost-inverse": "bg-button-ghost-inverse-bg text-button-ghost-inverse-text hover:bg-button-ghost-inverse-bg-hover hover:text-button-ghost-inverse-text-hover border-transparent focus-visible:ring-gray-50 focus-visible:ring-offset-gray-1000"
|
|
112
|
+
},
|
|
113
|
+
size: {
|
|
114
|
+
sm: "size-28",
|
|
115
|
+
md: "size-40",
|
|
116
|
+
lg: "size-56"
|
|
117
|
+
},
|
|
118
|
+
rounded: {
|
|
119
|
+
default: "",
|
|
120
|
+
full: "rounded-full"
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
compoundVariants: [
|
|
124
|
+
// Apply size-specific rounded only when rounded is "default"
|
|
125
|
+
{ size: "sm", rounded: "default", class: "rounded-surface-button-small" },
|
|
126
|
+
{ size: "md", rounded: "default", class: "rounded-surface-button-medium" },
|
|
127
|
+
{ size: "lg", rounded: "default", class: "rounded-surface-button-large" }
|
|
128
|
+
],
|
|
129
|
+
defaultVariants: {
|
|
130
|
+
variant: "primary",
|
|
131
|
+
size: "md",
|
|
132
|
+
rounded: "default"
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
function IconButton(props) {
|
|
136
|
+
const {
|
|
137
|
+
className,
|
|
138
|
+
variant = "primary",
|
|
139
|
+
size = "md",
|
|
140
|
+
rounded = "default",
|
|
141
|
+
render,
|
|
142
|
+
...otherProps
|
|
143
|
+
} = props;
|
|
144
|
+
const state = {
|
|
145
|
+
variant,
|
|
146
|
+
size,
|
|
147
|
+
rounded
|
|
148
|
+
};
|
|
149
|
+
const buttonClassName = iconButtonVariants({
|
|
150
|
+
variant,
|
|
151
|
+
size,
|
|
152
|
+
rounded,
|
|
153
|
+
class: className
|
|
154
|
+
});
|
|
155
|
+
return useRender({
|
|
156
|
+
render,
|
|
157
|
+
state,
|
|
158
|
+
props: {
|
|
159
|
+
type: "button",
|
|
160
|
+
className: buttonClassName,
|
|
161
|
+
...otherProps
|
|
162
|
+
},
|
|
163
|
+
defaultTagName: "button"
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export { Button, IconButton, buttonVariants, iconButtonVariants };
|
|
168
|
+
//# sourceMappingURL=index.js.map
|
|
169
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/theme.ts","../../src/components/atoms/button/button.tsx","../../src/components/atoms/button/icon-button.tsx"],"names":["BaseButton","tv"],"mappings":";;;;;;;;;AAqzBA,SAAS,WAAW,KAAA,EAA2B;AAC9C,EAAA,OAAO,eAAe,KAAK,CAAA,CAAA,CAAA;AAC5B;AAYA,SAAS,YAAY,KAAA,EAA4B;AAChD,EAAA,OAAO,SAAS,KAAK,CAAA,CAAA,CAAA;AACtB;AAgJO,SAAS,uBACf,KAAA,EACsB;AACtB,EAAA,IAAI,CAAC,KAAA,EAAO,OAAO,EAAC;AAEpB,EAAA,MAAM,OAA+B,EAAC;AAEtC,EAAA,IAAI,MAAM,EAAA,EAAI,IAAA,CAAK,UAAU,CAAA,GAAI,UAAA,CAAW,MAAM,EAAE,CAAA;AACpD,EAAA,IAAI,MAAM,OAAA,EAAS,IAAA,CAAK,gBAAgB,CAAA,GAAI,UAAA,CAAW,MAAM,OAAO,CAAA;AACpE,EAAA,IAAI,MAAM,QAAA,EAAU,IAAA,CAAK,iBAAiB,CAAA,GAAI,UAAA,CAAW,MAAM,QAAQ,CAAA;AACvE,EAAA,IAAI,MAAM,IAAA,EAAM,IAAA,CAAK,YAAY,CAAA,GAAI,UAAA,CAAW,MAAM,IAAI,CAAA;AAC1D,EAAA,IAAI,KAAA,CAAM,WAAA;AACT,IAAA,IAAA,CAAK,oBAAoB,CAAA,GAAI,UAAA,CAAW,KAAA,CAAM,WAAW,CAAA;AAC1D,EAAA,IAAI,MAAM,WAAA,KAAgB,MAAA;AACzB,IAAA,IAAA,CAAK,oBAAoB,CAAA,GAAI,CAAA,EAAG,KAAA,CAAM,WAAW,CAAA,EAAA,CAAA;AAClD,EAAA,IAAI,MAAM,MAAA,EAAQ,IAAA,CAAK,cAAc,CAAA,GAAI,WAAA,CAAY,MAAM,MAAM,CAAA;AAEjE,EAAA,OAAO,IAAA;AACR;ACv8BA,IAAM,iBAAiB,EAAA,CAAG;AAAA,EACzB,IAAA,EAAM,0SAAA;AAAA,EACN,QAAA,EAAU;AAAA,IACT,OAAA,EAAS;AAAA;AAAA,MAER,OAAA,EACC,sIAAA;AAAA;AAAA,MAED,OAAA,EACC,sIAAA;AAAA;AAAA,MAED,SAAA,EACC,iKAAA;AAAA;AAAA,MAED,WAAA,EACC,sJAAA;AAAA;AAAA,MAED,OAAA,EACC,gMAAA;AAAA;AAAA,MAED,KAAA,EACC,8HAAA;AAAA;AAAA,MAED,IAAA,EAAM,iKAAA;AAAA;AAAA,MAEN,MAAA,EACC;AAAA,KACF;AAAA,IACA,IAAA,EAAM;AAAA,MACL,EAAA,EAAI,+JAAA;AAAA,MACJ,OAAA,EACC,mKAAA;AAAA,MACD,EAAA,EAAI;AAAA;AACL,GACD;AAAA,EACA,eAAA,EAAiB;AAAA,IAChB,OAAA,EAAS,SAAA;AAAA,IACT,IAAA,EAAM;AAAA;AAER,CAAC;AAeD,SAAS,eAAe,KAAA,EAAyC;AAChE,EAAA,IAAI,CAAC,OAAO,OAAO,KAAA;AACnB,EAAA,OAAO,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,KAAM,MAAA,IAAa,CAAA,KAAM,IAAI,CAAA;AACtE;AAEA,IAAM,MAAA,GAAe,KAAA,CAAA,UAAA;AAAA,EACpB,CACC,EAAE,SAAA,EAAW,OAAA,EAAS,IAAA,EAAM,MAAA,EAAQ,YAAA,EAAc,KAAA,EAAO,KAAA,EAAO,GAAG,KAAA,EAAM,EACzE,GAAA,KACI;AAEJ,IAAA,MAAM,cAAA,GAAiB,gBAAgB,MAAA,KAAW,MAAA;AAGlD,IAAA,MAAM,QAAA,GAAW,eAAe,KAAK,CAAA;AACrC,IAAA,MAAM,gBAAA,GAAmB,WAAW,QAAA,GAAW,OAAA;AAC/C,IAAA,MAAM,WAAA,GAAc,uBAAuB,KAAK,CAAA;AAChD,IAAA,MAAM,iBAAiB,QAAA,GAAW,EAAE,GAAG,WAAA,EAAa,GAAG,OAAM,GAAI,KAAA;AAGjE,IAAA,MAAM,kBAAkB,gBAAA,IAAoB,SAAA;AAC5C,IAAA,MAAM,eAAe,IAAA,IAAQ,SAAA;AAE7B,IAAA,uBACC,GAAA;AAAA,MAACA,QAAA;AAAA,MAAA;AAAA,QACA,WAAW,cAAA,CAAe;AAAA,UACzB,OAAA,EAAS,gBAAA;AAAA,UACT,IAAA;AAAA,UACA,KAAA,EAAO;AAAA,SACP,CAAA;AAAA,QACD,GAAA;AAAA,QACA,MAAA;AAAA,QACA,YAAA,EAAc,cAAA;AAAA,QACd,KAAA,EAAO,cAAA;AAAA,QACP,cAAA,EAAc,eAAA;AAAA,QACd,WAAA,EAAW,YAAA;AAAA,QACV,GAAG;AAAA;AAAA,KACL;AAAA,EAEF;AACD;AACA,MAAA,CAAO,WAAA,GAAc,QAAA;ACxFrB,IAAM,qBAAqBC,EAAAA,CAAG;AAAA,EAC7B,IAAA,EAAM,uPAAA;AAAA,EACN,QAAA,EAAU;AAAA,IACT,OAAA,EAAS;AAAA;AAAA,MAER,OAAA,EACC,6KAAA;AAAA;AAAA,MAED,iBAAA,EACC,mSAAA;AAAA;AAAA,MAED,SAAA,EACC,2NAAA;AAAA;AAAA,MAED,mBAAA,EACC,qVAAA;AAAA;AAAA,MAED,KAAA,EACC,6JAAA;AAAA;AAAA,MAED,eAAA,EACC;AAAA,KACF;AAAA,IACA,IAAA,EAAM;AAAA,MACL,EAAA,EAAI,SAAA;AAAA,MACJ,EAAA,EAAI,SAAA;AAAA,MACJ,EAAA,EAAI;AAAA,KACL;AAAA,IACA,OAAA,EAAS;AAAA,MACR,OAAA,EAAS,EAAA;AAAA,MACT,IAAA,EAAM;AAAA;AACP,GACD;AAAA,EACA,gBAAA,EAAkB;AAAA;AAAA,IAEjB,EAAE,IAAA,EAAM,IAAA,EAAM,OAAA,EAAS,SAAA,EAAW,OAAO,8BAAA,EAA+B;AAAA,IACxE,EAAE,IAAA,EAAM,IAAA,EAAM,OAAA,EAAS,SAAA,EAAW,OAAO,+BAAA,EAAgC;AAAA,IACzE,EAAE,IAAA,EAAM,IAAA,EAAM,OAAA,EAAS,SAAA,EAAW,OAAO,8BAAA;AAA+B,GACzE;AAAA,EACA,eAAA,EAAiB;AAAA,IAChB,OAAA,EAAS,SAAA;AAAA,IACT,IAAA,EAAM,IAAA;AAAA,IACN,OAAA,EAAS;AAAA;AAEX,CAAC;AAkBD,SAAS,WAAW,KAAA,EAAwB;AAC3C,EAAA,MAAM;AAAA,IACL,SAAA;AAAA,IACA,OAAA,GAAU,SAAA;AAAA,IACV,IAAA,GAAO,IAAA;AAAA,IACP,OAAA,GAAU,SAAA;AAAA,IACV,MAAA;AAAA,IACA,GAAG;AAAA,GACJ,GAAI,KAAA;AAEJ,EAAA,MAAM,KAAA,GAAyB;AAAA,IAC9B,OAAA;AAAA,IACA,IAAA;AAAA,IACA;AAAA,GACD;AAEA,EAAA,MAAM,kBAAkB,kBAAA,CAAmB;AAAA,IAC1C,OAAA;AAAA,IACA,IAAA;AAAA,IACA,OAAA;AAAA,IACA,KAAA,EAAO;AAAA,GACP,CAAA;AAED,EAAA,OAAO,SAAA,CAA8C;AAAA,IACpD,MAAA;AAAA,IACA,KAAA;AAAA,IACA,KAAA,EAAO;AAAA,MACN,IAAA,EAAM,QAAA;AAAA,MACN,SAAA,EAAW,eAAA;AAAA,MACX,GAAG;AAAA,KACJ;AAAA,IACA,cAAA,EAAgB;AAAA,GAChB,CAAA;AACF","file":"index.js","sourcesContent":["/**\n * Component-level theming interface\n *\n * This interface defines all the customizable design tokens that can be\n * overridden at the component level. Components accepting a `theme` prop\n * will apply these values as CSS custom properties, allowing fine-grained\n * control over appearance without creating new variants.\n *\n * All values use primitive token names (e.g., \"gray-100\", \"spacing-40\")\n * which are converted to CSS variable references internally.\n */\n\n// =============================================================================\n// Token Name Types\n// =============================================================================\n\n/**\n * Color token names - maps to `--color-{name}` CSS variables\n * @example \"gray-100\", \"ember-v300\", \"alpha-black-10\"\n */\nexport type ColorToken =\n\t// Grayscale\n\t| \"gray-50\"\n\t| \"gray-100\"\n\t| \"gray-200\"\n\t| \"gray-300\"\n\t| \"gray-400\"\n\t| \"gray-500\"\n\t| \"gray-600\"\n\t| \"gray-700\"\n\t| \"gray-800\"\n\t| \"gray-900\"\n\t| \"gray-1000\"\n\t| \"gray-1100\"\n\t| \"gray-1200\"\n\t// Steel\n\t| \"steel-50\"\n\t| \"steel-100\"\n\t| \"steel-200\"\n\t| \"steel-300\"\n\t| \"steel-400\"\n\t| \"steel-500\"\n\t| \"steel-600\"\n\t| \"steel-700\"\n\t| \"steel-800\"\n\t| \"steel-900\"\n\t| \"steel-1000\"\n\t| \"steel-1100\"\n\t| \"steel-1200\"\n\t// Brown\n\t| \"brown-50\"\n\t| \"brown-100\"\n\t| \"brown-200\"\n\t| \"brown-300\"\n\t| \"brown-400\"\n\t| \"brown-500\"\n\t| \"brown-600\"\n\t| \"brown-700\"\n\t| \"brown-800\"\n\t| \"brown-900\"\n\t| \"brown-1000\"\n\t| \"brown-1100\"\n\t| \"brown-1200\"\n\t// Ember\n\t| \"ember-50\"\n\t| \"ember-100\"\n\t| \"ember-200\"\n\t| \"ember-300\"\n\t| \"ember-400\"\n\t| \"ember-500\"\n\t| \"ember-600\"\n\t| \"ember-700\"\n\t| \"ember-800\"\n\t| \"ember-900\"\n\t| \"ember-v100\"\n\t| \"ember-v200\"\n\t| \"ember-v300\"\n\t| \"ember-v400\"\n\t// Orange\n\t| \"orange-50\"\n\t| \"orange-100\"\n\t| \"orange-200\"\n\t| \"orange-300\"\n\t| \"orange-400\"\n\t| \"orange-500\"\n\t| \"orange-600\"\n\t| \"orange-700\"\n\t| \"orange-800\"\n\t| \"orange-900\"\n\t| \"orange-v100\"\n\t| \"orange-v200\"\n\t| \"orange-v300\"\n\t| \"orange-v400\"\n\t// Amber\n\t| \"amber-50\"\n\t| \"amber-100\"\n\t| \"amber-200\"\n\t| \"amber-300\"\n\t| \"amber-400\"\n\t| \"amber-500\"\n\t| \"amber-600\"\n\t| \"amber-700\"\n\t| \"amber-800\"\n\t| \"amber-900\"\n\t| \"amber-v100\"\n\t| \"amber-v200\"\n\t| \"amber-v300\"\n\t| \"amber-v400\"\n\t// Yellow\n\t| \"yellow-50\"\n\t| \"yellow-100\"\n\t| \"yellow-200\"\n\t| \"yellow-300\"\n\t| \"yellow-400\"\n\t| \"yellow-500\"\n\t| \"yellow-600\"\n\t| \"yellow-700\"\n\t| \"yellow-800\"\n\t| \"yellow-900\"\n\t| \"yellow-v100\"\n\t| \"yellow-v200\"\n\t| \"yellow-v300\"\n\t| \"yellow-v400\"\n\t// Lime\n\t| \"lime-50\"\n\t| \"lime-100\"\n\t| \"lime-200\"\n\t| \"lime-300\"\n\t| \"lime-400\"\n\t| \"lime-500\"\n\t| \"lime-600\"\n\t| \"lime-700\"\n\t| \"lime-800\"\n\t| \"lime-900\"\n\t| \"lime-v100\"\n\t| \"lime-v200\"\n\t| \"lime-v300\"\n\t| \"lime-v400\"\n\t// Green\n\t| \"green-50\"\n\t| \"green-100\"\n\t| \"green-200\"\n\t| \"green-300\"\n\t| \"green-400\"\n\t| \"green-500\"\n\t| \"green-600\"\n\t| \"green-700\"\n\t| \"green-800\"\n\t| \"green-900\"\n\t| \"green-v100\"\n\t| \"green-v200\"\n\t| \"green-v300\"\n\t| \"green-v400\"\n\t// Sage\n\t| \"sage-50\"\n\t| \"sage-100\"\n\t| \"sage-200\"\n\t| \"sage-300\"\n\t| \"sage-400\"\n\t| \"sage-500\"\n\t| \"sage-600\"\n\t| \"sage-700\"\n\t| \"sage-800\"\n\t| \"sage-900\"\n\t| \"sage-v100\"\n\t| \"sage-v200\"\n\t| \"sage-v300\"\n\t| \"sage-v400\"\n\t// Teal\n\t| \"teal-50\"\n\t| \"teal-100\"\n\t| \"teal-200\"\n\t| \"teal-300\"\n\t| \"teal-400\"\n\t| \"teal-500\"\n\t| \"teal-600\"\n\t| \"teal-700\"\n\t| \"teal-800\"\n\t| \"teal-900\"\n\t| \"teal-v100\"\n\t| \"teal-v200\"\n\t| \"teal-v300\"\n\t| \"teal-v400\"\n\t// Cyan\n\t| \"cyan-50\"\n\t| \"cyan-100\"\n\t| \"cyan-200\"\n\t| \"cyan-300\"\n\t| \"cyan-400\"\n\t| \"cyan-500\"\n\t| \"cyan-600\"\n\t| \"cyan-700\"\n\t| \"cyan-800\"\n\t| \"cyan-900\"\n\t| \"cyan-v100\"\n\t| \"cyan-v200\"\n\t| \"cyan-v300\"\n\t| \"cyan-v400\"\n\t// Ice\n\t| \"ice-50\"\n\t| \"ice-100\"\n\t| \"ice-200\"\n\t| \"ice-300\"\n\t| \"ice-400\"\n\t| \"ice-500\"\n\t| \"ice-600\"\n\t| \"ice-700\"\n\t| \"ice-800\"\n\t| \"ice-900\"\n\t| \"ice-v100\"\n\t| \"ice-v200\"\n\t| \"ice-v300\"\n\t| \"ice-v400\"\n\t// Blue\n\t| \"blue-50\"\n\t| \"blue-100\"\n\t| \"blue-200\"\n\t| \"blue-300\"\n\t| \"blue-400\"\n\t| \"blue-500\"\n\t| \"blue-600\"\n\t| \"blue-700\"\n\t| \"blue-800\"\n\t| \"blue-900\"\n\t| \"blue-v100\"\n\t| \"blue-v200\"\n\t| \"blue-v300\"\n\t| \"blue-v400\"\n\t// Indigo\n\t| \"indigo-50\"\n\t| \"indigo-100\"\n\t| \"indigo-200\"\n\t| \"indigo-300\"\n\t| \"indigo-400\"\n\t| \"indigo-500\"\n\t| \"indigo-600\"\n\t| \"indigo-700\"\n\t| \"indigo-800\"\n\t| \"indigo-900\"\n\t| \"indigo-v100\"\n\t| \"indigo-v200\"\n\t| \"indigo-v300\"\n\t| \"indigo-v400\"\n\t// Iris\n\t| \"iris-50\"\n\t| \"iris-100\"\n\t| \"iris-200\"\n\t| \"iris-300\"\n\t| \"iris-400\"\n\t| \"iris-500\"\n\t| \"iris-600\"\n\t| \"iris-700\"\n\t| \"iris-800\"\n\t| \"iris-900\"\n\t| \"iris-v100\"\n\t| \"iris-v200\"\n\t| \"iris-v300\"\n\t| \"iris-v400\"\n\t// Purple\n\t| \"purple-50\"\n\t| \"purple-100\"\n\t| \"purple-200\"\n\t| \"purple-300\"\n\t| \"purple-400\"\n\t| \"purple-500\"\n\t| \"purple-600\"\n\t| \"purple-700\"\n\t| \"purple-800\"\n\t| \"purple-900\"\n\t| \"purple-v100\"\n\t| \"purple-v200\"\n\t| \"purple-v300\"\n\t| \"purple-v400\"\n\t// Pink\n\t| \"pink-50\"\n\t| \"pink-100\"\n\t| \"pink-200\"\n\t| \"pink-300\"\n\t| \"pink-400\"\n\t| \"pink-500\"\n\t| \"pink-600\"\n\t| \"pink-700\"\n\t| \"pink-800\"\n\t| \"pink-900\"\n\t| \"pink-v100\"\n\t| \"pink-v200\"\n\t| \"pink-v300\"\n\t| \"pink-v400\"\n\t// Red\n\t| \"red-50\"\n\t| \"red-100\"\n\t| \"red-200\"\n\t| \"red-300\"\n\t| \"red-400\"\n\t| \"red-500\"\n\t| \"red-600\"\n\t| \"red-700\"\n\t| \"red-800\"\n\t| \"red-900\"\n\t| \"red-v100\"\n\t| \"red-v200\"\n\t| \"red-v300\"\n\t| \"red-v400\"\n\t// Alpha\n\t| \"alpha-black-5\"\n\t| \"alpha-black-10\"\n\t| \"alpha-black-20\"\n\t| \"alpha-black-30\"\n\t| \"alpha-black-40\"\n\t| \"alpha-black-50\"\n\t| \"alpha-black-60\"\n\t| \"alpha-black-70\"\n\t| \"alpha-black-80\"\n\t| \"alpha-black-90\"\n\t| \"alpha-black-95\"\n\t| \"alpha-white-5\"\n\t| \"alpha-white-10\"\n\t| \"alpha-white-20\"\n\t| \"alpha-white-30\"\n\t| \"alpha-white-40\"\n\t| \"alpha-white-50\"\n\t| \"alpha-white-60\"\n\t| \"alpha-white-70\"\n\t| \"alpha-white-80\"\n\t| \"alpha-white-90\"\n\t| \"alpha-white-95\"\n\t// Special\n\t| \"white\"\n\t| \"black\";\n\n/**\n * Spacing token names - maps to `--spacing-{name}` CSS variables\n * @example \"spacing-40\", \"spacing-72\"\n */\nexport type SpacingToken =\n\t| \"spacing-0\"\n\t| \"spacing-2\"\n\t| \"spacing-4\"\n\t| \"spacing-6\"\n\t| \"spacing-8\"\n\t| \"spacing-10\"\n\t| \"spacing-11\"\n\t| \"spacing-12\"\n\t| \"spacing-16\"\n\t| \"spacing-20\"\n\t| \"spacing-24\"\n\t| \"spacing-28\"\n\t| \"spacing-32\"\n\t| \"spacing-36\"\n\t| \"spacing-40\"\n\t| \"spacing-48\"\n\t| \"spacing-56\"\n\t| \"spacing-64\"\n\t| \"spacing-72\"\n\t| \"spacing-80\"\n\t| \"spacing-96\"\n\t| \"spacing-112\"\n\t| \"spacing-128\"\n\t| \"spacing-144\"\n\t| \"spacing-160\"\n\t| \"spacing-176\"\n\t| \"spacing-192\"\n\t| \"spacing-208\"\n\t| \"spacing-224\"\n\t| \"spacing-240\"\n\t| \"spacing-256\"\n\t| \"spacing-288\"\n\t| \"spacing-320\"\n\t| \"spacing-352\"\n\t| \"spacing-384\"\n\t| \"spacing-400\";\n\n/**\n * Radius token names - maps to `--radii-{name}` CSS variables\n * @example \"radii-4\", \"radii-6\"\n */\nexport type RadiusToken =\n\t| \"radii-0\"\n\t| \"radii-2\"\n\t| \"radii-4\"\n\t| \"radii-6\"\n\t| \"radii-8\"\n\t| \"radii-10\"\n\t| \"radii-11\"\n\t| \"radii-12\"\n\t| \"radii-16\"\n\t| \"radii-20\"\n\t| \"radii-24\"\n\t| \"radii-28\"\n\t| \"radii-32\"\n\t| \"radii-36\"\n\t| \"radii-40\"\n\t| \"radii-48\"\n\t| \"radii-56\"\n\t| \"radii-64\"\n\t| \"radii-72\"\n\t| \"radii-80\"\n\t| \"radii-96\"\n\t| \"radii-112\"\n\t| \"radii-128\"\n\t| \"radii-144\"\n\t| \"radii-160\"\n\t| \"radii-176\"\n\t| \"radii-192\"\n\t| \"radii-208\"\n\t| \"radii-224\"\n\t| \"radii-240\"\n\t| \"radii-256\"\n\t| \"radii-288\"\n\t| \"radii-320\"\n\t| \"radii-352\"\n\t| \"radii-384\"\n\t| \"radii-400\";\n\n/**\n * Font size token values - primitive font sizes available in the design system\n * These correspond to Tailwind classes like `text-64`, `text-128`, etc.\n * @example 64, 128, 192\n */\nexport type FontSizeToken =\n\t| 9\n\t| 11\n\t| 12\n\t| 14\n\t| 16\n\t| 18\n\t| 21\n\t| 24\n\t| 28\n\t| 32\n\t| 36\n\t| 42\n\t| 48\n\t| 56\n\t| 64\n\t| 72\n\t| 84\n\t| 88\n\t| 96\n\t| 112\n\t| 128\n\t| 148\n\t| 168\n\t| 192\n\t| 224\n\t| 256\n\t| 280;\n\n/**\n * Array of all available font sizes for use in UI selectors/dropdowns\n */\nexport const FONT_SIZES: FontSizeToken[] = [\n\t9, 11, 12, 14, 16, 18, 21, 24, 28, 32, 36, 42, 48, 56, 64, 72, 84, 88, 96,\n\t112, 128, 148, 168, 192, 224, 256, 280,\n];\n\n/**\n * Helper to generate typography class string from font size\n * @example fontSizeToClass(128) => \"text-128 leading-128 tracking-128\"\n */\nexport function fontSizeToClass(size: FontSizeToken): string {\n\treturn `text-${size} leading-${size} tracking-${size}`;\n}\n\n/**\n * Helper to generate responsive typography class string\n * @example responsiveTypographyClass(64, 128, 192) => \"text-64 leading-64 tracking-64 md:text-128 md:leading-128 md:tracking-128 xl:text-192 xl:leading-192 xl:tracking-192\"\n */\nexport function responsiveTypographyClass(\n\tmobile: FontSizeToken,\n\ttablet: FontSizeToken,\n\tdesktop: FontSizeToken,\n): string {\n\treturn [\n\t\tfontSizeToClass(mobile),\n\t\t`md:${fontSizeToClass(tablet).split(\" \").join(\" md:\")}`,\n\t\t`xl:${fontSizeToClass(desktop).split(\" \").join(\" xl:\")}`,\n\t].join(\" \");\n}\n\n// =============================================================================\n// Color Tokens\n// =============================================================================\n\nexport interface ComponentThemeColors {\n\t/**\n\t * Background color for sections\n\t * @example \"gray-100\"\n\t */\n\tbgSection?: ColorToken;\n\n\t/**\n\t * Background color for cards\n\t * @example \"white\"\n\t */\n\tcardBackground?: ColorToken;\n\n\t/**\n\t * Muted background color\n\t * @example \"gray-50\"\n\t */\n\tbgMuted?: ColorToken;\n\n\t/**\n\t * Primary text color\n\t * @example \"gray-1100\"\n\t */\n\ttextPrimary?: ColorToken;\n\n\t/**\n\t * Secondary text color\n\t * @example \"gray-800\"\n\t */\n\ttextSecondary?: ColorToken;\n\n\t/**\n\t * Muted text color\n\t * @example \"gray-600\"\n\t */\n\ttextMuted?: ColorToken;\n\n\t/**\n\t * Inverted text color (for dark backgrounds)\n\t * @example \"gray-100\"\n\t */\n\ttextInverted?: ColorToken;\n\n\t/**\n\t * Link text color\n\t * @example \"gray-1100\"\n\t */\n\ttextLink?: ColorToken;\n\n\t/**\n\t * Link hover text color\n\t * @example \"gray-700\"\n\t */\n\ttextLinkHover?: ColorToken;\n\n\t/**\n\t * Brand accent color\n\t * @example \"ember-v300\"\n\t */\n\taccentBrand?: ColorToken;\n\n\t/**\n\t * Soft brand accent color\n\t * @example \"ember-100\"\n\t */\n\taccentBrandSoft?: ColorToken;\n\n\t/**\n\t * Subtle border color\n\t * @example \"alpha-black-10\"\n\t */\n\tborderSubtle?: ColorToken;\n\n\t/**\n\t * Strong border color\n\t * @example \"alpha-black-20\"\n\t */\n\tborderStrong?: ColorToken;\n\n\t/**\n\t * Focus border color (uses accentBrand by default)\n\t * @example \"ember-v300\"\n\t */\n\tborderFocus?: ColorToken;\n\n\t/**\n\t * Divider border color\n\t * @example \"alpha-black-10\"\n\t */\n\tborderDivider?: ColorToken;\n\n\t/**\n\t * Primary button background color\n\t * @example \"gray-1100\"\n\t */\n\tbuttonPrimaryBg?: ColorToken;\n\n\t/**\n\t * Primary button hover background color\n\t * @example \"gray-600\"\n\t */\n\tbuttonPrimaryBgHover?: ColorToken;\n\n\t/**\n\t * Secondary button background color\n\t * @example \"white\"\n\t */\n\tbuttonSecondaryBg?: ColorToken;\n\n\t/**\n\t * Secondary button hover background color\n\t * @example \"gray-100\"\n\t */\n\tbuttonSecondaryBgHover?: ColorToken;\n}\n\n// =============================================================================\n// Spatial Tokens\n// =============================================================================\n\nexport interface ComponentThemeSpatial {\n\t/**\n\t * Grid margin for large breakpoint\n\t * @example \"spacing-72\"\n\t */\n\tgridLargeMargin?: SpacingToken;\n\n\t/**\n\t * Grid gutter for large breakpoint\n\t * @example \"spacing-24\"\n\t */\n\tgridLargeGutter?: SpacingToken;\n\n\t/**\n\t * Number of grid columns for large breakpoint\n\t */\n\tgridLargeColumns?: number;\n\n\t/**\n\t * Grid margin for medium breakpoint\n\t * @example \"spacing-56\"\n\t */\n\tgridMediumMargin?: SpacingToken;\n\n\t/**\n\t * Grid gutter for medium breakpoint\n\t * @example \"spacing-20\"\n\t */\n\tgridMediumGutter?: SpacingToken;\n\n\t/**\n\t * Number of grid columns for medium breakpoint\n\t */\n\tgridMediumColumns?: number;\n\n\t/**\n\t * Grid margin for small breakpoint\n\t * @example \"spacing-24\"\n\t */\n\tgridSmallMargin?: SpacingToken;\n\n\t/**\n\t * Grid gutter for small breakpoint\n\t * @example \"spacing-12\"\n\t */\n\tgridSmallGutter?: SpacingToken;\n\n\t/**\n\t * Number of grid columns for small breakpoint\n\t */\n\tgridSmallColumns?: number;\n\n\t/**\n\t * Section gap for large breakpoint\n\t * @example \"spacing-64\"\n\t */\n\tsectionLargeGap?: SpacingToken;\n\n\t/**\n\t * Section padding for large breakpoint\n\t * @example \"spacing-128\"\n\t */\n\tsectionLargePadding?: SpacingToken;\n\n\t/**\n\t * Section gap for medium breakpoint\n\t * @example \"spacing-56\"\n\t */\n\tsectionMediumGap?: SpacingToken;\n\n\t/**\n\t * Section padding for medium breakpoint\n\t * @example \"spacing-96\"\n\t */\n\tsectionMediumPadding?: SpacingToken;\n\n\t/**\n\t * Section gap for small breakpoint\n\t * @example \"spacing-32\"\n\t */\n\tsectionSmallGap?: SpacingToken;\n\n\t/**\n\t * Section padding for small breakpoint\n\t * @example \"spacing-64\"\n\t */\n\tsectionSmallPadding?: SpacingToken;\n\n\t/**\n\t * Card gap for large size\n\t * @example \"spacing-10\"\n\t */\n\tcardLargeGap?: SpacingToken;\n\n\t/**\n\t * Card padding for large size\n\t * @example \"spacing-24\"\n\t */\n\tcardLargePadding?: SpacingToken;\n\n\t/**\n\t * Card gap for small size\n\t * @example \"spacing-12\"\n\t */\n\tcardSmallGap?: SpacingToken;\n\n\t/**\n\t * Card padding for small size\n\t * @example \"spacing-16\"\n\t */\n\tcardSmallPadding?: SpacingToken;\n}\n\n// =============================================================================\n// Surface Tokens\n// =============================================================================\n\nexport interface ComponentThemeSurface {\n\t/**\n\t * Card border radius\n\t * @example \"radii-4\"\n\t */\n\tcardRadius?: RadiusToken;\n\n\t/**\n\t * Card stroke/border width in pixels\n\t * @example 1\n\t */\n\tcardStroke?: number;\n\n\t/**\n\t * Button border radius\n\t * @example \"radii-6\"\n\t */\n\tbuttonRadius?: RadiusToken;\n\n\t/**\n\t * Button stroke/border weight in pixels\n\t * @example 1\n\t */\n\tbuttonStrokeWeight?: number;\n}\n\n// =============================================================================\n// Combined Theme Interface\n// =============================================================================\n\n/**\n * Complete component theme interface combining colors, spatial, and surface tokens.\n *\n * @example\n * ```tsx\n * const customTheme: ComponentTheme = {\n * colors: {\n * textPrimary: \"gray-100\",\n * accentBrand: \"ember-500\",\n * },\n * spatial: {\n * sectionLargePadding: \"spacing-96\",\n * },\n * surface: {\n * cardRadius: \"radii-8\",\n * },\n * };\n *\n * <Tout theme={customTheme} ... />\n * ```\n */\nexport interface ComponentTheme {\n\tcolors?: ComponentThemeColors;\n\tspatial?: ComponentThemeSpatial;\n\tsurface?: ComponentThemeSurface;\n}\n\n// =============================================================================\n// Button Theme Interface\n// =============================================================================\n\n/**\n * Button-specific theme interface for customizing individual button appearance.\n *\n * @example\n * ```tsx\n * const buttonTheme: ButtonTheme = {\n * bg: \"ember-500\",\n * bgHover: \"ember-600\",\n * text: \"white\",\n * radius: \"radii-8\",\n * };\n *\n * <Button theme={buttonTheme}>Themed Button</Button>\n * ```\n */\nexport interface ButtonTheme {\n\t/** Background color */\n\tbg?: ColorToken;\n\t/** Background color on hover */\n\tbgHover?: ColorToken;\n\t/** Background color on active/press */\n\tbgActive?: ColorToken;\n\t/** Text color */\n\ttext?: ColorToken;\n\t/** Border color (if using border) */\n\tborderColor?: ColorToken;\n\t/** Border width in pixels (0 for no border) */\n\tborderWidth?: number;\n\t/** Border radius */\n\tradius?: RadiusToken;\n}\n\n// =============================================================================\n// Utility Functions\n// =============================================================================\n\n/**\n * Converts a color token name to a CSS variable reference\n */\nfunction colorToVar(token: ColorToken): string {\n\treturn `var(--color-${token})`;\n}\n\n/**\n * Converts a spacing token name to a CSS variable reference\n */\nfunction spacingToVar(token: SpacingToken): string {\n\treturn `var(--${token})`;\n}\n\n/**\n * Converts a radius token name to a CSS variable reference\n */\nfunction radiusToVar(token: RadiusToken): string {\n\treturn `var(--${token})`;\n}\n\n/**\n * Converts a ComponentTheme object to CSS custom properties (inline style object)\n *\n * @param theme - The theme object to convert\n * @returns An object suitable for use as React inline styles\n */\nexport function themeToStyleVars(\n\ttheme: ComponentTheme | undefined,\n): React.CSSProperties {\n\tif (!theme) return {};\n\n\tconst vars: Record<string, string> = {};\n\n\t// Colors\n\tif (theme.colors) {\n\t\tconst { colors } = theme;\n\t\tif (colors.bgSection)\n\t\t\tvars[\"--theme-bg-section\"] = colorToVar(colors.bgSection);\n\t\tif (colors.cardBackground)\n\t\t\tvars[\"--theme-card-background\"] = colorToVar(colors.cardBackground);\n\t\tif (colors.bgMuted) vars[\"--theme-bg-muted\"] = colorToVar(colors.bgMuted);\n\t\tif (colors.textPrimary)\n\t\t\tvars[\"--theme-text-primary\"] = colorToVar(colors.textPrimary);\n\t\tif (colors.textSecondary)\n\t\t\tvars[\"--theme-text-secondary\"] = colorToVar(colors.textSecondary);\n\t\tif (colors.textMuted)\n\t\t\tvars[\"--theme-text-muted\"] = colorToVar(colors.textMuted);\n\t\tif (colors.textInverted)\n\t\t\tvars[\"--theme-text-inverted\"] = colorToVar(colors.textInverted);\n\t\tif (colors.textLink)\n\t\t\tvars[\"--theme-text-link\"] = colorToVar(colors.textLink);\n\t\tif (colors.textLinkHover)\n\t\t\tvars[\"--theme-text-link-hover\"] = colorToVar(colors.textLinkHover);\n\t\tif (colors.accentBrand)\n\t\t\tvars[\"--theme-accent-brand\"] = colorToVar(colors.accentBrand);\n\t\tif (colors.accentBrandSoft)\n\t\t\tvars[\"--theme-accent-brand-soft\"] = colorToVar(colors.accentBrandSoft);\n\t\tif (colors.borderSubtle)\n\t\t\tvars[\"--theme-border-subtle\"] = colorToVar(colors.borderSubtle);\n\t\tif (colors.borderStrong)\n\t\t\tvars[\"--theme-border-strong\"] = colorToVar(colors.borderStrong);\n\t\tif (colors.borderFocus)\n\t\t\tvars[\"--theme-border-focus\"] = colorToVar(colors.borderFocus);\n\t\tif (colors.borderDivider)\n\t\t\tvars[\"--theme-border-divider\"] = colorToVar(colors.borderDivider);\n\t\tif (colors.buttonPrimaryBg)\n\t\t\tvars[\"--theme-button-primary-bg\"] = colorToVar(colors.buttonPrimaryBg);\n\t\tif (colors.buttonPrimaryBgHover)\n\t\t\tvars[\"--theme-button-primary-bg-hover\"] = colorToVar(\n\t\t\t\tcolors.buttonPrimaryBgHover,\n\t\t\t);\n\t\tif (colors.buttonSecondaryBg)\n\t\t\tvars[\"--theme-button-secondary-bg\"] = colorToVar(\n\t\t\t\tcolors.buttonSecondaryBg,\n\t\t\t);\n\t\tif (colors.buttonSecondaryBgHover)\n\t\t\tvars[\"--theme-button-secondary-bg-hover\"] = colorToVar(\n\t\t\t\tcolors.buttonSecondaryBgHover,\n\t\t\t);\n\t}\n\n\t// Spatial\n\tif (theme.spatial) {\n\t\tconst { spatial } = theme;\n\t\tif (spatial.gridLargeMargin)\n\t\t\tvars[\"--theme-grid-large-margin\"] = spacingToVar(spatial.gridLargeMargin);\n\t\tif (spatial.gridLargeGutter)\n\t\t\tvars[\"--theme-grid-large-gutter\"] = spacingToVar(spatial.gridLargeGutter);\n\t\tif (spatial.gridLargeColumns)\n\t\t\tvars[\"--theme-grid-large-columns\"] = String(spatial.gridLargeColumns);\n\t\tif (spatial.gridMediumMargin)\n\t\t\tvars[\"--theme-grid-medium-margin\"] = spacingToVar(\n\t\t\t\tspatial.gridMediumMargin,\n\t\t\t);\n\t\tif (spatial.gridMediumGutter)\n\t\t\tvars[\"--theme-grid-medium-gutter\"] = spacingToVar(\n\t\t\t\tspatial.gridMediumGutter,\n\t\t\t);\n\t\tif (spatial.gridMediumColumns)\n\t\t\tvars[\"--theme-grid-medium-columns\"] = String(spatial.gridMediumColumns);\n\t\tif (spatial.gridSmallMargin)\n\t\t\tvars[\"--theme-grid-small-margin\"] = spacingToVar(spatial.gridSmallMargin);\n\t\tif (spatial.gridSmallGutter)\n\t\t\tvars[\"--theme-grid-small-gutter\"] = spacingToVar(spatial.gridSmallGutter);\n\t\tif (spatial.gridSmallColumns)\n\t\t\tvars[\"--theme-grid-small-columns\"] = String(spatial.gridSmallColumns);\n\t\tif (spatial.sectionLargeGap)\n\t\t\tvars[\"--theme-section-large-gap\"] = spacingToVar(spatial.sectionLargeGap);\n\t\tif (spatial.sectionLargePadding)\n\t\t\tvars[\"--theme-section-large-padding\"] = spacingToVar(\n\t\t\t\tspatial.sectionLargePadding,\n\t\t\t);\n\t\tif (spatial.sectionMediumGap)\n\t\t\tvars[\"--theme-section-medium-gap\"] = spacingToVar(\n\t\t\t\tspatial.sectionMediumGap,\n\t\t\t);\n\t\tif (spatial.sectionMediumPadding)\n\t\t\tvars[\"--theme-section-medium-padding\"] = spacingToVar(\n\t\t\t\tspatial.sectionMediumPadding,\n\t\t\t);\n\t\tif (spatial.sectionSmallGap)\n\t\t\tvars[\"--theme-section-small-gap\"] = spacingToVar(spatial.sectionSmallGap);\n\t\tif (spatial.sectionSmallPadding)\n\t\t\tvars[\"--theme-section-small-padding\"] = spacingToVar(\n\t\t\t\tspatial.sectionSmallPadding,\n\t\t\t);\n\t\tif (spatial.cardLargeGap)\n\t\t\tvars[\"--theme-card-large-gap\"] = spacingToVar(spatial.cardLargeGap);\n\t\tif (spatial.cardLargePadding)\n\t\t\tvars[\"--theme-card-large-padding\"] = spacingToVar(\n\t\t\t\tspatial.cardLargePadding,\n\t\t\t);\n\t\tif (spatial.cardSmallGap)\n\t\t\tvars[\"--theme-card-small-gap\"] = spacingToVar(spatial.cardSmallGap);\n\t\tif (spatial.cardSmallPadding)\n\t\t\tvars[\"--theme-card-small-padding\"] = spacingToVar(\n\t\t\t\tspatial.cardSmallPadding,\n\t\t\t);\n\t}\n\n\t// Surface\n\tif (theme.surface) {\n\t\tconst { surface } = theme;\n\t\tif (surface.cardRadius)\n\t\t\tvars[\"--theme-card-radius\"] = radiusToVar(surface.cardRadius);\n\t\tif (surface.cardStroke)\n\t\t\tvars[\"--theme-card-stroke\"] = `${surface.cardStroke}px`;\n\t\tif (surface.buttonRadius)\n\t\t\tvars[\"--theme-button-radius\"] = radiusToVar(surface.buttonRadius);\n\t\tif (surface.buttonStrokeWeight)\n\t\t\tvars[\"--theme-button-stroke-weight\"] = `${surface.buttonStrokeWeight}px`;\n\t}\n\n\treturn vars as React.CSSProperties;\n}\n\n/**\n * Converts a ButtonTheme object to CSS custom properties (inline style object)\n *\n * @param theme - The button theme object to convert\n * @returns An object suitable for use as React inline styles\n */\nexport function buttonThemeToStyleVars(\n\ttheme: ButtonTheme | undefined,\n): React.CSSProperties {\n\tif (!theme) return {};\n\n\tconst vars: Record<string, string> = {};\n\n\tif (theme.bg) vars[\"--btn-bg\"] = colorToVar(theme.bg);\n\tif (theme.bgHover) vars[\"--btn-bg-hover\"] = colorToVar(theme.bgHover);\n\tif (theme.bgActive) vars[\"--btn-bg-active\"] = colorToVar(theme.bgActive);\n\tif (theme.text) vars[\"--btn-text\"] = colorToVar(theme.text);\n\tif (theme.borderColor)\n\t\tvars[\"--btn-border-color\"] = colorToVar(theme.borderColor);\n\tif (theme.borderWidth !== undefined)\n\t\tvars[\"--btn-border-width\"] = `${theme.borderWidth}px`;\n\tif (theme.radius) vars[\"--btn-radius\"] = radiusToVar(theme.radius);\n\n\treturn vars as React.CSSProperties;\n}\n","\"use client\";\n\nimport {\n\tButton as BaseButton,\n\ttype ButtonProps as BaseButtonProps,\n} from \"@base-ui-components/react/button\";\nimport * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\nimport { type ButtonTheme, buttonThemeToStyleVars } from \"../../../lib/theme\";\n\n/**\n * Button component based on Figma Button component\n *\n * Variants (matches Figma):\n * - primary: Blue filled button for primary actions\n * - default: Dark filled button for secondary prominence\n * - secondary: Light gray filled button with subtle border\n * - destructive: Red filled button for destructive actions\n * - outline: Bordered button with transparent background\n * - ghost: Transparent button with subtle hover\n * - link: Text-only button with underline on hover\n *\n * Sizes (matches Figma):\n * - sm: Small buttons (32px height)\n * - default: Default buttons (36px height)\n * - lg: Large buttons (40px height)\n *\n * For icon-only buttons, use the IconButton component instead.\n *\n * Theme Support:\n * Pass a `theme` prop to override default colors via CSS custom properties.\n */\nconst buttonVariants = tv({\n\tbase: \"inline-flex items-center justify-center gap-spatial-ui-button-gap-icon-text whitespace-nowrap transition-colors duration-150 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-1 disabled:pointer-events-none disabled:opacity-50\",\n\tvariants: {\n\t\tvariant: {\n\t\t\t// Primary - blue filled button\n\t\t\tprimary:\n\t\t\t\t\"bg-button-primary-bg text-button-primary-text hover:bg-button-primary-bg-hover active:bg-button-primary-bg-active border-transparent\",\n\t\t\t// Default - dark filled button\n\t\t\tdefault:\n\t\t\t\t\"bg-button-default-bg text-button-default-text hover:bg-button-default-bg-hover active:bg-button-default-bg-active border-transparent\",\n\t\t\t// Secondary - light gray filled with subtle border\n\t\t\tsecondary:\n\t\t\t\t\"bg-button-secondary-bg text-button-secondary-text hover:bg-button-secondary-bg-hover active:bg-button-secondary-bg-active border border-button-secondary-border\",\n\t\t\t// Destructive - red filled button\n\t\t\tdestructive:\n\t\t\t\t\"bg-button-destructive-bg text-button-destructive-text hover:bg-button-destructive-bg-hover active:bg-button-destructive-bg-active border-transparent\",\n\t\t\t// Outline - bordered with transparent background\n\t\t\toutline:\n\t\t\t\t\"bg-button-outline-bg text-button-outline-text hover:bg-button-outline-bg-hover active:bg-button-outline-bg-active border border-button-outline-border hover:border-button-outline-border-hover\",\n\t\t\t// Ghost - transparent with subtle hover\n\t\t\tghost:\n\t\t\t\t\"bg-button-ghost-bg text-button-ghost-text hover:bg-button-ghost-bg-hover active:bg-button-ghost-bg-active border-transparent\",\n\t\t\t// Link - text only with underline on hover\n\t\t\tlink: \"bg-transparent text-button-link-text hover:text-button-link-text-hover hover:underline active:text-button-link-text-hover border-transparent underline-offset-4\",\n\t\t\t// Themed - uses CSS custom properties for styling\n\t\t\tthemed:\n\t\t\t\t\"[background:var(--btn-bg)] [color:var(--btn-text)] [border-color:var(--btn-border-color,transparent)] hover:[background:var(--btn-bg-hover,var(--btn-bg))] active:[background:var(--btn-bg-active,var(--btn-bg-hover,var(--btn-bg)))]\",\n\t\t},\n\t\tsize: {\n\t\t\tsm: \"typography-ui-text-xs h-spatial-ui-button-height-small px-spatial-ui-button-padding-x-small py-spatial-ui-button-padding-y-small rounded-surface-button-small\",\n\t\t\tdefault:\n\t\t\t\t\"typography-ui-text-sm h-spatial-ui-button-height-medium px-spatial-ui-button-padding-x-medium py-spatial-ui-button-padding-y-medium rounded-surface-button-medium\",\n\t\t\tlg: \"typography-ui-text-md h-spatial-ui-button-height-large px-spatial-ui-button-padding-x-large py-spatial-ui-button-padding-y-large rounded-surface-button-large\",\n\t\t},\n\t},\n\tdefaultVariants: {\n\t\tvariant: \"default\",\n\t\tsize: \"default\",\n\t},\n});\n\nexport type HTMLButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement>;\nexport type ButtonProps = BaseButtonProps &\n\tVariantProps<typeof buttonVariants> &\n\tHTMLButtonProps & {\n\t\t/**\n\t\t * Theme overrides for button styling via CSS custom properties\n\t\t */\n\t\ttheme?: ButtonTheme;\n\t};\n\n/**\n * Check if a ButtonTheme has any actual values set\n */\nfunction hasThemeValues(theme: ButtonTheme | undefined): boolean {\n\tif (!theme) return false;\n\treturn Object.values(theme).some((v) => v !== undefined && v !== null);\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n\t(\n\t\t{ className, variant, size, render, nativeButton, theme, style, ...props },\n\t\tref,\n\t) => {\n\t\t// When render prop is provided, default nativeButton to false to suppress warnings\n\t\tconst isNativeButton = nativeButton ?? render === undefined;\n\n\t\t// If theme has actual values, use \"themed\" variant to enable CSS custom property styling\n\t\tconst hasTheme = hasThemeValues(theme);\n\t\tconst effectiveVariant = hasTheme ? \"themed\" : variant;\n\t\tconst themeStyles = buttonThemeToStyleVars(theme);\n\t\tconst combinedStyles = hasTheme ? { ...themeStyles, ...style } : style;\n\n\t\t// Resolve actual values for data attributes\n\t\tconst resolvedVariant = effectiveVariant ?? \"default\";\n\t\tconst resolvedSize = size ?? \"default\";\n\n\t\treturn (\n\t\t\t<BaseButton\n\t\t\t\tclassName={buttonVariants({\n\t\t\t\t\tvariant: effectiveVariant,\n\t\t\t\t\tsize,\n\t\t\t\t\tclass: className,\n\t\t\t\t})}\n\t\t\t\tref={ref}\n\t\t\t\trender={render}\n\t\t\t\tnativeButton={isNativeButton}\n\t\t\t\tstyle={combinedStyles}\n\t\t\t\tdata-variant={resolvedVariant}\n\t\t\t\tdata-size={resolvedSize}\n\t\t\t\t{...props}\n\t\t\t/>\n\t\t);\n\t},\n);\nButton.displayName = \"Button\";\n\nexport { Button, buttonVariants };\n","\"use client\";\n\nimport { useRender } from \"@base-ui-components/react/use-render\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\n/**\n * IconButton component based on Figma Button component (icon-only variant)\n *\n * **IMPORTANT: Accessibility Requirement**\n * Icon-only buttons MUST have an accessible label. Provide one of:\n * - `aria-label`: A text description of the button's action (recommended)\n * - `aria-labelledby`: Reference to an element containing the label\n * - `title`: Tooltip text (less preferred, but provides a label)\n *\n * @example\n * ```tsx\n * // Correct usage with aria-label\n * <IconButton aria-label=\"Close menu\">\n * <CloseIcon />\n * </IconButton>\n * ```\n *\n * Variants (matches Figma):\n * - primary: Filled brand button (indigo background)\n * - primary-outline: Outlined brand button (indigo border/text)\n * - secondary: Filled neutral button (white background, for dark backgrounds)\n * - secondary-outline: Outlined neutral button (white border/text, for dark backgrounds)\n * - ghost: Transparent button with subtle hover (for light backgrounds)\n * - ghost-inverse: Transparent button with subtle hover (for dark backgrounds)\n *\n * Sizes:\n * - sm: Small (28x28)\n * - md: Medium (40x40) - default\n * - lg: Large (56x56)\n *\n * Rounded:\n * - default: Standard border radius (matches size)\n * - full: Fully circular\n */\nconst iconButtonVariants = tv({\n\tbase: \"inline-flex items-center justify-center whitespace-nowrap transition-colors duration-150 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&>svg]:shrink-0\",\n\tvariants: {\n\t\tvariant: {\n\t\t\t// Primary - filled brand button\n\t\t\tprimary:\n\t\t\t\t\"bg-button-primary-bg text-button-primary-text hover:bg-button-primary-bg-hover hover:text-button-primary-text-hover border-transparent focus-visible:ring-button-primary-bg\",\n\t\t\t// Primary Outline - outlined brand button\n\t\t\t\"primary-outline\":\n\t\t\t\t\"bg-button-primary-outline-bg text-button-primary-outline-text border border-button-primary-outline-border hover:bg-button-primary-outline-bg-hover hover:text-button-primary-outline-text-hover hover:border-button-primary-outline-border-hover focus-visible:ring-button-primary-outline-border\",\n\t\t\t// Secondary - filled neutral button (for dark backgrounds)\n\t\t\tsecondary:\n\t\t\t\t\"bg-button-secondary-bg text-button-secondary-text hover:bg-button-secondary-bg-hover hover:text-button-secondary-text-hover border-transparent focus-visible:ring-button-secondary-bg focus-visible:ring-offset-gray-1000\",\n\t\t\t// Secondary Outline - outlined neutral button (for dark backgrounds)\n\t\t\t\"secondary-outline\":\n\t\t\t\t\"bg-button-secondary-outline-bg text-button-secondary-outline-text border border-button-secondary-outline-border hover:bg-button-secondary-outline-bg-hover hover:text-button-secondary-outline-text-hover hover:border-button-secondary-outline-border-hover focus-visible:ring-button-secondary-outline-border focus-visible:ring-offset-gray-1000\",\n\t\t\t// Ghost - transparent button (for light backgrounds)\n\t\t\tghost:\n\t\t\t\t\"bg-button-ghost-bg text-button-ghost-text hover:bg-button-ghost-bg-hover hover:text-button-ghost-text-hover border-transparent focus-visible:ring-gray-1000\",\n\t\t\t// Ghost Inverse - transparent button (for dark backgrounds)\n\t\t\t\"ghost-inverse\":\n\t\t\t\t\"bg-button-ghost-inverse-bg text-button-ghost-inverse-text hover:bg-button-ghost-inverse-bg-hover hover:text-button-ghost-inverse-text-hover border-transparent focus-visible:ring-gray-50 focus-visible:ring-offset-gray-1000\",\n\t\t},\n\t\tsize: {\n\t\t\tsm: \"size-28\",\n\t\t\tmd: \"size-40\",\n\t\t\tlg: \"size-56\",\n\t\t},\n\t\trounded: {\n\t\t\tdefault: \"\",\n\t\t\tfull: \"rounded-full\",\n\t\t},\n\t},\n\tcompoundVariants: [\n\t\t// Apply size-specific rounded only when rounded is \"default\"\n\t\t{ size: \"sm\", rounded: \"default\", class: \"rounded-surface-button-small\" },\n\t\t{ size: \"md\", rounded: \"default\", class: \"rounded-surface-button-medium\" },\n\t\t{ size: \"lg\", rounded: \"default\", class: \"rounded-surface-button-large\" },\n\t],\n\tdefaultVariants: {\n\t\tvariant: \"primary\",\n\t\tsize: \"md\",\n\t\trounded: \"default\",\n\t},\n});\n\ninterface IconButtonState extends Record<string, unknown> {\n\tvariant:\n\t\t| \"primary\"\n\t\t| \"primary-outline\"\n\t\t| \"secondary\"\n\t\t| \"secondary-outline\"\n\t\t| \"ghost\"\n\t\t| \"ghost-inverse\";\n\tsize: \"sm\" | \"md\" | \"lg\";\n\trounded: \"default\" | \"full\";\n}\n\nexport interface IconButtonProps\n\textends useRender.ComponentProps<\"button\", IconButtonState>,\n\t\tVariantProps<typeof iconButtonVariants> {}\n\nfunction IconButton(props: IconButtonProps) {\n\tconst {\n\t\tclassName,\n\t\tvariant = \"primary\",\n\t\tsize = \"md\",\n\t\trounded = \"default\",\n\t\trender,\n\t\t...otherProps\n\t} = props;\n\n\tconst state: IconButtonState = {\n\t\tvariant,\n\t\tsize,\n\t\trounded,\n\t};\n\n\tconst buttonClassName = iconButtonVariants({\n\t\tvariant,\n\t\tsize,\n\t\trounded,\n\t\tclass: className,\n\t});\n\n\treturn useRender<IconButtonState, HTMLButtonElement>({\n\t\trender,\n\t\tstate,\n\t\tprops: {\n\t\t\ttype: \"button\",\n\t\t\tclassName: buttonClassName,\n\t\t\t...otherProps,\n\t\t},\n\t\tdefaultTagName: \"button\",\n\t});\n}\n\nexport { IconButton, iconButtonVariants };\n"]}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import * as tailwind_variants from 'tailwind-variants';
|
|
2
|
+
import { VariantProps } from 'tailwind-variants';
|
|
3
|
+
import * as tailwind_variants_dist_config_js from 'tailwind-variants/dist/config.js';
|
|
4
|
+
import { ButtonProps as ButtonProps$1 } from '@base-ui-components/react/button';
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
import { B as ButtonTheme } from './theme-CzBPUlh_.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Button component based on Figma Button component
|
|
10
|
+
*
|
|
11
|
+
* Variants (matches Figma):
|
|
12
|
+
* - primary: Blue filled button for primary actions
|
|
13
|
+
* - default: Dark filled button for secondary prominence
|
|
14
|
+
* - secondary: Light gray filled button with subtle border
|
|
15
|
+
* - destructive: Red filled button for destructive actions
|
|
16
|
+
* - outline: Bordered button with transparent background
|
|
17
|
+
* - ghost: Transparent button with subtle hover
|
|
18
|
+
* - link: Text-only button with underline on hover
|
|
19
|
+
*
|
|
20
|
+
* Sizes (matches Figma):
|
|
21
|
+
* - sm: Small buttons (32px height)
|
|
22
|
+
* - default: Default buttons (36px height)
|
|
23
|
+
* - lg: Large buttons (40px height)
|
|
24
|
+
*
|
|
25
|
+
* For icon-only buttons, use the IconButton component instead.
|
|
26
|
+
*
|
|
27
|
+
* Theme Support:
|
|
28
|
+
* Pass a `theme` prop to override default colors via CSS custom properties.
|
|
29
|
+
*/
|
|
30
|
+
declare const buttonVariants: tailwind_variants.TVReturnType<{
|
|
31
|
+
variant: {
|
|
32
|
+
primary: string;
|
|
33
|
+
default: string;
|
|
34
|
+
secondary: string;
|
|
35
|
+
destructive: string;
|
|
36
|
+
outline: string;
|
|
37
|
+
ghost: string;
|
|
38
|
+
link: string;
|
|
39
|
+
themed: string;
|
|
40
|
+
};
|
|
41
|
+
size: {
|
|
42
|
+
sm: string;
|
|
43
|
+
default: string;
|
|
44
|
+
lg: string;
|
|
45
|
+
};
|
|
46
|
+
}, undefined, "inline-flex items-center justify-center gap-spatial-ui-button-gap-icon-text whitespace-nowrap transition-colors duration-150 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-1 disabled:pointer-events-none disabled:opacity-50", tailwind_variants_dist_config_js.TVConfig<{
|
|
47
|
+
variant: {
|
|
48
|
+
primary: string;
|
|
49
|
+
default: string;
|
|
50
|
+
secondary: string;
|
|
51
|
+
destructive: string;
|
|
52
|
+
outline: string;
|
|
53
|
+
ghost: string;
|
|
54
|
+
link: string;
|
|
55
|
+
themed: string;
|
|
56
|
+
};
|
|
57
|
+
size: {
|
|
58
|
+
sm: string;
|
|
59
|
+
default: string;
|
|
60
|
+
lg: string;
|
|
61
|
+
};
|
|
62
|
+
}, {
|
|
63
|
+
variant: {
|
|
64
|
+
primary: string;
|
|
65
|
+
default: string;
|
|
66
|
+
secondary: string;
|
|
67
|
+
destructive: string;
|
|
68
|
+
outline: string;
|
|
69
|
+
ghost: string;
|
|
70
|
+
link: string;
|
|
71
|
+
themed: string;
|
|
72
|
+
};
|
|
73
|
+
size: {
|
|
74
|
+
sm: string;
|
|
75
|
+
default: string;
|
|
76
|
+
lg: string;
|
|
77
|
+
};
|
|
78
|
+
}>, {
|
|
79
|
+
variant: {
|
|
80
|
+
primary: string;
|
|
81
|
+
default: string;
|
|
82
|
+
secondary: string;
|
|
83
|
+
destructive: string;
|
|
84
|
+
outline: string;
|
|
85
|
+
ghost: string;
|
|
86
|
+
link: string;
|
|
87
|
+
themed: string;
|
|
88
|
+
};
|
|
89
|
+
size: {
|
|
90
|
+
sm: string;
|
|
91
|
+
default: string;
|
|
92
|
+
lg: string;
|
|
93
|
+
};
|
|
94
|
+
}, undefined, tailwind_variants.TVReturnType<{
|
|
95
|
+
variant: {
|
|
96
|
+
primary: string;
|
|
97
|
+
default: string;
|
|
98
|
+
secondary: string;
|
|
99
|
+
destructive: string;
|
|
100
|
+
outline: string;
|
|
101
|
+
ghost: string;
|
|
102
|
+
link: string;
|
|
103
|
+
themed: string;
|
|
104
|
+
};
|
|
105
|
+
size: {
|
|
106
|
+
sm: string;
|
|
107
|
+
default: string;
|
|
108
|
+
lg: string;
|
|
109
|
+
};
|
|
110
|
+
}, undefined, "inline-flex items-center justify-center gap-spatial-ui-button-gap-icon-text whitespace-nowrap transition-colors duration-150 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-1 disabled:pointer-events-none disabled:opacity-50", tailwind_variants_dist_config_js.TVConfig<{
|
|
111
|
+
variant: {
|
|
112
|
+
primary: string;
|
|
113
|
+
default: string;
|
|
114
|
+
secondary: string;
|
|
115
|
+
destructive: string;
|
|
116
|
+
outline: string;
|
|
117
|
+
ghost: string;
|
|
118
|
+
link: string;
|
|
119
|
+
themed: string;
|
|
120
|
+
};
|
|
121
|
+
size: {
|
|
122
|
+
sm: string;
|
|
123
|
+
default: string;
|
|
124
|
+
lg: string;
|
|
125
|
+
};
|
|
126
|
+
}, {
|
|
127
|
+
variant: {
|
|
128
|
+
primary: string;
|
|
129
|
+
default: string;
|
|
130
|
+
secondary: string;
|
|
131
|
+
destructive: string;
|
|
132
|
+
outline: string;
|
|
133
|
+
ghost: string;
|
|
134
|
+
link: string;
|
|
135
|
+
themed: string;
|
|
136
|
+
};
|
|
137
|
+
size: {
|
|
138
|
+
sm: string;
|
|
139
|
+
default: string;
|
|
140
|
+
lg: string;
|
|
141
|
+
};
|
|
142
|
+
}>, unknown, unknown, undefined>>;
|
|
143
|
+
type HTMLButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement>;
|
|
144
|
+
type ButtonProps = ButtonProps$1 & VariantProps<typeof buttonVariants> & HTMLButtonProps & {
|
|
145
|
+
/**
|
|
146
|
+
* Theme overrides for button styling via CSS custom properties
|
|
147
|
+
*/
|
|
148
|
+
theme?: ButtonTheme;
|
|
149
|
+
};
|
|
150
|
+
declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>;
|
|
151
|
+
|
|
152
|
+
export { type ButtonProps as B, Button as a, buttonVariants as b };
|