From b6325aa3fc01dbcf90da10dfdd81b62d7bb07758 Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Sat, 17 Jan 2026 15:58:27 +0000 Subject: [PATCH] Generated by Spark: Expand atomic component library until done. --- src/components/AtomicComponentShowcase.tsx | 453 +++++++++++++++++++ src/components/atoms/Alert.tsx | 51 +++ src/components/atoms/Avatar.tsx | 37 ++ src/components/atoms/Chip.tsx | 54 +++ src/components/atoms/Code.tsx | 34 ++ src/components/atoms/Container.tsx | 24 + src/components/atoms/Divider.tsx | 25 ++ src/components/atoms/Dot.tsx | 53 +++ src/components/atoms/HelperText.tsx | 22 + src/components/atoms/Image.tsx | 67 +++ src/components/atoms/Kbd.tsx | 21 + src/components/atoms/Label.tsx | 24 + src/components/atoms/Link.tsx | 40 ++ src/components/atoms/ProgressBar.tsx | 62 +++ src/components/atoms/README.md | 496 +++++++++++++++++++++ src/components/atoms/ScrollArea.tsx | 35 ++ src/components/atoms/Section.tsx | 24 + src/components/atoms/Skeleton.tsx | 36 ++ src/components/atoms/Spacer.tsx | 31 ++ src/components/atoms/Spinner.tsx | 17 + src/components/atoms/Stack.tsx | 63 +++ src/components/atoms/Timestamp.tsx | 31 ++ src/components/atoms/Tooltip.tsx | 36 ++ src/components/atoms/index.ts | 22 + 24 files changed, 1758 insertions(+) create mode 100644 src/components/AtomicComponentShowcase.tsx create mode 100644 src/components/atoms/Alert.tsx create mode 100644 src/components/atoms/Avatar.tsx create mode 100644 src/components/atoms/Chip.tsx create mode 100644 src/components/atoms/Code.tsx create mode 100644 src/components/atoms/Container.tsx create mode 100644 src/components/atoms/Divider.tsx create mode 100644 src/components/atoms/Dot.tsx create mode 100644 src/components/atoms/HelperText.tsx create mode 100644 src/components/atoms/Image.tsx create mode 100644 src/components/atoms/Kbd.tsx create mode 100644 src/components/atoms/Label.tsx create mode 100644 src/components/atoms/Link.tsx create mode 100644 src/components/atoms/ProgressBar.tsx create mode 100644 src/components/atoms/README.md create mode 100644 src/components/atoms/ScrollArea.tsx create mode 100644 src/components/atoms/Section.tsx create mode 100644 src/components/atoms/Skeleton.tsx create mode 100644 src/components/atoms/Spacer.tsx create mode 100644 src/components/atoms/Spinner.tsx create mode 100644 src/components/atoms/Stack.tsx create mode 100644 src/components/atoms/Timestamp.tsx create mode 100644 src/components/atoms/Tooltip.tsx diff --git a/src/components/AtomicComponentShowcase.tsx b/src/components/AtomicComponentShowcase.tsx new file mode 100644 index 0000000..0c7f405 --- /dev/null +++ b/src/components/AtomicComponentShowcase.tsx @@ -0,0 +1,453 @@ +import { + ActionButton, + IconButton, + Text, + Heading, + Link, + Divider, + Avatar, + Chip, + Code, + Kbd, + ProgressBar, + Skeleton, + Tooltip, + Alert, + Spinner, + Dot, + Image, + Label, + HelperText, + Container, + Section, + Stack, + Spacer, + Timestamp, + ScrollArea, + StatusBadge, + LoadingSpinner, + EmptyState, +} from '@/components/atoms' +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' +import { + Plus, + Download, + Trash, + Heart, + Star, + GitBranch, +} from '@phosphor-icons/react' + +export function AtomicComponentShowcase() { + return ( + + + +
+ Atomic Component Library + + Comprehensive collection of reusable atomic components for CodeForge + +
+ + + +
+ + + + Typography + Text elements and headings + + + +
+ Heading Level 1 + Heading Level 2 + Heading Level 3 + Heading Level 4 + Heading Level 5 + Heading Level 6 +
+ +
+ Body text - Default paragraph text + Muted text - For less important information + Caption text - Small descriptive text + Small text - Compact information +
+
+
+
+ + + + Buttons + Action buttons and icon buttons + + + + {}} variant="default" /> + {}} variant="outline" /> + {}} variant="ghost" /> + {}} variant="destructive" /> + } + label="With Icon" + onClick={() => {}} + /> + } + label="Tooltip" + onClick={() => {}} + tooltip="Download file" + /> + + + + } /> + } variant="default" /> + } variant="destructive" /> + } variant="outline" /> + + + + + + + Links + Text links with variants + + + + Default Link + Muted Link + Accent Link + Destructive Link + + External Link + + + + + + + + Badges & Chips + Status indicators and removable tags + + + + + + + + + + + + + Default + Primary + Accent + Muted + Small + {}}>Removable + + + + + + + + Dots & Indicators + Status dots with pulse animation + + + + + + Default + + + + Primary + + + + Success (pulse) + + + + Warning (pulse) + + + + Error (pulse) + + + + + + + + Avatar + User avatars with fallbacks + + + + + + + + + + + + + + + Code & Keyboard + Code snippets and keyboard shortcuts + + + +
+ + Use npm install to install dependencies. + +
+ +{`function hello() { + console.log("Hello, world!"); +}`} + + + Press + Ctrl + + + K + to search + +
+
+
+ + + + Progress Bars + Progress indicators with variants + + + +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + + + Loading States + Spinners and skeletons + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Alerts + Contextual feedback messages + + + + + This is an informational message with useful context. + + + Your changes have been saved successfully. + + + Please review your input before continuing. + + + An error occurred while processing your request. + + + + + + + + Form Elements + Labels and helper text + + + +
+ + + We'll never share your email with anyone else. + +
+
+ + + This field is required. + +
+
+ + + This username is available! + +
+
+
+
+ + + + Timestamps + Date and time formatting + + + + + Absolute: + + + + Relative: + + + + Custom format: + + + + + + + + + Tooltips + Hover to see tooltips + + + + + {}} /> + + + {}} /> + + + {}} /> + + + {}} /> + + + + + + + + Layout Components + Stack and spacing utilities + + + +
+ + +
1
+
2
+
3
+
+
+
+ + +
A
+
B
+
C
+
+
+
+ + + } /> + Center Content + } /> + +
+
+
+
+ + + + Empty States + Placeholder for empty content + + + } + title="No items found" + description="Get started by creating your first item" + /> + + +
+
+
+
+
+ ) +} diff --git a/src/components/atoms/Alert.tsx b/src/components/atoms/Alert.tsx new file mode 100644 index 0000000..f456881 --- /dev/null +++ b/src/components/atoms/Alert.tsx @@ -0,0 +1,51 @@ +import { ReactNode } from 'react' +import { Info, Warning, CheckCircle, XCircle } from '@phosphor-icons/react' +import { cn } from '@/lib/utils' + +interface AlertProps { + variant?: 'info' | 'warning' | 'success' | 'error' + title?: string + children: ReactNode + className?: string +} + +const variantConfig = { + info: { + icon: Info, + classes: 'bg-blue-50 border-blue-200 text-blue-900', + }, + warning: { + icon: Warning, + classes: 'bg-yellow-50 border-yellow-200 text-yellow-900', + }, + success: { + icon: CheckCircle, + classes: 'bg-green-50 border-green-200 text-green-900', + }, + error: { + icon: XCircle, + classes: 'bg-red-50 border-red-200 text-red-900', + }, +} + +export function Alert({ variant = 'info', title, children, className }: AlertProps) { + const config = variantConfig[variant] + const Icon = config.icon + + return ( +
+ +
+ {title &&
{title}
} +
{children}
+
+
+ ) +} diff --git a/src/components/atoms/Avatar.tsx b/src/components/atoms/Avatar.tsx new file mode 100644 index 0000000..fd5083a --- /dev/null +++ b/src/components/atoms/Avatar.tsx @@ -0,0 +1,37 @@ +import { cn } from '@/lib/utils' + +interface AvatarProps { + src?: string + alt?: string + fallback?: string + size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' + className?: string +} + +const sizeClasses = { + xs: 'w-6 h-6 text-xs', + sm: 'w-8 h-8 text-sm', + md: 'w-10 h-10 text-base', + lg: 'w-12 h-12 text-lg', + xl: 'w-16 h-16 text-xl', +} + +export function Avatar({ src, alt, fallback, size = 'md', className }: AvatarProps) { + const initials = fallback || alt?.slice(0, 2).toUpperCase() || '?' + + return ( +
+ {src ? ( + {alt} + ) : ( + {initials} + )} +
+ ) +} diff --git a/src/components/atoms/Chip.tsx b/src/components/atoms/Chip.tsx new file mode 100644 index 0000000..fb36da5 --- /dev/null +++ b/src/components/atoms/Chip.tsx @@ -0,0 +1,54 @@ +import { ReactNode } from 'react' +import { X } from '@phosphor-icons/react' +import { cn } from '@/lib/utils' + +interface ChipProps { + children: ReactNode + variant?: 'default' | 'primary' | 'accent' | 'muted' + size?: 'sm' | 'md' + onRemove?: () => void + className?: string +} + +const variantClasses = { + default: 'bg-secondary text-secondary-foreground', + primary: 'bg-primary text-primary-foreground', + accent: 'bg-accent text-accent-foreground', + muted: 'bg-muted text-muted-foreground', +} + +const sizeClasses = { + sm: 'px-2 py-0.5 text-xs', + md: 'px-3 py-1 text-sm', +} + +export function Chip({ + children, + variant = 'default', + size = 'md', + onRemove, + className +}: ChipProps) { + return ( + + {children} + {onRemove && ( + + )} + + ) +} diff --git a/src/components/atoms/Code.tsx b/src/components/atoms/Code.tsx new file mode 100644 index 0000000..0416b4c --- /dev/null +++ b/src/components/atoms/Code.tsx @@ -0,0 +1,34 @@ +import { ReactNode } from 'react' +import { cn } from '@/lib/utils' + +interface CodeProps { + children: ReactNode + inline?: boolean + className?: string +} + +export function Code({ children, inline = true, className }: CodeProps) { + if (inline) { + return ( + + {children} + + ) + } + + return ( +
+      {children}
+    
+ ) +} diff --git a/src/components/atoms/Container.tsx b/src/components/atoms/Container.tsx new file mode 100644 index 0000000..e913ad5 --- /dev/null +++ b/src/components/atoms/Container.tsx @@ -0,0 +1,24 @@ +import { ReactNode } from 'react' +import { cn } from '@/lib/utils' + +interface ContainerProps { + children: ReactNode + size?: 'sm' | 'md' | 'lg' | 'xl' | 'full' + className?: string +} + +const sizeClasses = { + sm: 'max-w-screen-sm', + md: 'max-w-screen-md', + lg: 'max-w-screen-lg', + xl: 'max-w-screen-xl', + full: 'max-w-full', +} + +export function Container({ children, size = 'xl', className }: ContainerProps) { + return ( +
+ {children} +
+ ) +} diff --git a/src/components/atoms/Divider.tsx b/src/components/atoms/Divider.tsx new file mode 100644 index 0000000..9f87515 --- /dev/null +++ b/src/components/atoms/Divider.tsx @@ -0,0 +1,25 @@ +import { cn } from '@/lib/utils' + +interface DividerProps { + orientation?: 'horizontal' | 'vertical' + className?: string + decorative?: boolean +} + +export function Divider({ + orientation = 'horizontal', + className, + decorative = true +}: DividerProps) { + return ( +
+ ) +} diff --git a/src/components/atoms/Dot.tsx b/src/components/atoms/Dot.tsx new file mode 100644 index 0000000..c64addf --- /dev/null +++ b/src/components/atoms/Dot.tsx @@ -0,0 +1,53 @@ +import { cn } from '@/lib/utils' + +interface DotProps { + variant?: 'default' | 'primary' | 'accent' | 'success' | 'warning' | 'error' + size?: 'xs' | 'sm' | 'md' | 'lg' + pulse?: boolean + className?: string +} + +const variantClasses = { + default: 'bg-muted-foreground', + primary: 'bg-primary', + accent: 'bg-accent', + success: 'bg-green-500', + warning: 'bg-yellow-500', + error: 'bg-destructive', +} + +const sizeClasses = { + xs: 'w-1.5 h-1.5', + sm: 'w-2 h-2', + md: 'w-3 h-3', + lg: 'w-4 h-4', +} + +export function Dot({ + variant = 'default', + size = 'sm', + pulse = false, + className +}: DotProps) { + return ( + + + {pulse && ( + + )} + + ) +} diff --git a/src/components/atoms/HelperText.tsx b/src/components/atoms/HelperText.tsx new file mode 100644 index 0000000..ce0cd33 --- /dev/null +++ b/src/components/atoms/HelperText.tsx @@ -0,0 +1,22 @@ +import { ReactNode } from 'react' +import { cn } from '@/lib/utils' + +interface HelperTextProps { + children: ReactNode + variant?: 'default' | 'error' | 'success' + className?: string +} + +const variantClasses = { + default: 'text-muted-foreground', + error: 'text-destructive', + success: 'text-green-600', +} + +export function HelperText({ children, variant = 'default', className }: HelperTextProps) { + return ( +

+ {children} +

+ ) +} diff --git a/src/components/atoms/Image.tsx b/src/components/atoms/Image.tsx new file mode 100644 index 0000000..415b3cd --- /dev/null +++ b/src/components/atoms/Image.tsx @@ -0,0 +1,67 @@ +import { useState } from 'react' +import { cn } from '@/lib/utils' + +interface ImageProps { + src: string + alt: string + width?: number | string + height?: number | string + fit?: 'cover' | 'contain' | 'fill' | 'none' | 'scale-down' + fallback?: string + className?: string + onLoad?: () => void + onError?: () => void +} + +export function Image({ + src, + alt, + width, + height, + fit = 'cover', + fallback, + className, + onLoad, + onError +}: ImageProps) { + const [error, setError] = useState(false) + const [loading, setLoading] = useState(true) + + const handleLoad = () => { + setLoading(false) + onLoad?.() + } + + const handleError = () => { + setError(true) + setLoading(false) + onError?.() + } + + const imgSrc = error && fallback ? fallback : src + + return ( +
+ {loading && ( +
+ )} + {alt} +
+ ) +} diff --git a/src/components/atoms/Kbd.tsx b/src/components/atoms/Kbd.tsx new file mode 100644 index 0000000..42e1e90 --- /dev/null +++ b/src/components/atoms/Kbd.tsx @@ -0,0 +1,21 @@ +import { ReactNode } from 'react' +import { cn } from '@/lib/utils' + +interface KbdProps { + children: ReactNode + className?: string +} + +export function Kbd({ children, className }: KbdProps) { + return ( + + {children} + + ) +} diff --git a/src/components/atoms/Label.tsx b/src/components/atoms/Label.tsx new file mode 100644 index 0000000..97e897c --- /dev/null +++ b/src/components/atoms/Label.tsx @@ -0,0 +1,24 @@ +import { ReactNode } from 'react' +import { cn } from '@/lib/utils' + +interface LabelProps { + children: ReactNode + htmlFor?: string + required?: boolean + className?: string +} + +export function Label({ children, htmlFor, required, className }: LabelProps) { + return ( + + ) +} diff --git a/src/components/atoms/Link.tsx b/src/components/atoms/Link.tsx new file mode 100644 index 0000000..34a2089 --- /dev/null +++ b/src/components/atoms/Link.tsx @@ -0,0 +1,40 @@ +import { ReactNode } from 'react' +import { cn } from '@/lib/utils' + +interface LinkProps { + href: string + children: ReactNode + variant?: 'default' | 'muted' | 'accent' | 'destructive' + external?: boolean + className?: string + onClick?: (e: React.MouseEvent) => void +} + +const variantClasses = { + default: 'text-foreground hover:text-primary underline-offset-4 hover:underline', + muted: 'text-muted-foreground hover:text-foreground underline-offset-4 hover:underline', + accent: 'text-accent hover:text-accent/80 underline-offset-4 hover:underline', + destructive: 'text-destructive hover:text-destructive/80 underline-offset-4 hover:underline', +} + +export function Link({ + href, + children, + variant = 'default', + external = false, + className, + onClick +}: LinkProps) { + const externalProps = external ? { target: '_blank', rel: 'noopener noreferrer' } : {} + + return ( + + {children} + + ) +} diff --git a/src/components/atoms/ProgressBar.tsx b/src/components/atoms/ProgressBar.tsx new file mode 100644 index 0000000..680a4ec --- /dev/null +++ b/src/components/atoms/ProgressBar.tsx @@ -0,0 +1,62 @@ +import { cn } from '@/lib/utils' + +interface ProgressBarProps { + value: number + max?: number + size?: 'sm' | 'md' | 'lg' + variant?: 'default' | 'accent' | 'destructive' + showLabel?: boolean + className?: string +} + +const sizeClasses = { + sm: 'h-1', + md: 'h-2', + lg: 'h-3', +} + +const variantClasses = { + default: 'bg-primary', + accent: 'bg-accent', + destructive: 'bg-destructive', +} + +export function ProgressBar({ + value, + max = 100, + size = 'md', + variant = 'default', + showLabel = false, + className +}: ProgressBarProps) { + const percentage = Math.min(Math.max((value / max) * 100, 0), 100) + + return ( +
+
+
+
+ {showLabel && ( + + {Math.round(percentage)}% + + )} +
+ ) +} diff --git a/src/components/atoms/README.md b/src/components/atoms/README.md new file mode 100644 index 0000000..12a2b44 --- /dev/null +++ b/src/components/atoms/README.md @@ -0,0 +1,496 @@ +# Atomic Component Library + +A comprehensive collection of reusable atomic components for the CodeForge low-code development platform. These components follow atomic design principles and provide consistent, accessible UI elements across the application. + +## Component Categories + +### Typography + +#### Heading +Semantic heading elements with consistent styling. + +```tsx +Main Title +Section Title +Custom Styled +``` + +**Props:** +- `level`: 1-6 (default: 1) +- `className`: string +- `children`: ReactNode + +#### Text +Paragraph text with semantic variants. + +```tsx +Default body text +Less important text +Small descriptive text +Compact information +``` + +**Props:** +- `variant`: 'body' | 'caption' | 'muted' | 'small' +- `className`: string +- `children`: ReactNode + +#### Link +Styled anchor elements with variant support. + +```tsx +Internal Link +External Link +``` + +**Props:** +- `href`: string +- `variant`: 'default' | 'muted' | 'accent' | 'destructive' +- `external`: boolean +- `onClick`: (e: MouseEvent) => void +- `className`: string + +--- + +### Buttons & Actions + +#### ActionButton +Full-featured button with icon and tooltip support. + +```tsx +} + onClick={handleSave} + variant="default" + tooltip="Save changes" +/> +``` + +**Props:** +- `label`: string +- `onClick`: () => void +- `icon`: ReactNode +- `variant`: 'default' | 'outline' | 'ghost' | 'destructive' +- `size`: 'default' | 'sm' | 'lg' | 'icon' +- `tooltip`: string +- `disabled`: boolean +- `className`: string + +#### IconButton +Compact icon-only button. + +```tsx +} onClick={handleAdd} /> +} variant="destructive" /> +``` + +**Props:** +- `icon`: ReactNode +- `onClick`: () => void +- `variant`: 'default' | 'secondary' | 'outline' | 'ghost' | 'destructive' +- `size`: 'default' | 'sm' | 'lg' | 'icon' +- `disabled`: boolean +- `title`: string +- `className`: string + +--- + +### Indicators & Badges + +#### StatusBadge +Status indicator with predefined states. + +```tsx + + +``` + +**Props:** +- `status`: 'active' | 'inactive' | 'pending' | 'error' | 'success' | 'warning' +- `label`: string (optional) + +#### Chip +Tag component with optional remove functionality. + +```tsx +React +Removable +``` + +**Props:** +- `variant`: 'default' | 'primary' | 'accent' | 'muted' +- `size`: 'sm' | 'md' +- `onRemove`: () => void +- `className`: string + +#### Dot +Small status indicator dot with pulse animation. + +```tsx + + +``` + +**Props:** +- `variant`: 'default' | 'primary' | 'accent' | 'success' | 'warning' | 'error' +- `size`: 'xs' | 'sm' | 'md' | 'lg' +- `pulse`: boolean +- `className`: string + +--- + +### Display Components + +#### Avatar +User avatar with fallback to initials. + +```tsx + + +``` + +**Props:** +- `src`: string +- `alt`: string +- `fallback`: string +- `size`: 'xs' | 'sm' | 'md' | 'lg' | 'xl' +- `className`: string + +#### Image +Enhanced image component with loading and error states. + +```tsx +Description +``` + +**Props:** +- `src`: string +- `alt`: string +- `width`: number | string +- `height`: number | string +- `fit`: 'cover' | 'contain' | 'fill' | 'none' | 'scale-down' +- `fallback`: string +- `onLoad`: () => void +- `onError`: () => void +- `className`: string + +#### Code +Inline or block code display. + +```tsx +npm install + + {`function hello() { + console.log("Hello"); + }`} + +``` + +**Props:** +- `inline`: boolean (default: true) +- `className`: string + +#### Kbd +Keyboard shortcut display. + +```tsx +Ctrl + K +``` + +**Props:** +- `className`: string + +--- + +### Feedback Components + +#### Alert +Contextual alert messages. + +```tsx + + This is important information. + + + Something went wrong. + +``` + +**Props:** +- `variant`: 'info' | 'warning' | 'success' | 'error' +- `title`: string +- `className`: string + +#### Spinner / LoadingSpinner +Loading indicators. + +```tsx + + +``` + +**Props (Spinner):** +- `size`: number +- `className`: string + +**Props (LoadingSpinner):** +- `size`: 'sm' | 'md' | 'lg' +- `className`: string + +#### ProgressBar +Progress indicator with percentage. + +```tsx + + +``` + +**Props:** +- `value`: number +- `max`: number (default: 100) +- `size`: 'sm' | 'md' | 'lg' +- `variant`: 'default' | 'accent' | 'destructive' +- `showLabel`: boolean +- `className`: string + +#### Skeleton +Loading placeholder. + +```tsx + + + +``` + +**Props:** +- `variant`: 'text' | 'rectangular' | 'circular' | 'rounded' +- `width`: string | number +- `height`: string | number +- `className`: string + +#### Tooltip +Hover tooltip. + +```tsx + + + +``` + +**Props:** +- `content`: ReactNode +- `side`: 'top' | 'right' | 'bottom' | 'left' +- `delayDuration`: number (default: 200) +- `className`: string + +--- + +### Form Components + +#### Label +Form field label with required indicator. + +```tsx + + +``` + +**Props:** +- `htmlFor`: string +- `required`: boolean +- `className`: string + +#### HelperText +Form field helper or error text. + +```tsx + + Enter your email address + + + This field is required + +``` + +**Props:** +- `variant`: 'default' | 'error' | 'success' +- `className`: string + +--- + +### Layout Components + +#### Container +Max-width content container with responsive padding. + +```tsx + +

