Skip to main content
Design/design-handoff

Design Handoff

You need annotated design handoff documentation for engineering.

Use this when a design is approved and engineers need clear specifications to build it. Produces annotated handoff documentation covering behavior specs, responsive rules, interaction notes, accessibility requirements, and acceptance criteria mapping. Bridges the gap between "looks good in Figma" and "built correctly in code."

Related skills: Run /design-review before handoff to catch issues. Use /component-spec for reusable component documentation. Feed acceptance criteria into /story-write.

Process

Step 1: Gather inputs

Ask the user to provide:

  1. The approved design -- Figma link, screenshots, or prototype URL
  2. The story or feature spec -- acceptance criteria, PRD section, or feature description
  3. Design system -- component library or style guide the team uses (if any)
  4. Target platforms -- web (responsive?), iOS, Android, or all
  5. Breakpoints -- the team's responsive breakpoint definitions (or use defaults: 320, 768, 1024, 1440)

Step 2: Component inventory

List every distinct component visible in the design:

## Component Inventory -- {{feature name}}

| Component | Existing in DS? | Variant | Notes |
|-----------|----------------|---------|-------|
| (e.g., DataTable) | Yes / No / Modified | (e.g., sortable, paginated) | (customizations needed) |

Flag components that don't exist in the design system -- these need new component specs or custom implementation.

Step 3: Behavior specifications

For each interactive element, document what happens:

## Behavior Specs

### {{Component or interaction name}}

**Trigger:** (What the user does -- click, hover, focus, swipe, type)
**Action:** (What the system does in response)
**Feedback:** (What the user sees/hears -- animation, state change, message, sound)
**Duration:** (Animation timing if applicable -- e.g., 200ms ease-out)
**Reversibility:** (Can the user undo this? How?)

#### State transitions
| From state | User action | To state | Visual change |
|------------|------------|----------|---------------|
| Default | Hover | Hover | (describe change) |
| Default | Focus | Focused | (describe focus ring) |
| Default | Click | Active/Loading | (describe transition) |
| Loading | Complete | Success | (describe confirmation) |
| Loading | Failure | Error | (describe error display) |

Step 4: Responsive behavior

## Responsive Specifications

| Breakpoint | Layout change | Components affected | Notes |
|------------|--------------|--------------------|----- |
| < 320px | (minimum supported -- what breaks?) | | |
| 320-767px (mobile) | (layout description) | (components that change) | (stack, hide, reorder) |
| 768-1023px (tablet) | (layout description) | (components that change) | |
| 1024-1439px (desktop) | (layout description) | (components that change) | |
| 1440px+ (wide) | (layout description -- max-width or fluid?) | | |

Step 5: Accessibility notes

## Accessibility Requirements

| Element | Requirement | Implementation |
|---------|------------|----------------|
| (interactive element) | Keyboard operable | Tab order: (specify), Enter/Space to activate |
| (dynamic content) | Screen reader announcement | aria-live="polite" for (state change) |
| (form field) | Error association | aria-describedby linking to error message |
| (modal/dialog) | Focus management | Trap focus, return to trigger on close |
| (images) | Alt text | (specific alt text or aria-hidden if decorative) |

Step 6: Content and copy specifications

## Copy Specs

| Element | Copy | Character limit | Dynamic? | Truncation rule |
|---------|------|----------------|----------|-----------------|
| (heading) | (exact text) | (limit) | Yes/No | (ellipsis, wrap, tooltip) |
| (CTA) | (exact text) | (limit) | No | N/A |
| (error message) | (exact text) | (limit) | Yes -- (variable) | (rule) |

Step 7: Compile and present

Combine all sections into a single handoff document. Ask the user:

  • Are there interactions I missed?
  • Does the team need more detail on any component?
  • Should I generate stories from these specs using /story-write?
  • Any platform-specific considerations I should add?

Output location

Present the handoff document as formatted text in the conversation. For team use, export to Notion, Confluence, or attach to the relevant stories in the project tracker.

Example Output

Input

  • Approved design: Figma file for "Guest Checkout Flow" – 3-screen flow: Cart Summary → Payment Entry → Order Confirmation; includes mobile and desktop frames
  • Feature spec: PRD section 4.2 – "Allow unauthenticated users to complete purchase without creating an account; capture email for order tracking; AC includes inline validation on all payment fields, error recovery within same screen, and confirmation email trigger"
  • Design system: Meridian DS (internal React component library, v2.3) with tokens for color, typography, and spacing
  • Target platforms: Responsive web
  • Breakpoints: 375, 768, 1280, 1600

Output

Design Handoff — Guest Checkout Flow

Feature: Guest Checkout (PRD §4.2) | Design version: Approved 2024-01-18 | Prepared for: Sprint 11 Engineering


Component Inventory — Guest Checkout Flow

