"use client" import type React from "react" import { Button } from "@/components/ui/button" import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Badge } from "@/components/ui/badge" import { useState, useRef, useEffect } from "react" // Define our spacing scale in rem units const spacingScale = [ { name: "0.25rem", value: "0.25rem", tailwind: "1", px: "4px", description: "Tight spacing" }, { name: "0.5rem", value: "0.5rem", tailwind: "2", px: "8px", description: "Small spacing" }, { name: "0.75rem", value: "0.75rem", tailwind: "3", px: "12px", description: "Compact spacing" }, { name: "1rem", value: "1rem", tailwind: "4", px: "16px", description: "Base spacing" }, { name: "1.25rem", value: "1.25rem", tailwind: "5", px: "20px", description: "Comfortable spacing" }, { name: "1.5rem", value: "1.5rem", tailwind: "6", px: "24px", description: "Relaxed spacing" }, { name: "2rem", value: "2rem", tailwind: "8", px: "32px", description: "Section spacing" }, { name: "2.5rem", value: "2.5rem", tailwind: "10", px: "40px", description: "Large spacing" }, { name: "3rem", value: "3rem", tailwind: "12", px: "48px", description: "Extra large spacing" }, { name: "4rem", value: "4rem", tailwind: "16", px: "64px", description: "Maximum spacing" }, ] // Spacing Notation Component function SpacingNotation({ value, direction = "all", label, color = "primary", }: { value: string direction?: "top" | "bottom" | "left" | "right" | "horizontal" | "vertical" | "all" label?: string color?: "primary" | "secondary" | "accent" | "muted" }) { const colorClasses = { primary: "border-primary bg-primary/10 text-primary", secondary: "border-orange-500 bg-orange-500/10 text-orange-600", accent: "border-green-500 bg-green-500/10 text-green-600", muted: "border-muted-foreground bg-muted text-muted-foreground", } const directionSymbols = { top: "↑", bottom: "↓", left: "←", right: "→", horizontal: "↔", vertical: "↕", all: "⊞", } return (
{directionSymbols[direction]} {value} {label && ({label})}
) } // Interactive Annotated Component with precise positioning function InteractiveAnnotatedComponent({ children, spacingAreas = [], title, }: { children: React.ReactNode spacingAreas?: Array<{ type: "padding" | "margin" | "gap" value: string direction?: "top" | "bottom" | "left" | "right" | "horizontal" | "vertical" | "all" selector: string // CSS selector to target the specific element spacingType: "padding" | "margin" | "gap" label?: string description?: string }> title: string }) { const [hoveredArea, setHoveredArea] = useState(null) const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 }) const [overlayPositions, setOverlayPositions] = useState< Array<{ top: number; left: number; width: number; height: number }> >([]) const containerRef = useRef(null) // Calculate overlay positions based on actual DOM elements useEffect(() => { if (!containerRef.current) return const positions = spacingAreas.map((area) => { const element = containerRef.current?.querySelector(area.selector) as HTMLElement if (!element) return { top: 0, left: 0, width: 0, height: 0 } const containerRect = containerRef.current!.getBoundingClientRect() const elementRect = element.getBoundingClientRect() // Get computed styles to calculate actual spacing const computedStyle = window.getComputedStyle(element) let overlayRect = { top: elementRect.top - containerRect.top, left: elementRect.left - containerRect.left, width: elementRect.width, height: elementRect.height, } // Adjust overlay based on spacing type if (area.spacingType === "padding") { // For padding, highlight the inner content area const paddingTop = Number.parseFloat(computedStyle.paddingTop) const paddingLeft = Number.parseFloat(computedStyle.paddingLeft) const paddingRight = Number.parseFloat(computedStyle.paddingRight) const paddingBottom = Number.parseFloat(computedStyle.paddingBottom) overlayRect = { top: overlayRect.top + paddingTop, left: overlayRect.left + paddingLeft, width: overlayRect.width - paddingLeft - paddingRight, height: overlayRect.height - paddingTop - paddingBottom, } } else if (area.spacingType === "margin") { // For margin, highlight the margin area around the element const marginTop = Number.parseFloat(computedStyle.marginTop) const marginLeft = Number.parseFloat(computedStyle.marginLeft) const marginRight = Number.parseFloat(computedStyle.marginRight) const marginBottom = Number.parseFloat(computedStyle.marginBottom) overlayRect = { top: overlayRect.top - marginTop, left: overlayRect.left - marginLeft, width: overlayRect.width + marginLeft + marginRight, height: overlayRect.height + marginTop + marginBottom, } } return overlayRect }) setOverlayPositions(positions) }, [spacingAreas]) const handleMouseMove = (e: React.MouseEvent) => { const rect = e.currentTarget.getBoundingClientRect() setMousePosition({ x: e.clientX - rect.left, y: e.clientY - rect.top, }) } const getAreaColor = (type: "padding" | "margin" | "gap") => { switch (type) { case "padding": return "bg-primary/30 border-primary border-2" case "margin": return "bg-orange-500/30 border-orange-500 border-2" case "gap": return "bg-green-500/30 border-green-500 border-2" } } const getNotationColor = (type: "padding" | "margin" | "gap") => { switch (type) { case "padding": return "primary" as const case "margin": return "secondary" as const case "gap": return "accent" as const } } return (
{title}
setHoveredArea(null)} > {/* Component */}
{children}
{/* Interactive spacing overlays */} {overlayPositions.map((position, index) => (
setHoveredArea(index)} onMouseLeave={() => setHoveredArea(null)} /> ))} {/* Tooltip */} {hoveredArea !== null && (
200 ? "translateX(-100%)" : "none", }} >
{spacingAreas[hoveredArea].type} {spacingAreas[hoveredArea].description && ` • ${spacingAreas[hoveredArea].description}`}
)}
) } export function SpacingSection() { const [selectedExample, setSelectedExample] = useState("button") return (

Spacing

Our spacing system uses a consistent scale based on rem units. Hover over components below to explore their spacing interactively.

{/* Notation System Legend */}

Interactive Spacing Notation

Types

Padding
Margin
Gap

Directions

Top
Bottom
Left
Right
Horizontal
Vertical

How to Use

• Hover over components to see spacing
• Colored overlays show spacing areas
• Tooltips display exact values
• Direction arrows indicate application
{/* Spacing Scale */}

Spacing Scale

{spacingScale.map((spacing, index) => (
{/* Value and Tailwind class */}
{spacing.name}
{spacing.tailwind}
({spacing.px})
{/* Visual representation */}
{spacing.description}
))}
{/* Interactive Component Examples */}

Interactive Examples

{["button", "card", "form"].map((example) => ( ))}
Hover over the components below to explore their spacing interactively.
{selectedExample === "button" && (
)} {selectedExample === "card" && ( Card Title

Card content with consistent internal spacing for optimal readability and visual hierarchy.

)} {selectedExample === "form" && (
)}
{/* Additional Interactive Examples */}

Component Spacing Patterns

Design Development Featured Urgent
{/* Usage Guidelines */}

Spacing Guidelines

Component padding
Use 1rem (16px) for default internal spacing
Related elements
Use 1rem (16px) gap between related items
Section spacing
Use 2rem (32px) between distinct sections
Interactive exploration
Hover over components to understand their spacing structure
Consistency
Maintain consistent spacing within component types
Responsive scaling
Consider reducing spacing on smaller screens
) }