UI Component Spec — Design to Tailwind Handoff

    1

    Turn a component idea into a build-ready spec: props, variants, every state, tokens, the a11y contract, and the code.

    $9

    Secure checkout via Stripe

    30-day refund guarantee

    Converts to your local currency at checkout

    0 installsSecurity scanned

    Works with the AI tools you already use

    Claude CodeClaude CodeCursorCursorCodex CLICodex CLIGitHub CopilotGitHub CopilotGemini CLIGemini CLI+20 more

    UI Component Spec — Design to Tailwind Handoff

    Example session with this skill installed

    Spec out a Toast/notification component for our React app. We use Tailwind and Radix, and we have semantic tokens already. Needs success, error and warning kinds, an optional action button, and it should stack when there are several.

    • Read your context and instructions
    • Compiled the ui component spec
    • Generated the app files

    Toast

    Status: draft · Built on: Radix @radix-ui/react-toast · Owner: Design Systems

    Purpose

    A short, transient message confirming something happened or reporting that it didn't.

    Use when: the result isn't already visible on screen — a background job finished, a save failed, an item was deleted with an undo available.
    Don't use when: the user must act before continuing → use Dialog. The message belongs to one field → use inline error text. The state is permanent → use Banner.

    Anatomy

    ┌─ Root ──────────────────────────────────────────┐
    │ ┌Icon┐  ┌─ Title ──────────────┐  ┌─ Close ─┐  │
    │ └────┘  │ Description          │  └─────────┘  │
    │         └──────────────────────┘  ┌─ Action ─┐ │
    └───────────────────────────────────└──────────┘─┘
    
    PartRequiredNotes
    RootRadix Toast.Root; receives className and ...rest
    IconDerived from kind, 20px, aria-hidden="true"
    TitleRadix Toast.Title, 1 line, truncates
    DescriptionRadix Toast.Description, max 2 lines
    ActionRadix Toast.Action, requires altText
    CloseRadix Toast.Close, icon button
    ViewportOne per app, rendered at root

    Props

    PropTypeDefaultReqDescription
    kind'success' | 'error' | 'warning' | 'info''info'Sets icon, accent and live-region politeness
    titlestringOne line, ≤ 60 chars
    descriptionstring≤ 140 chars
    action{ label: string; onClick: () => void; altText: string }altText is required by Radix for screen readers
    durationnumber | null5000null = never auto-dismiss
    onOpenChange(open: boolean) => void
    classNamestringMerged via twMerge onto Root

    Controlled/uncontrolled: uncontrolled by default via the useToast() hook; open + onOpenChange for controlled use.

    Variants

    successerrorwarninginfo
    IconCheckCircleAlertCircleAlertTriangleInfo
    Accent (left border 3px)--color-success--color-danger--color-warning--color-info
    Default duration5000null80005000
    rolestatusalertalertstatus

    DECISION NEEDED: should error auto-dismiss? Recommend

    no (duration: null) — an error the user didn't see is an error that gets reported as a bug. Overridable per call.

    States

    StateBackgroundBorderOther
    default--color-surface-raised1px --color-border, 3px left accentshadow --shadow-md
    hover--color-surface-raisedsametimer pauses (Radix default)
    focus-withinsamesametimer pauses; Close and Action show their own rings
    swipingsamesamefollows pointer X, 0.9 opacity
    stacked (2nd+)samesamesee Stacking

    Close / Action focus: focus-visible:ring-2 ring-focus ring-offset-2 ring-offset-surface-raised

    Sizing

    TokenValue
    min-width / max-width320px / 440px
    padding16px
    gap icon → text12px
    gap title → description2px
    radius--radius-md (6px)
    icon20px
    close target24×24 (meets WCAG 2.2 SC 2.5.8)
    viewport offset16px from edge; 24px ≥ 768px

    Typography

    Title 14px / 600 / 1.4 · Description 14px / 400 / 1.5, --color-text-muted · Action 14px / 600
    Title truncates at 1 line; description clamps at 2 (line-clamp-2).

    Stacking

    • Max 3 visible; older ones collapse behind with translateY(-8px) scale(0.97) per level.
    • New toasts enter at the front. Queue beyond 3 — don't drop.
    • Hovering the stack expands it to full height.
    • Toast.Viewport is the single source of stacking order — don't manage it per toast.

    Accessibility

    Element: Radix Toast.Root → renders <li> inside the viewport's <ol>.
    Live region: the viewport is the live region and must be in the DOM before any toast is inserted. role="status" for success/info (polite), role="alert" for error/warning (assertive).

    KeyAction
    F8Focus the toast viewport (Radix hotkey)
    TabMove through Action, then Close
    EscapeDismiss focused toast

    Focus: toasts never steal focus. Focus returns to where it was on dismiss.
    Announcements: action.altText is what a screen reader hears — write it as an instruction ("Press F8 then Enter to undo"), not a repeat of the label.
    Reduced motion: slide-in and swipe become a 120ms opacity fade; stacking offset applies instantly with no transition.

    Behaviour

    • Timer pauses on hover, focus-within, and when the tab is backgrounded.
    • Identical consecutive toasts collapse into one with a count badge rather than stacking duplicates.
    • Action click dismisses the toast unless onClick returns false.
    • Swipe right dismisses on touch; 80px threshold.

    Responsive

    BreakpointChange
    < 640pxFull width minus 32px, anchored bottom, swipe down to dismiss
    ≥ 640px320–440px, anchored bottom-right, swipe right

    Implementation

    const toast = cva(
      'pointer-events-auto relative flex w-full items-start gap-3 rounded-md border ' +
      'border-l-[3px] bg-surface-raised p-4 shadow-md ' +
      'data-[state=open]:animate-in data-[state=open]:slide-in-from-bottom-2 ' +
      'data-[state=closed]:animate-out data-[state=closed]:fade-out-80 ' +
      'data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] ' +
      'motion-reduce:animate-none motion-reduce:transition-none',
      {
        variants: {
          kind: {
            success: 'border-l-success',
            error:   'border-l-danger',
            warning: 'border-l-warning',
            info:    'border-l-info',
          },
        },
        defaultVariants: { kind: 'info' },
      }
    )
    
    const ICON = { success: CheckCircle, error: AlertCircle, warning: AlertTriangle, info: Info }
    const POLITENESS = { success: 'status', error: 'alert', warning: 'alert', info: 'status' } as const
    
    <Toast.Root className={cn(toast({ kind }), className)}
                duration={duration} role={POLITENESS[kind]} {...rest}>
      <Icon className="mt-0.5 size-5 shrink-0" aria-hidden="true" />
      <div className="min-w-0 flex-1">
        <Toast.Title className="truncate text-sm font-semibold">{title}</Toast.Title>
        {description && (
          <Toast.Description className="mt-0.5 line-clamp-2 text-sm text-muted">
            {description}
          </Toast.Description>
        )}
      </div>
      {action && (
        <Toast.Action altText={action.altText} asChild>
          <button className="shrink-0 text-sm font-semibold text-primary
                             focus-visible:outline-none focus-visible:ring-2
                             focus-visible:ring-focus focus-visible:ring-offset-2">
            {action.label}
          </button>
        </Toast.Action>
      )}
      <Toast.Close className="shrink-0 rounded p-0.5 text-muted hover:text-text
                              focus-visible:outline-none focus-visible:ring-2
                              focus-visible:ring-focus" aria-label="Dismiss">
        <X className="size-4" />
      </Toast.Close>
    </Toast.Root>
    

    Test cases

    • Each kind renders the right icon, accent and role
    • Error toast does not auto-dismiss
    • Timer pauses on hover and on focus-within
    • F8 → Tab → Enter fires the action
    • Escape dismisses the focused toast only
    • 4th toast queues rather than dropping
    • 200-character description clamps to 2 lines, no overflow
    • prefers-reduced-motion removes slide and swipe animation
    • Contrast passes for all four accents in light and dark
    • Close button target ≥ 24×24

    Open decisions

    • DECISION NEEDED: error auto-dismiss — recommend null (see Variants).
    • DECISION NEEDED: do we need a loading toast for long jobs? Recommend no — use an inline progress indicator; a toast that lives for 40s isn't a toast.

    ui-component-spec-design-to-tailwind-han-app.zip

    ZIP · project files

    Generated

    Example file from a real run - the skill writes it into your workspace.

    Connects securely to your tools. The creator never sees your data.

    What you get

    Generate build-ready component APIs from design descriptions.Define accessibility and keyboard interaction contracts for complex UI.Standardize component states across a design system.Create implementation-ready Tailwind and CVA code blocks.

    About this skill

    The problem

    "Here's the design" is where most handoffs end, and where the six-question Slack thread begins. What are the props? What happens on hover? What about disabled and loading at the same time? What's the keyboard behaviour? Half of it gets guessed, and the guesses diverge across the codebase.

    What it does

    • Produces a full component spec: purpose and when not to use it, named anatomy, a real props table, a variant by size grid with invalid combinations marked, every state with its exact token, sizing, typography and responsive behaviour.
    • Writes the accessibility contract — correct element, keyboard table, focus management, ARIA, reduced-motion fallback — using the WAI-ARIA patterns for the twelve components this comes up for most.
    • Gives real implementation code: cva variants, twMerge overrides, semantic tokens instead of hex values, focus-visible instead of focus.
    • Flags genuinely open questions as DECISION NEEDED with a recommendation, rather than silently picking.

    Why this beats prompting it yourself

    A spec is only useful if it's complete, and completeness is exactly what a freeform answer misses. This enforces the sections that get skipped — the invalid variant combinations, the disabled-and-loading interaction, where focus goes on close. It also extends what you already use rather than reinventing it: if you're on Radix or shadcn, it specs the visual layer and says which primitive handles the rest.

    Use cases

    • Adding a component to a design system and wanting it documented once, properly
    • Handing a design to an engineer who isn't in the room
    • Building a component yourself and wanting the states enumerated before you start
    • Reviewing an existing component for what's missing

    Known limitations

    Tailwind and React are the default examples; other stacks are supported but get less idiomatic code. Doesn't generate visual designs — it specs decisions already made.

    How to install

    Works the same in every agent - Claude, Cursor, Codex, Copilot and 20+ more.

    ~30 seconds
    1. 1

      Download the ZIP

      Free skills download straight away. Paid skills unlock right after purchase.

    2. 2

      Unzip into your skills folder

      Every agent reads skills from one folder on your machine. Drop the unzipped folder in there.

    3. 3

      Ask your agent to use it

      Restart the agent if it was already running. It picks the skill up automatically - no config needed.

    Skills folder by agent

    Click the path to copy it. Create the folder if it does not exist yet.

    Reviews

    No reviews yet

    Be one of the first to try it. Every listed skill passes our trust checks below.

    Security scanned

    Passed our 8-point scan before listing

    Fresh listing

    Recently published to Agensi

    30-day refund

    Not a fit? Get your money back

    Trust & safety

    Security scanned

    Verified clean 7 days ago

    • Passed all security checks, Safe to install

    Listed7 days ago

    What's inside

    Frequently Asked Questions