Files
gridpilot.gg/apps/website/ui/primitives/Surface.tsx
2026-01-18 17:55:04 +01:00

101 lines
3.1 KiB
TypeScript

import React, { ReactNode, ElementType, ComponentPropsWithoutRef, forwardRef, ForwardedRef } from 'react';
import { Box, BoxProps } from './Box';
/**
* WARNING: DO NOT VIOLATE THE PURPOSE OF THIS PRIMITIVE.
*
* Surface is a styled container with background, border, and shadow.
*
* - DO NOT add layout props (flex, grid, gap) - use Stack or Grid instead.
* - DO NOT add positioning props (absolute, top, zIndex).
*
* If you need a more specific layout, create a new component in apps/website/components.
*/
export interface SurfaceProps<T extends ElementType = 'div'> extends Omit<BoxProps<T>, 'children' | 'padding'> {
as?: T;
children?: ReactNode;
variant?: 'default' | 'muted' | 'dark' | 'glass' | 'gradient-blue' | 'gradient-gold' | 'gradient-purple' | 'gradient-green' | 'discord' | 'discord-inner';
rounded?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full' | string | boolean;
border?: boolean | string;
padding?: number;
className?: string;
shadow?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | 'discord' | string;
}
export const Surface = forwardRef(<T extends ElementType = 'div'>(
{
as,
children,
variant = 'default',
rounded = 'none',
border = false,
padding = 0,
className = '',
shadow = 'none',
...props
}: SurfaceProps<T> & ComponentPropsWithoutRef<T>,
ref: ForwardedRef<HTMLElement>
) => {
const variantClasses: Record<string, string> = {
default: 'bg-panel-gray',
muted: 'bg-panel-gray/40',
dark: 'bg-graphite-black',
glass: 'bg-graphite-black/60 backdrop-blur-md',
'gradient-blue': 'bg-gradient-to-br from-primary-accent/10 via-panel-gray/80 to-graphite-black',
'gradient-gold': 'bg-gradient-to-br from-warning-amber/10 via-panel-gray/80 to-graphite-black',
'gradient-purple': 'bg-gradient-to-br from-purple-600/10 via-panel-gray/80 to-graphite-black',
'gradient-green': 'bg-gradient-to-br from-success-green/10 via-panel-gray/80 to-graphite-black',
'discord': 'bg-gradient-to-b from-graphite-black to-panel-gray',
'discord-inner': 'bg-gradient-to-br from-panel-gray via-graphite-black to-panel-gray'
};
const shadowClasses: Record<string, string> = {
none: '',
sm: 'shadow-sm',
md: 'shadow-md',
lg: 'shadow-lg',
xl: 'shadow-xl',
discord: 'shadow-[0_0_80px_rgba(88,101,242,0.15)]'
};
const roundedClasses: Record<string, string> = {
none: 'rounded-none',
sm: 'rounded-sm',
md: 'rounded-md',
lg: 'rounded-lg',
xl: 'rounded-xl',
'2xl': 'rounded-2xl',
full: 'rounded-full'
};
const paddingClasses: Record<number, string> = {
0: 'p-0',
1: 'p-1',
2: 'p-2',
3: 'p-3',
4: 'p-4',
6: 'p-6',
8: 'p-8',
10: 'p-10',
12: 'p-12'
};
const classes = [
variantClasses[variant],
typeof rounded === 'string' && roundedClasses[rounded] ? roundedClasses[rounded] : '',
border ? 'border border-border-gray' : '',
paddingClasses[padding] || 'p-0',
shadowClasses[shadow],
className
].filter(Boolean).join(' ');
return (
<Box as={as} ref={ref} className={classes} {...props}>
{children}
</Box>
);
});
Surface.displayName = 'Surface';