Marco Rossi·
From a Figma description to an accessible, fully-typed React component
Creates a complete, accessible, responsive UI component from a design description or Figma-like specification.
Frontend Component from Design Spec
You are a senior frontend engineer building a production-grade UI component. Implement based on the design spec below.
## Design Specification
{{design_specification}}
## Technical Requirements
- Framework: {{frontend_framework}} (React, Vue, Angular, Svelte)
- Styling: {{styling_approach}} (CSS Modules, Styled Components, Tailwind, SCSS)
- Component library: {{component_library}} (MUI, shadcn, Ant Design, none)
- Accessibility: WCAG 2.1 AA minimum
- Responsive: Mobile-first, breakpoints at 640px, 768px, 1024px, 1280px
## Component Requirements
1. **Main Component** - Complete implementation with:
- Props interface with JSDoc comments
- Proper TypeScript types
- Forward refs where appropriate
- Composition pattern support
- Loading and error states
- Empty state handling
2. **Sub-components** - Any logical child components
3. **Custom Hooks** - Extract reusable logic into hooks
4. **Storybook Story** - For component documentation/testing
5. **Unit Tests** - Using {{testing_library}}, cover interactions and a11y
6. **CSS/Styling** - Complete styles matching the spec
Follow {{frontend_framework}} best practices. Ensure keyboard navigation and screen reader compatibility. Avoid unnecessary re-renders. Export named components with proper displayName.
Ergebnisse
Accessible, fully-typed `<Rating>` component — keyboard navigable, WCAG 2.1 AA, no library.
```tsx
import { useId, useState, type KeyboardEvent } from "react";
interface RatingProps {
/** Current value, 0–max. Controlled when provided. */
value?: number;
max?: number;
onChange?: (value: number) => void;
label?: string;
}
export function Rating({ value, max = 5, onChange, label = "Rating" }: RatingProps) {
const [hover, setHover] = useState<number | null>(null);
const groupId = useId();
const active = hover ?? value ?? 0;
const onKey = (e: KeyboardEvent, star: number) => {
if (e.key === "ArrowRight" || e.key === "ArrowUp") onChange?.(Math.min(max, star + 1));
if (e.key === "ArrowLeft" || e.key === "ArrowDown") onChange?.(Math.max(1, star - 1));
};
return (
<div role="radiogroup" aria-label={label} className="inline-flex gap-1">
{Array.from({ length: max }, (_, i) => i + 1).map((star) => (
<button
key={`${groupId}-${star}`}
role="radio"
aria-checked={star === value}
aria-label={`${star} of ${max}`}
tabIndex={star === (value || 1) ? 0 : -1}
onClick={() => onChange?.(star)}
onKeyDown={(e) => onKey(e, star)}
onMouseEnter={() => setHover(star)}
onMouseLeave={() => setHover(null)}
className={star <= active ? "text-amber-400" : "text-zinc-600"}
>
★
</button>
))}
</div>
);
}
Rating.displayName = "Rating";
```
Roving `tabIndex` + arrow-key handling means screen-reader and keyboard users get a proper radio group, not a pile of buttons.
Modell: Claude Sonnet 4
11 Likes2 SavesScore: 6
1 Kommentar
Ahmed Hassan·
This is going in our internal wiki, thanks for sharing.