The last mile

AI generates the 80%. The last 20% is what users remember.

AI generates the 80%. The last 20% is what users remember. Every AI-generated page is missing the same things: favicons, OG images, keyboard shortcuts, loading skeletons, error states with personality, hover transitions. The things that only exist because someone thought about them. ## Metadata and identity | Detail | Why it matters | | ------------------------------------ | ------------------------------------------------------------- | | Favicon (light + dark mode SVG) | The tab bar is the most persistent brand surface | | Open Graph image | How the product looks when shared on Slack, Twitter, LinkedIn | | `<title>` that's useful, not generic | "Dashboard | Acme" beats "Acme" beats "Home" | | `theme-color` meta tag | Tints the browser chrome on mobile | | `apple-touch-icon` | Home screen icon on iOS | ## Interaction states AI generates the default state. Not the other four: | State | What to check | | -------- | ----------------------------------------------------------------- | | Hover | Does every interactive element change on hover? | | Focus | Is the focus ring visible, styled, and following `border-radius`? | | Active | Does the button depress or shift on click? | | Disabled | Is it visually distinct from enabled? Is the cursor set? | | Loading | Skeleton, spinner, or progress bar. Not a blank space | ## Keyboard support | Pattern | Minimum bar | | ---------- | ----------------------------------------------- | | Tab order | Follows visual order, skips decorative elements | | Escape | Closes modals, drawers, popovers | | Enter | Submits forms, activates focused buttons | | Arrow keys | Navigates tabs, menus, radio groups | Can the primary task be completed without touching the mouse? If not, that's a bug. ## Responsive refinement AI generates for one breakpoint. Check three: | Breakpoint | Common AI failures | | -------------- | ------------------------------------------------------------ | | Mobile (375px) | Text too small, touch targets too close, horizontal overflow | | Tablet (768px) | Awkward column count (3 → 2 looks weird at some widths) | | Wide (1440px+) | Content stretches, `line-length` exceeds 80ch | ## Micro-transitions Elements that benefit from a transition: ```css .button { transition: background-color 150ms ease-out; } .card { transition: box-shadow 200ms ease-out; } .link { transition: color 100ms ease-out; } .input { transition: border-color 150ms ease-out; } .accordion { transition: grid-template-rows 200ms ease-out; } ``` Do _not_ animate: text colour in body copy, layout shifts, scrollbar appearance. ## The 15-minute polish pass After the three-pass edit from the previous lesson, run this final checklist. 1. **Tab through the page.** Fix broken focus order. Style the focus ring. 2. **Resize to 375px.** Fix overflow, touch targets, text size. 3. **Hover every interactive element.** Add missing hover states. 4. **Check the tab title and favicon.** Replace defaults. 5. **Inspect the `<head>`.** Add `og:image`, `theme-color`, `apple-touch-icon`. 6. **Trigger every error state.** Submit empty forms. Disconnect wifi. Load with slow 3G. 7. **Test with keyboard only.** Escape closes overlays. Enter submits. ## Common mistakes - **Treating polish as optional.** It's not the cherry on top. It's the structural integrity of the user's trust. - **Polishing visuals before fixing interactions.** A beautiful button that isn't keyboard-accessible is worse than an ugly one that is. - **Adding polish in the prompt.** "Add hover states to all buttons" works for buttons. It misses links, cards, nav items, and every other interactive element. The last mile is manual.