Safety Inspector at Springfield Nuclear Power Plant
//
//
```
### Pattern 5: Polymorphic Component (as prop)
```tsx
// components/ui/box.tsx
import { cn } from "@/lib/utils";
import { type ElementType, type ComponentPropsWithoutRef } from "react";
type BoxProps = {
as?: T;
className?: string;
children?: React.ReactNode;
} & Omit, "as" | "className" | "children">;
export function Box({
as,
className,
children,
...props
}: BoxProps) {
const Component = as || "div";
return (
{children}
);
}
// Usage:
// Default div
// Section element
// Article element
// Link element
```
---
## Shadcn UI Integration
### Installing New Components
```bash
# Add single component
pnpm dlx shadcn@latest add button
# Add multiple components
pnpm dlx shadcn@latest add card badge avatar
# List available components
pnpm dlx shadcn@latest add
```
### Customizing Shadcn Components
```tsx
// Extend the button with Simpsons theme
// components/ui/simpsons-button.tsx
import { Button, type ButtonProps } from "@/components/ui/button";
import { cn } from "@/lib/utils";
interface SimpsonsButtonProps extends ButtonProps {
character?: "homer" | "bart" | "lisa" | "marge";
}
const characterColors = {
homer: "bg-amber-500 hover:bg-amber-600 text-white",
bart: "bg-orange-500 hover:bg-orange-600 text-white",
lisa: "bg-red-500 hover:bg-red-600 text-white",
marge: "bg-blue-500 hover:bg-blue-600 text-white",
};
export function SimpsonsButton({
character,
className,
variant,
...props
}: SimpsonsButtonProps) {
return (
);
}
```
### Using Radix Primitives Directly
```tsx
// When you need more control than Shadcn provides
import * as Dialog from "@radix-ui/react-dialog";
import { cn } from "@/lib/utils";
export function CustomDialog({
trigger,
title,
children,
}: {
trigger: React.ReactNode;
title: string;
children: React.ReactNode;
}) {
return (
{trigger}{title}
{children}
Close
✕
);
}
```
---
## Accessibility Patterns
### Keyboard Navigation
```tsx
// Ensure all interactive elements are keyboard accessible
"use client";
import { useRef, KeyboardEvent } from "react";
export function KeyboardNavigableList({ items }: { items: string[] }) {
const listRef = useRef(null);
function handleKeyDown(e: KeyboardEvent, index: number) {
const list = listRef.current;
if (!list) return;
const items = list.querySelectorAll('[role="listitem"]');
let nextIndex = index;
switch (e.key) {
case "ArrowDown":
e.preventDefault();
nextIndex = (index + 1) % items.length;
break;
case "ArrowUp":
e.preventDefault();
nextIndex = (index - 1 + items.length) % items.length;
break;
case "Home":
e.preventDefault();
nextIndex = 0;
break;
case "End":
e.preventDefault();
nextIndex = items.length - 1;
break;
default:
return;
}
(items[nextIndex] as HTMLElement).focus();
}
return (
{items.map((item, index) => (
handleKeyDown(e, index)}
>
{item}
))}
);
}
```
### ARIA Labels and Live Regions
```tsx
"use client";
import { useState } from "react";
import { Button } from "@/components/ui/button";
export function AccessibleCounter() {
const [count, setCount] = useState(0);
return (
{/* Live region announces changes to screen readers */}
{count}
);
}
```
### Focus Management
```tsx
"use client";
import { useEffect, useRef } from "react";
export function AutoFocusInput({ shouldFocus }: { shouldFocus: boolean }) {
const inputRef = useRef(null);
useEffect(() => {
if (shouldFocus && inputRef.current) {
inputRef.current.focus();
}
}, [shouldFocus]);
return (
);
}
```
---
## Dark Mode Support
### Using CSS Variables (Tailwind CSS 4)
```tsx
// Component automatically adapts to dark mode via CSS variables
export function ThemedCard({ children }: { children: React.ReactNode }) {
return (
{children}
);
}
// bg-background = var(--background)
// text-foreground = var(--foreground)
// These switch automatically with .dark class on html
```
### Manual Dark Mode Variants
```tsx
export function DarkModeExample() {
return (