Claude-skill-registry component-wrapper-architecture
Best practices for wrapping shadcn/ui components. Apply when creating 8-bit styled variants of existing shadcn/ui components.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/component-wrapper-architecture" ~/.claude/skills/majiayu000-claude-skill-registry-component-wrapper-architecture && rm -rf "$T"
manifest:
skills/data/component-wrapper-architecture/SKILL.mdsource content
Component Wrapper Architecture
8-bit components wrap shadcn/ui components rather than replacing them. This pattern maintains compatibility while adding retro styling.
Basic Wrapper Pattern
Structure:
- Import base component with alias
- Define variants using class-variance-authority
- Export separate interface for props
- Use ref prop (not forwardRef for React 19)
import { type VariantProps, cva } from "class-variance-authority"; import { cn } from "@/lib/utils"; import { Button as ShadcnButton } from "@/components/ui/button"; import "@/components/ui/8bit/styles/retro.css"; export const buttonVariants = cva("", { variants: { font: { normal: "", retro: "retro", }, variant: { default: "bg-foreground", // ... }, }, defaultVariants: { variant: "default", size: "default", }, }); export interface BitButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> { asChild?: boolean; ref?: React.Ref<HTMLButtonElement>; } function Button({ children, asChild, ...props }: BitButtonProps) { const { variant, size, className, font } = props; return ( <ShadcnButton {...props} className={cn( "rounded-none active:translate-y-1 transition-transform", className )} size={size} variant={variant} asChild={asChild} > {children} </ShadcnButton> ); }
Re-exporting Base Components
For components with multiple sub-components, re-export unchanged parts:
import { Dialog as ShadcnDialog, DialogHeader as ShadcnDialogHeader, DialogFooter as ShadcnDialogFooter, DialogDescription as ShadcnDialogDescription, } from "@/componentsconst Dialog = ShadcnDialog; const DialogHeader =/ui/dialog"; ShadcnDialogHeader; const DialogFooter = ShadcnDialogFooter; const DialogDescription = ShadcnDialogDescription; export { Dialog, DialogHeader, DialogFooter, DialogDescription, // ...custom implementations };
Card Wrapper Pattern
Use outer wrapper for pixelated borders while keeping base component:
function Card({ className, font, ...props }: BitCardProps) { return ( <div className={cn( "relative border-y-6 border-foreground dark:border-ring !p-0", className )} > <ShadcnCard {...props} className={cn( "rounded-none border-0 !w-full", font !== "normal" && "retro", className )} /> {/* Pixelated side borders */} <div className="absolute inset-0 border-x-6 -mx-1.5 border-foreground dark:border-ring pointer-events-none" aria-hidden="true" /> </div> ); }
Key Principles
- Alias imports - Use
pattern for base componentsas ShadcnComponent - Empty cva base - Variants often start empty, relying on CSS for styling
- Separate prop interface - Export
for TypeScriptBitComponentProps - React 19 ref - Use
instead of forwardRefref?: React.Ref<T> - rounded-none - Remove all border radius from base component
- Pass through props - Forward all props including
,size
,variantclassName - Conditional retro - Use
patternfont !== "normal" && "retro"
Component Examples
- Basic wrapper with pixel borderscomponents/ui/8bit/button.tsx
- Card with outer wrappercomponents/ui/8bit/card.tsx
- Multi-subcomponent wrappercomponents/ui/8bit/dialog.tsx