Content

+
+``` + +**Props:** +- `size`: 'sm' | 'md' | 'lg' | 'xl' | 'full' +- `className`: string + +#### Section +Semantic section with vertical spacing. + +```tsx +
+

Section Title

+

Content

+
+``` + +**Props:** +- `spacing`: 'none' | 'sm' | 'md' | 'lg' | 'xl' +- `className`: string + +#### Stack +Flexbox layout with consistent spacing. + +```tsx + +
Item 1
+
Item 2
+
+ + + + + +``` + +**Props:** +- `direction`: 'horizontal' | 'vertical' +- `spacing`: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' +- `align`: 'start' | 'center' | 'end' | 'stretch' +- `justify`: 'start' | 'center' | 'end' | 'between' | 'around' | 'evenly' +- `wrap`: boolean +- `className`: string + +#### Spacer +Blank space for layout spacing. + +```tsx + + +``` + +**Props:** +- `size`: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' +- `axis`: 'horizontal' | 'vertical' | 'both' +- `className`: string + +#### Divider +Visual separator line. + +```tsx + + +``` + +**Props:** +- `orientation`: 'horizontal' | 'vertical' +- `decorative`: boolean (default: true) +- `className`: string + +#### ScrollArea +Custom styled scrollable area. + +```tsx + +
Long content...
+
+``` + +**Props:** +- `maxHeight`: string | number +- `className`: string + +--- + +### Utility Components + +#### Timestamp +Formatted date/time display. + +```tsx + + + +``` + +**Props:** +- `date`: Date | number | string +- `relative`: boolean +- `formatString`: string (default: 'MMM d, yyyy h:mm a') +- `className`: string + +--- + +## Design Principles + +1. **Consistency**: All components use the same design tokens and styling patterns +2. **Accessibility**: ARIA attributes and semantic HTML throughout +3. **Flexibility**: Comprehensive prop APIs for customization +4. **Performance**: Lightweight implementations with minimal dependencies +5. **Type Safety**: Full TypeScript support with proper prop types + +## Usage Guidelines + +### Import Pattern +```tsx +import { Heading, Text, Button, Stack } from '@/components/atoms' +``` + +### Composition +Atomic components are designed to be composed together: + +```tsx + + + + + + + John Doe + 2 hours ago + + + + + + + Check out this implementation detail. + + + } label="Like" onClick={handleLike} /> + } label="Share" onClick={handleShare} variant="outline" /> + + + +``` + +## Theme Integration + +All components respect the design system defined in `index.css`: +- Color variables: `--primary`, `--accent`, `--background`, etc. +- Typography: JetBrains Mono for code, IBM Plex Sans for UI +- Spacing scale: Consistent gap/padding values +- Border radius: `--radius` variable + +## Browser Support + +- Modern browsers (Chrome, Firefox, Safari, Edge) +- CSS Grid and Flexbox support required +- ES2020+ JavaScript features diff --git a/src/components/atoms/ScrollArea.tsx b/src/components/atoms/ScrollArea.tsx new file mode 100644 index 0000000..01b7a2e --- /dev/null +++ b/src/components/atoms/ScrollArea.tsx @@ -0,0 +1,35 @@ +import { ReactNode } from 'react' +import { cn } from '@/lib/utils' +import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area' + +interface ScrollAreaProps { + children: ReactNode + className?: string + maxHeight?: string | number +} + +export function ScrollArea({ children, className, maxHeight }: ScrollAreaProps) { + return ( + + + {children} + + + + + + + + + + ) +} diff --git a/src/components/atoms/Section.tsx b/src/components/atoms/Section.tsx new file mode 100644 index 0000000..4fede6d --- /dev/null +++ b/src/components/atoms/Section.tsx @@ -0,0 +1,24 @@ +import { ReactNode } from 'react' +import { cn } from '@/lib/utils' + +interface SectionProps { + children: ReactNode + spacing?: 'none' | 'sm' | 'md' | 'lg' | 'xl' + className?: string +} + +const spacingClasses = { + none: '', + sm: 'py-4', + md: 'py-8', + lg: 'py-12', + xl: 'py-16', +} + +export function Section({ children, spacing = 'md', className }: SectionProps) { + return ( +
+ {children} +
+ ) +} diff --git a/src/components/atoms/Skeleton.tsx b/src/components/atoms/Skeleton.tsx new file mode 100644 index 0000000..02ec9ec --- /dev/null +++ b/src/components/atoms/Skeleton.tsx @@ -0,0 +1,36 @@ +import { cn } from '@/lib/utils' + +interface SkeletonProps { + variant?: 'text' | 'rectangular' | 'circular' | 'rounded' + width?: string | number + height?: string | number + className?: string +} + +const variantClasses = { + text: 'rounded h-4', + rectangular: 'rounded-none', + circular: 'rounded-full', + rounded: 'rounded-lg', +} + +export function Skeleton({ + variant = 'rectangular', + width, + height, + className +}: SkeletonProps) { + return ( +
+ ) +} diff --git a/src/components/atoms/Spacer.tsx b/src/components/atoms/Spacer.tsx new file mode 100644 index 0000000..6e96b5f --- /dev/null +++ b/src/components/atoms/Spacer.tsx @@ -0,0 +1,31 @@ +import { cn } from '@/lib/utils' + +interface SpacerProps { + size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' + axis?: 'horizontal' | 'vertical' | 'both' + className?: string +} + +const sizeClasses = { + xs: 1, + sm: 2, + md: 4, + lg: 8, + xl: 16, + '2xl': 24, +} + +export function Spacer({ size = 'md', axis = 'vertical', className }: SpacerProps) { + const spacing = sizeClasses[size] + + return ( +