Building UI Components
Learn to design reusable UI components — card, modal, dropdown, and form components with clean APIs, proper state management, and AI-generated starting points.
Premium Course Content
This lesson is part of a premium course. Upgrade to Pro to unlock all premium courses and content.
- Access all premium courses
- 1000+ AI skill templates included
- New content added weekly
🔄 Recall Bridge: In the previous lesson, you mastered modern CSS architecture — Grid, Flexbox, Container Queries, and design tokens. Now let’s build the components that use those layout tools: cards, modals, dropdowns, and forms.
Components are the building blocks of every modern interface. Good components are reusable, accessible, and maintainable. AI generates visually accurate components quickly — but the keyboard interactions, accessibility attributes, and clean architecture need your expertise.
Component Design Principles
| Principle | What It Means | AI Usually Gets Wrong |
|---|---|---|
| Semantic HTML first | Use <dialog>, <details>, <button> | Uses <div> for everything |
| Flat structure | Max 2-3 levels of nesting | Deeply nested div soup |
| Design tokens | var(--space-4) not 16px | Hardcoded values everywhere |
| Keyboard accessible | All interactions work via keyboard | Mouse-only interactions |
| Focus visible | Clear focus indicator for keyboard users | No focus styles (or removed defaults) |
Component 1: Card
AI prompt for a production card:
Create a product card component with: (1) Semantic HTML — use
<article>as the root, (2) Image with properalttext andloading="lazy", (3) Title as<h3>(assuming cards are inside a section with<h2>), (4) Price with<data>element for machine-readable value, (5) ‘Add to Cart’ as a real<button>with visible focus states, (6) CSS using design tokens (I’ll provide token names), (7) Max 2 levels of nesting, 5 or fewer CSS classes, (8) Responsive: horizontal on wide containers, vertical on narrow (using Container Queries).
Component 2: Modal with <dialog>
AI prompt for an accessible modal:
Create a modal component using the native
<dialog>element: (1) Open with.showModal(), close with.close(), (2) Style the::backdropfor the overlay effect, (3) Close on Escape (built-in) and close on backdrop click (add handler), (4) Setaria-labelledbypointing to the modal’s heading, (5) Animate open/close with CSS transitions (use@starting-stylefor entry animation), (6) Focus the first interactive element on open. No extra JavaScript for focus trapping or Escape —<dialog>handles these natively.
Component 3: Dropdown Menu
AI prompt for an accessible dropdown:
Create a dropdown menu component with full keyboard support: (1) Trigger button with
aria-haspopup="true"andaria-expandedstate, (2) Menu withrole="menu", items withrole="menuitem", (3) Keyboard: Arrow Down/Up navigates items, Enter selects, Escape closes and returns focus to trigger, (4) Focus management: first item focused on open, focus trapped inside menu, (5) Click outside closes the menu, (6) CSS positioning: below the trigger, aligned to left edge, flip up if near bottom of viewport.
Component Keyboard Patterns
Every interactive component has expected keyboard behaviors:
| Component | Keyboard Pattern |
|---|---|
| Button | Enter/Space activates |
| Link | Enter activates |
| Dropdown | Arrow keys navigate, Enter selects, Escape closes |
| Modal | Escape closes, Tab cycles within, focus restored on close |
| Tabs | Arrow keys switch tabs, Tab moves to tab content |
| Accordion | Enter/Space toggles, Arrow keys between headers |
| Tooltip | Escape dismisses, focus/hover triggers |
✅ Quick Check: AI generates a clickable card where the entire card is wrapped in an
<a>tag. Is this a good pattern? (Answer: It works but has a problem: if the card contains multiple interactive elements (links, buttons), nesting them inside an<a>is invalid HTML and confuses screen readers. Better pattern: make the card’s primary action (e.g., title link) the<a>, then use CSS::afteron that link withposition: absoluteandinset: 0to make the entire card clickable. Other interactive elements inside the card (like an ‘Add to Cart’ button) remain independently focusable and clickable.)
Component Quality Checklist
Before shipping any component:
| Check | Method |
|---|---|
| Semantic HTML | View source — no unnecessary <div> wrappers |
| Keyboard navigation | Tab to the component, use Enter/Escape/Arrows |
| Focus visibility | Keyboard focus indicator is clearly visible |
| Screen reader | Test with VoiceOver (Mac) or NVDA (Windows) |
| Dark mode | Toggle system dark mode — check contrast |
| Responsive | Resize browser from 320px to 1920px |
| Loading state | What does the component look like while data loads? |
| Error state | What happens when something fails? |
| Empty state | What if there’s no data to display? |
Key Takeaways
- Prefer native HTML elements for interactive components:
<dialog>for modals,<details>/<summary>for accordions,<select>for dropdowns — they provide focus management, keyboard navigation, and screen reader support for free that custom<div>implementations must manually replicate (and usually miss) - Every interactive component has expected keyboard behaviors (ARIA patterns): dropdowns need arrow keys + Escape, modals need focus trapping + Escape, tabs need arrow keys between headers — AI generates mouse interactions but rarely implements these keyboard patterns
- Keep components flat (max 2-3 levels), use design tokens for all values, and limit to 3-5 clearly named CSS classes — deep nesting creates specificity wars, hardcoded values resist theming, and excessive classes increase cognitive load
Up Next
In the next lesson, you’ll make everything responsive — fluid typography, responsive images, and layouts that work from 320px phones to 4K displays.
Knowledge Check
Complete the quiz above first
Lesson completed!