ComponentExisting in DS?VariantNotes
CartSummaryPanelYesCollapsibleCollapsed by default on mobile only
TextInputYesDefault, Error, DisabledNeeds new card-number variant with PAN masking
CreditCardInputNoCustom implementation needed; see component spec
SelectDropdownYesDefaultUsed for expiry month/year — chain-dependent behavior
PrimaryButtonYesDefault, Loading, DisabledLoading state not yet in DS v2.3 — needs addition
InlineErrorMessageYesField-levelReused as-is
OrderConfirmationBannerNoNew component; full-width, success green, animated entry
ProgressIndicatorModified3-step, no labels on mobileDS version has labels at all breakpoints — suppress below 768px

⚠️ 2 new components required: CreditCardInput, OrderConfirmationBanner — run /component-spec before sprint kickoff.


Behavior Specs

Payment Form — Field Validation

Trigger: User removes focus (blur) from any payment input field Action: Client-side validation runs against field ruleset Feedback: Red border + inline error message appears below field; field label turns error-red Duration: 150ms ease-in for border color transition; error message fades in at 200ms Reversibility: Error clears on next valid input (live validation after first blur)

State transitions

From stateUser actionTo stateVisual change
DefaultFocusFocusedBlue $color-focus-ring border (2px)
FocusedBlur (invalid)ErrorRed border, error message appears below
ErrorType valid inputCorrectingError message hides; border returns to focused blue
CorrectingBlur (valid)ValidGreen checkmark icon appears inline right
ValidBlurValidCheckmark persists; no border

Place Order Button

Trigger: User clicks "Place Order" Action: Form submission; payment tokenization API call fires Feedback: Button transitions to Loading state (spinner replaces label text); all fields disabled Duration: Spinner appears immediately; no timeout — resolves on API response Reversibility: No cancel affordance during loading (per design decision); back-button navigates to cart

State transitions

From stateUser actionTo stateVisual change
DefaultHoverHover$color-primary-dark background
DefaultClick (form valid)LoadingLabel replaces with 20px spinner; button width locked
DefaultClick (form invalid)Error summaryScroll-to-top; all invalid fields highlighted simultaneously
LoadingAPI successFull-page transition to Order Confirmation screen
LoadingAPI failureErrorToast notification (top-center, 4s); button resets to Default

Responsive Specifications

BreakpointLayout changeComponents affectedNotes
< 375pxNot officially supported; content scrollable, no horizontal scrollAllMinimum tested at 320px; no layout breakage expected
375–767px (mobile)Single column; CartSummaryPanel collapsed to accordion at top; payment form full-width belowCartSummaryPanel, ProgressIndicatorProgressIndicator shows dots only, no step labels
768–1279px (tablet)Two-column: cart left (40%), payment form right (60%); CartSummaryPanel always expandedCartSummaryPanel, ProgressIndicatorProgressIndicator shows step labels
1280–1599px (desktop)Two-column: cart left (35%), payment form right (65%); max-width 1200px centeredAllOrder summary sticky within left column on scroll
1600px+ (wide)Same as desktop; container max-width 1200px, outer background fills viewportLayout containerNo fluid stretching beyond 1200px

Accessibility Requirements

ElementRequirementImplementation
Card Number fieldInput purpose declaredautocomplete="cc-number"; inputmode="numeric"
Expiry selectsGrouped labelWrap month + year in <fieldset> with <legend>Expiration date</legend>
Inline error messagesProgrammatically associatedEach error <span> has unique ID; input has aria-describedby pointing to it
Place Order button (Loading)State communicatedaria-busy="true" during loading; aria-label="Processing your order" replaces visible label
Error summary (form-invalid submit)Focus managementFocus moves to summary container div[role="alert"] at top of form on invalid submit
Order Confirmation bannerAnnouncementaria-live="assertive" on banner container; fires once on mount
CartSummaryPanel accordionKeyboard operableToggle via Enter/Space; aria-expanded reflects state
Progress indicatorNon-interactive contextaria-label="Step 2 of 3: Payment" on container; steps not focusable

Copy Specs

ElementCopyCharacter limitDynamic?Truncation rule
Page heading"Secure Checkout"20NoN/A
Place Order CTA"Place Order — ${{total}}"28Yes — order totalNever truncate; reflow if needed
Card number placeholder"1234 5678 9012 3456"NoN/A
Generic API error toast"Payment couldn't be processed. Please try again or use a different card."80NoWrap to 2 lines max in toast
Confirmation heading"You're all set, {{first_name}}!"35Yes — first name or "there" if unavailableN/A
Confirmation subtext"Order #{{order_id}} confirmation sent to {{email}}"60Yes — order ID, emailTruncate email at 25 chars with tooltip showing full
Email field label"Email address (for order updates)"40NoN/A

Open Questions for Team

  • CreditCardInput masking: Should PAN be masked after 4 chars during entry (e.g., •••• •••• •••• 1234) or only after blur? Design shows post-blur masking — confirm with security team.
  • Express checkout options (Apple Pay / Google Pay) appear in Figma desktop frames but are not in PRD §4.2 scope — include or defer?
  • Loading button width: Figma shows button locks at current width during loading. Confirm this should be min-width locked via JS or CSS-only.

Next steps:

  • Run /component-spec for CreditCardInput and OrderConfirmationBanner
  • Run /story-write to generate tickets from the acceptance criteria above
  • Assign PrimaryButton Loading state addition to DS team before sprint start