import React, { forwardRef, ForwardedRef, ElementType } from 'react'; /** * WARNING: DO NOT VIOLATE THE PURPOSE OF THIS PRIMITIVE. * * Box is a basic container primitive for spacing, sizing and basic layout. * * - DO NOT add decoration props (bg, border, shadow) - use Surface instead. * - DO NOT add positioning props (absolute, top, zIndex) - create a specific component. * - DO NOT add animation props - create a specific component. * * If you need more complex behavior, create a specific component in apps/website/components. */ export type Spacing = 0 | 0.5 | 1 | 1.5 | 2 | 2.5 | 3 | 3.5 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 16 | 20 | 24 | 28 | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | 64 | 72 | 80 | 96 | string; interface ResponsiveSpacing { base?: Spacing; sm?: Spacing; md?: Spacing; lg?: Spacing; xl?: Spacing; } export type ResponsiveValue = { base?: T; sm?: T; md?: T; lg?: T; xl?: T; '2xl'?: T; }; export interface BoxProps { as?: T; children?: React.ReactNode; // Spacing margin?: Spacing | ResponsiveSpacing; marginTop?: Spacing | 'auto' | ResponsiveSpacing; marginBottom?: Spacing | 'auto' | ResponsiveSpacing; marginLeft?: Spacing | 'auto' | ResponsiveSpacing; marginRight?: Spacing | 'auto' | ResponsiveSpacing; marginX?: Spacing | 'auto' | ResponsiveSpacing; marginY?: Spacing | 'auto' | ResponsiveSpacing; padding?: Spacing | ResponsiveSpacing; paddingTop?: Spacing | ResponsiveSpacing; paddingBottom?: Spacing | ResponsiveSpacing; paddingLeft?: Spacing | ResponsiveSpacing; paddingRight?: Spacing | ResponsiveSpacing; paddingX?: Spacing | ResponsiveSpacing; paddingY?: Spacing | ResponsiveSpacing; // Aliases (Deprecated - use full names) m?: Spacing | ResponsiveSpacing; mt?: Spacing | 'auto' | ResponsiveSpacing; mb?: Spacing | 'auto' | ResponsiveSpacing; ml?: Spacing | 'auto' | ResponsiveSpacing; mr?: Spacing | 'auto' | ResponsiveSpacing; mx?: Spacing | 'auto' | ResponsiveSpacing; my?: Spacing | 'auto' | ResponsiveSpacing; p?: Spacing | ResponsiveSpacing; pt?: Spacing | ResponsiveSpacing; pb?: Spacing | ResponsiveSpacing; pl?: Spacing | ResponsiveSpacing; pr?: Spacing | ResponsiveSpacing; px?: Spacing | ResponsiveSpacing; py?: Spacing | ResponsiveSpacing; // Sizing width?: string | number | ResponsiveValue; height?: string | number | ResponsiveValue; maxWidth?: string | ResponsiveValue; minWidth?: string | ResponsiveValue; maxHeight?: string | ResponsiveValue; minHeight?: string | ResponsiveValue; fullWidth?: boolean; fullHeight?: boolean; aspectRatio?: string; // Aliases w?: string | number | ResponsiveValue; h?: string | number | ResponsiveValue; // Display display?: 'block' | 'inline-block' | 'flex' | 'inline-flex' | 'grid' | 'none' | string | ResponsiveValue<'block' | 'inline-block' | 'flex' | 'inline-flex' | 'grid' | 'none' | string | any>; center?: boolean; overflow?: 'auto' | 'hidden' | 'visible' | 'scroll' | string; overflowX?: 'auto' | 'hidden' | 'visible' | 'scroll'; overflowY?: 'auto' | 'hidden' | 'visible' | 'scroll'; textAlign?: 'left' | 'center' | 'right' | 'justify' | string; visibility?: 'visible' | 'hidden' | 'collapse'; // Positioning position?: 'static' | 'relative' | 'absolute' | 'fixed' | 'sticky'; top?: string | number | ResponsiveValue; right?: string | number | ResponsiveValue; bottom?: string | number | ResponsiveValue; left?: string | number | ResponsiveValue; inset?: string | number; insetY?: string | number; insetX?: string | number; zIndex?: number; // Flex/Grid Item props flex?: number | string; flexShrink?: number; flexGrow?: number; flexDirection?: 'row' | 'row-reverse' | 'col' | 'col-reverse' | string | ResponsiveValue; flexWrap?: 'wrap' | 'nowrap' | 'wrap-reverse' | string; alignItems?: 'start' | 'center' | 'end' | 'stretch' | 'baseline' | string | ResponsiveValue; justifyContent?: 'start' | 'center' | 'end' | 'between' | 'around' | 'evenly' | string | ResponsiveValue; alignSelf?: 'auto' | 'start' | 'end' | 'center' | 'stretch' | 'baseline'; gap?: number | string | ResponsiveValue; gridCols?: number | ResponsiveValue; colSpan?: number | ResponsiveValue; order?: number | string | ResponsiveValue; // Interaction onClick?: React.MouseEventHandler; onMouseEnter?: React.MouseEventHandler; onMouseLeave?: React.MouseEventHandler; id?: string; role?: React.AriaRole; tabIndex?: number; // Internal use only borderTop?: string | boolean; borderBottom?: string | boolean; borderLeft?: string | boolean; borderRight?: string | boolean; bg?: string; rounded?: string | boolean; borderColor?: string; border?: string | boolean; color?: string; opacity?: number; transition?: any; hoverBg?: string; group?: boolean; groupHoverOpacity?: number; groupHoverBorderColor?: string; groupHoverWidth?: string; animate?: any; blur?: string; backdropBlur?: string; pointerEvents?: string; bgOpacity?: number; borderWidth?: string | number; borderStyle?: string; initial?: any; variants?: any; whileHover?: any; whileTap?: any; onHoverStart?: any; onHoverEnd?: any; whileInView?: any; viewport?: any; custom?: any; exit?: any; translateX?: string; translateY?: string; translate?: string; rotate?: string; scale?: string | number; perspective?: string | number; whiteSpace?: 'normal' | 'nowrap' | 'pre' | 'pre-line' | 'pre-wrap'; cursor?: string; fontSize?: string | ResponsiveValue; fontWeight?: string | number; weight?: string | number; letterSpacing?: string; lineHeight?: string | number; font?: string; ring?: string; hideScrollbar?: boolean; truncate?: boolean; src?: string; alt?: string; draggable?: boolean | string; min?: string | number; max?: string | number; step?: string | number; value?: string | number; onChange?: React.ChangeEventHandler; onError?: React.ReactEventHandler; onScroll?: React.UIEventHandler; onDragOver?: React.DragEventHandler; onDragLeave?: React.DragEventHandler; onDrop?: React.DragEventHandler; placeholder?: string; title?: string; size?: string | number | ResponsiveValue; accept?: string; multiple?: boolean; autoPlay?: boolean; loop?: boolean; muted?: boolean; playsInline?: boolean; objectFit?: 'fill' | 'contain' | 'cover' | 'none' | 'scale-down'; type?: string; checked?: boolean; disabled?: boolean; onSubmit?: React.FormEventHandler; onBlur?: React.FocusEventHandler; onKeyDown?: React.KeyboardEventHandler; onMouseDown?: React.MouseEventHandler; onMouseUp?: React.MouseEventHandler; onMouseMove?: React.MouseEventHandler; responsiveColSpan?: number | ResponsiveValue; responsiveGridCols?: number | ResponsiveValue; clickable?: boolean; hoverScale?: boolean | number; hoverBorderColor?: string; hoverTextColor?: string; groupHoverScale?: boolean; groupHoverTextColor?: string; shadow?: string; transform?: boolean | string; lineClamp?: number; fill?: string | boolean; viewBox?: string; stroke?: string; strokeWidth?: string | number; backgroundSize?: string; backgroundPosition?: string; backgroundImage?: string; htmlFor?: string; minH?: string | number | ResponsiveValue; borderOpacity?: number; rows?: number; backgroundColor?: string; outline?: string; focusBorderColor?: string; href?: string; name?: string; borderTopColor?: string; onPointerDown?: React.PointerEventHandler; onPointerMove?: React.PointerEventHandler; onPointerUp?: React.PointerEventHandler; /** @deprecated DO NOT USE. Use semantic props instead. */ className?: string; /** @deprecated DO NOT USE. Use semantic props instead. */ style?: React.CSSProperties; } export const Box = forwardRef(( { as, children, margin, marginTop, marginBottom, marginLeft, marginRight, marginX, marginY, padding, paddingTop, paddingBottom, paddingLeft, paddingRight, paddingX, paddingY, m, mt, mb, ml, mr, mx, my, p, pt, pb, pl, pr, px, py, width, height, w, h, maxWidth, minWidth, maxHeight, minHeight, fullWidth, fullHeight, aspectRatio, display, center, overflow, overflowX, overflowY, textAlign, visibility, position, top, right, bottom, left, inset, insetY, insetX, zIndex, flex, flexShrink, flexGrow, flexDirection, flexWrap, alignItems, justifyContent, alignSelf, gap, gridCols, colSpan, order, onClick, onMouseEnter, onMouseLeave, id, role, tabIndex, style: styleProp, className, borderTop, borderBottom, borderLeft, borderRight, bg, rounded, borderColor, border, color, opacity, transition, hoverBg, group, groupHoverOpacity, groupHoverBorderColor, groupHoverWidth, animate, blur, backdropBlur, pointerEvents, bgOpacity, borderWidth, borderStyle, initial, variants, whileHover, whileTap, onHoverStart, onHoverEnd, whileInView, viewport, custom, exit, translateX, translateY, translate, rotate, scale, perspective, whiteSpace, cursor, fontSize, fontWeight, weight, letterSpacing, lineHeight, font, ring, hideScrollbar, truncate, src, alt, draggable, min, max, step, value, onChange, onError, onScroll, onDragOver, onDragLeave, onDrop, placeholder, title, size, accept, multiple, autoPlay, loop, muted, playsInline, objectFit, type, checked, disabled, onSubmit, onBlur, onKeyDown, onMouseDown, onMouseUp, onMouseMove, responsiveColSpan, responsiveGridCols, clickable, hoverScale, hoverBorderColor, hoverTextColor, groupHoverScale, groupHoverTextColor, shadow, transform, lineClamp, fill, viewBox, stroke, strokeWidth, backgroundSize, backgroundPosition, backgroundImage, htmlFor, minH, borderOpacity, rows, backgroundColor, outline, focusBorderColor, href, name, borderTopColor, onPointerDown, onPointerMove, onPointerUp, ...props }: BoxProps, ref: ForwardedRef ) => { const Tag = (as as ElementType) || 'div'; const spacingMap: Record = { 0: '0', 0.5: '0.5', 1: '1', 1.5: '1.5', 2: '2', 2.5: '2.5', 3: '3', 3.5: '3.5', 4: '4', 5: '5', 6: '6', 7: '7', 8: '8', 9: '9', 10: '10', 11: '11', 12: '12', 14: '14', 16: '16', 20: '20', 24: '24', 28: '28', 32: '32', 36: '36', 40: '40', 44: '44', 48: '48', 52: '52', 56: '56', 60: '60', 64: '64', 72: '72', 80: '80', 96: '96', 'auto': 'auto', 'none': '0', 'xs': '2', 'sm': '4', 'md': '6', 'lg': '8', 'xl': '12' }; const getSpacingClass = (prefix: string, value: Spacing | 'auto' | ResponsiveSpacing | undefined) => { if (value === undefined) return ''; if (typeof value === 'object') { const classes = []; if (value.base !== undefined) classes.push(`${prefix}-${spacingMap[value.base]}`); if (value.sm !== undefined) classes.push(`sm:${prefix}-${spacingMap[value.sm]}`); if (value.md !== undefined) classes.push(`md:${prefix}-${spacingMap[value.md]}`); if (value.lg !== undefined) classes.push(`lg:${prefix}-${spacingMap[value.lg]}`); if (value.xl !== undefined) classes.push(`xl:${prefix}-${spacingMap[value.xl]}`); return classes.join(' '); } return `${prefix}-${spacingMap[value]}`; }; const getResponsiveClasses = (prefix: string, value: any | ResponsiveValue | undefined) => { if (value === undefined) return ''; if (typeof value === 'object') { const classes = []; if (value.base !== undefined) classes.push(prefix ? `${prefix}-${value.base}` : String(value.base)); if (value.sm !== undefined) classes.push(prefix ? `sm:${prefix}-${value.sm}` : `sm:${value.sm}`); if (value.md !== undefined) classes.push(prefix ? `md:${prefix}-${value.md}` : `md:${value.md}`); if (value.lg !== undefined) classes.push(prefix ? `lg:${prefix}-${value.lg}` : `lg:${value.lg}`); if (value.xl !== undefined) classes.push(prefix ? `xl:${prefix}-${value.xl}` : `xl:${value.xl}`); if (value['2xl'] !== undefined) classes.push(prefix ? `2xl:${prefix}-${value['2xl']}` : `2xl:${value['2xl']}`); return classes.join(' '); } return prefix ? `${prefix}-${value}` : String(value); }; const classes = [ getSpacingClass('m', margin || m), getSpacingClass('mt', marginTop || mt), getSpacingClass('mb', marginBottom || mb), getSpacingClass('ml', marginLeft || ml), getSpacingClass('mr', marginRight || mr), getSpacingClass('mx', marginX || mx), getSpacingClass('my', marginY || my), getSpacingClass('p', padding || p), getSpacingClass('pt', paddingTop || pt), getSpacingClass('pb', paddingBottom || pb), getSpacingClass('pl', paddingLeft || pl), getSpacingClass('pr', paddingRight || pr), getSpacingClass('px', paddingX || px), getSpacingClass('py', paddingY || py), fullWidth ? 'w-full' : getResponsiveClasses('w', width || w), fullHeight ? 'h-full' : getResponsiveClasses('h', height || h), getResponsiveClasses('max-w', maxWidth), getResponsiveClasses('min-w', minWidth), getResponsiveClasses('max-h', maxHeight), getResponsiveClasses('min-h', minHeight || minH), getResponsiveClasses('', display), center ? 'flex items-center justify-center' : '', overflow ? (overflow.includes(':') ? overflow : `overflow-${overflow}`) : '', overflowX ? `overflow-x-${overflowX}` : '', overflowY ? `overflow-y-${overflowY}` : '', textAlign ? `text-${textAlign}` : '', visibility ? visibility : '', position ? position : '', getResponsiveClasses('top', top), getResponsiveClasses('right', right), getResponsiveClasses('bottom', bottom), getResponsiveClasses('left', left), inset !== undefined ? `inset-${inset}` : '', insetY !== undefined ? `inset-y-${insetY}` : '', insetX !== undefined ? `inset-x-${insetX}` : '', zIndex !== undefined ? `z-${zIndex}` : '', flex !== undefined ? `flex-${flex}` : '', flexShrink !== undefined ? `flex-shrink-${flexShrink}` : '', flexGrow !== undefined ? `flex-grow-${flexGrow}` : '', getResponsiveClasses('flex', flexDirection), flexWrap ? `flex-${flexWrap}` : '', getResponsiveClasses('items', alignItems), getResponsiveClasses('justify', justifyContent), alignSelf !== undefined ? `self-${alignSelf}` : '', gap ? getResponsiveClasses('gap', gap) : '', getResponsiveClasses('grid-cols', gridCols || responsiveGridCols), getResponsiveClasses('col-span', colSpan || responsiveColSpan), getResponsiveClasses('order', order), group ? 'group' : '', animate === 'spin' ? 'animate-spin' : (animate === 'pulse' ? 'animate-pulse' : ''), blur ? `blur-${blur}` : '', backdropBlur ? `backdrop-blur-${backdropBlur}` : '', pointerEvents ? `pointer-events-${pointerEvents}` : '', hideScrollbar ? 'scrollbar-hide' : '', truncate ? 'truncate' : '', clickable ? 'cursor-pointer' : '', lineClamp ? `line-clamp-${lineClamp}` : '', (bg || backgroundColor)?.startsWith('bg-') ? (bg || backgroundColor) : '', borderColor?.startsWith('border-') ? borderColor : '', color?.startsWith('text-') ? color : '', className ].filter(Boolean).join(' '); const style: React.CSSProperties = { ...(typeof width === 'string' || typeof width === 'number' ? { width } : {}), ...(typeof height === 'string' || typeof height === 'number' ? { height } : {}), ...(typeof maxWidth === 'string' ? { maxWidth } : {}), ...(typeof minWidth === 'string' ? { minWidth } : {}), ...(typeof maxHeight === 'string' ? { maxHeight } : {}), ...(typeof minHeight === 'string' ? { minHeight } : {}), ...(typeof minH === 'string' || typeof minH === 'number' ? { minHeight: minH } : {}), ...(aspectRatio ? { aspectRatio } : {}), ...(typeof top === 'string' || typeof top === 'number' ? { top } : {}), ...(typeof right === 'string' || typeof right === 'number' ? { right } : {}), ...(typeof bottom === 'string' || typeof bottom === 'number' ? { bottom } : {}), ...(typeof left === 'string' || typeof left === 'number' ? { left } : {}), ...(typeof borderTop === 'string' ? { borderTop } : (borderTop === true ? { borderTop: '1px solid var(--ui-color-border-default)' } : {})), ...(typeof borderBottom === 'string' ? { borderBottom } : (borderBottom === true ? { borderBottom: '1px solid var(--ui-color-border-default)' } : {})), ...(typeof borderLeft === 'string' ? { borderLeft } : (borderLeft === true ? { borderLeft: '1px solid var(--ui-color-border-default)' } : {})), ...(typeof borderRight === 'string' ? { borderRight } : (borderRight === true ? { borderRight: '1px solid var(--ui-color-border-default)' } : {})), ...(borderTopColor ? { borderTopColor: borderTopColor.startsWith('border-') ? undefined : borderTopColor } : {}), ...(borderOpacity !== undefined ? { borderOpacity } : {}), ...(bg || backgroundColor ? { background: (bg || backgroundColor)!.startsWith('bg-') ? undefined : (bg || backgroundColor) } : {}), ...(rounded === true ? { borderRadius: 'var(--ui-radius-md)' } : (typeof rounded === 'string' ? { borderRadius: rounded.includes('rem') || rounded.includes('px') ? rounded : `var(--ui-radius-${rounded})` } : {})), ...(borderColor ? { borderColor: borderColor.startsWith('border-') ? undefined : borderColor } : {}), ...(border === true ? { border: '1px solid var(--ui-color-border-default)' } : (typeof border === 'string' ? { border } : {})), ...(color ? { color: color.startsWith('text-') ? undefined : color } : {}), ...(opacity !== undefined ? { opacity } : {}), ...(fontSize ? (typeof fontSize === 'string' ? { fontSize } : {}) : {}), ...(fontWeight ? { fontWeight } : {}), ...(weight ? { fontWeight: weight } : {}), ...(shadow ? { boxShadow: shadow.startsWith('shadow-') ? undefined : shadow } : {}), ...(transform === true ? { transform: 'auto' } : (typeof transform === 'string' ? { transform } : {})), ...(translateX ? { translateX } : {}), ...(translateY ? { translateY } : {}), ...(translate ? { translate } : {}), ...(rotate ? { rotate } : {}), ...(scale ? { scale } : {}), ...(perspective ? { perspective } : {}), ...(whiteSpace ? { whiteSpace } : {}), ...(typeof fill === 'string' ? { fill } : (fill === true ? { fill: 'currentColor' } : {})), ...(stroke ? { stroke } : {}), ...(strokeWidth ? { strokeWidth } : {}), ...(backgroundSize ? { backgroundSize } : {}), ...(backgroundPosition ? { backgroundPosition } : {}), ...(backgroundImage ? { backgroundImage } : {}), ...(styleProp || {}) }; return ( } className={classes} onClick={onClick} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} onMouseDown={onMouseDown} onMouseUp={onMouseUp} onMouseMove={onMouseMove} onKeyDown={onKeyDown} onBlur={onBlur} onSubmit={onSubmit} onScroll={onScroll} onDragOver={onDragOver} onDragLeave={onDragLeave} onDrop={onDrop} onError={onError} style={Object.keys(style).length > 0 ? style : undefined} id={id} role={role} tabIndex={tabIndex} type={type} src={src} alt={alt} draggable={draggable as any} min={min} max={max} step={step} value={value} onChange={onChange} placeholder={placeholder} title={title} autoPlay={autoPlay} loop={loop} muted={muted} playsInline={playsInline} viewBox={viewBox} htmlFor={htmlFor} rows={rows} href={href} name={name} multiple={multiple} onPointerDown={onPointerDown} onPointerMove={onPointerMove} onPointerUp={onPointerUp} {...props} > {children} ); }); Box.displayName = 'Box';