
Ui Patterns
- 3 installs
- 230 repo stars
- Updated July 27, 2026
- whawkinsiv/solo-founder-superpowers
Helps with ai & agent building tasks.
About
ui-patterns is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- ui-patterns
- AI & Agent Building
- AI-coding skill
Ui Patterns by the numbers
- 3 all-time installs (skills.sh)
- Ranked #13,674 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/whawkinsiv/solo-founder-superpowers --skill ui-patternsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 3 |
|---|---|
| repo stars | ★ 230 |
| Last updated | July 27, 2026 |
| Repository | whawkinsiv/solo-founder-superpowers ↗ |
What it does
Helps with ai & agent building tasks.
Files
UI Patterns
This skill covers how to build pages and compose components for SaaS applications. It provides the structural decisions — which components to use, how to lay out pages, and how to handle every UI state. For visual styling (colors, typography, spacing systems), see the beautify skill. For user flow design, see ux-design. For accessibility requirements (semantic HTML, keyboard nav, ARIA), see ux-design/ACCESSIBILITY.md.
Component Library Selection
Default: shadcn/ui + Tailwind CSS. This is the correct choice for 90% of founder-built SaaS apps. shadcn/ui provides copy-paste components built on Radix primitives — you own the code, can customize freely, and avoid dependency lock-in.
Decision framework:
| Situation | Choice | Why |
|---|---|---|
| Public-facing SaaS product | shadcn/ui + Tailwind | Full design control, great defaults, accessible |
| Internal admin tool / back-office | Ant Design or MUI | Dense data display, built-in table/form patterns, faster to ship |
| Need one specific primitive (tooltip, dialog) | Radix directly | Skip the styling layer, use just the behavior |
| Existing MUI/Chakra codebase | Stay with current lib | Migration cost rarely worth it mid-project |
| React Native / mobile app | React Native Paper or Tamagui | Web component libs don't apply |
When using shadcn/ui: install components individually (npx shadcn-ui@latest add button), don't install everything upfront. Configure the components.json path aliases to match the project structure.
Page Composition Principles
Every SaaS page follows a predictable anatomy. Structure pages using these content zones:
Page header — Title, description, primary action button (top-right). Breadcrumbs above the title if nested. Height: 64-80px.
Primary content — The main purpose of the page. Takes 60-70% of width on desktop. This is the "spine" — the vertical column the user's eye follows.
Secondary content — Supporting information, filters, related items. Sidebar or below primary content. Takes 30-40% width or full-width below on mobile.
Actions area — Sticky bottom bar for forms, floating action button, or inline with content. Never hide primary actions in menus.
The page spine principle: every page has one dominant vertical column. On a dashboard, it's the metrics-to-activity flow. On a settings page, it's the form sections. On a list page, it's the table. Identify the spine and give it visual priority — other content supports it.
Standard page container: max-w-7xl mx-auto px-4 sm:px-6 lg:px-8. For content-heavy pages (docs, settings forms): max-w-3xl. For full-bleed layouts (dashboards): no max-width, use the sidebar to constrain.
The State Matrix
Every data-driven component must handle five states. Missing any one of these creates a broken experience. When building any component that displays dynamic data, implement all five:
Loading State
Use skeleton loaders that match the shape of the real content. Never use spinners for inline content — spinners are only for full-page loads or button actions.
- Tables: render 5-8 skeleton rows with column-width-matched blocks
- Cards: render the card shell with pulsing placeholder blocks
- Lists: render 3-5 skeleton items
- Use
animate-pulsewithbg-mutedblocks. Match heights and widths to actual content.
Empty State
The most neglected state. Every empty state needs: an icon or illustration, a headline explaining what will appear here, a description of how to populate it, and a primary CTA button.
No projects yet → "Create your first project" button
No search results → "Try different keywords" suggestion
No notifications → "You're all caught up" message (no CTA needed)Empty states are onboarding moments. Use them to teach users what to do next.
Error State
Display what went wrong in plain language and provide a retry action. Never show raw error codes or stack traces.
- Inline errors: red text below the failed component + retry button
- Full-page errors: centered message with retry and "go back" options
- Network errors: "Connection lost. Retrying..." with auto-retry logic
- Always preserve user input on form errors — never clear fields
Success State
The normal content display. This is what most developers build first (and sometimes only). Ensure it handles variable content lengths — long titles, missing optional fields, various image aspect ratios.
Overflow State
What happens when there's too much data:
- Tables: paginate at 10-25 rows, show total count, provide page size selector
- Lists: infinite scroll or "Load more" button. Show count: "Showing 20 of 148"
- Text: truncate with ellipsis and "Show more" expand option. Use
line-clamp-2orline-clamp-3 - Tags/badges: show first 3 + "+N more" pill
Responsive Strategy
Use mobile-first with Tailwind's breakpoint system. Default styles target mobile, then layer on complexity:
| Breakpoint | Width | Target |
|---|---|---|
| default | 0-639px | Mobile phones |
sm | 640px+ | Large phones, small tablets |
md | 768px+ | Tablets |
lg | 1024px+ | Small laptops, landscape tablets |
xl | 1280px+ | Desktops |
2xl | 1536px+ | Large monitors |
What changes at each breakpoint:
- Mobile (default): Single column. Bottom navigation. Cards stack vertically. Tables become card lists. Sidebar hidden behind hamburger. Modals go full-screen. Horizontal padding: 16px.
- sm: Minor adjustments. Two-column grids where appropriate. Slightly more padding.
- md: Sidebar can appear as collapsible overlay. Tables can show with horizontal scroll. Two or three column grids. Padding: 24px.
- lg: Full sidebar visible. Tables display normally. Multi-column layouts activate. Dashboard grids expand. Padding: 32px.
- xl+: Max-width containers center content. Extra whitespace on sides. Optional: wider sidebar, more columns.
Key responsive patterns:
| Desktop Pattern | Mobile Adaptation |
|---|---|
| Sidebar navigation | Bottom tab bar (5 items max) or hamburger menu |
| Data table | Stacked cards with key fields visible |
| Multi-column grid | Single column, stacked |
| Side-by-side panels | Tabbed or stacked sections |
| Hover tooltips | Tap-to-reveal or always-visible labels |
| Right-click context menu | Long-press menu or action row with icons |
| Modal dialog | Full-screen sheet sliding up from bottom |
For the sidebar-to-bottom-nav pattern: don't just hide the sidebar. Build a dedicated BottomNav component with 4-5 icon+label items. The items should map to the top-level sidebar sections, not be a 1:1 copy.
Dark Mode
When to implement: If building an MVP for validation, skip dark mode. If building a product users will spend hours in (dashboards, dev tools, writing apps), implement from day 1. Adding dark mode later requires touching every component — it's significantly cheaper to build it in from the start.
Implementation approach:
1. Define all colors as CSS custom properties in :root and .dark 2. Use Tailwind's dark: prefix for overrides 3. If using shadcn/ui, this is already set up — just configure the theme in globals.css 4. Toggle with a class on <html> element: document.documentElement.classList.toggle('dark') 5. Respect system preference with prefers-color-scheme media query, but let users override 6. Persist preference in localStorage
What changes beyond swapping background/text colors:
- Shadows: reduce opacity significantly or remove entirely. Dark surfaces don't cast visible shadows — use subtle borders or lighter surface colors to create elevation instead.
- Borders: increase visibility. Use
border-border(which maps to a lighter value in dark mode) rather than hardcoded colors. - Images and illustrations: reduce brightness slightly (
brightness-90). Avoid pure-white images on dark backgrounds. - Status colors: adjust saturation down and lightness up. A red that works on white won't work on dark gray — it needs to be softer.
- Hover states: use
hover:bg-mutedrather than hardcoded opacity changes. Light mode hovers darken, dark mode hovers lighten. - Focus rings: ensure visibility on dark backgrounds. Default blue rings work; custom colored rings may not.
Common Mistakes
| Mistake | What to do instead |
|---|---|
| Building a custom component library from scratch | Use shadcn/ui. Customize after shipping. |
| Only implementing the success state | Build all five states (loading, empty, error, success, overflow) for every data component |
| Using spinners everywhere | Skeleton loaders for inline content, spinners only for page-level or button loading |
| Fixed layouts that don't respond to screen size | Mobile-first responsive with Tailwind breakpoints, test at 375px width |
| Hiding critical actions in dropdown menus | Primary actions get dedicated buttons, only secondary/tertiary go in menus |
| Pixel-perfect custom breakpoints | Use Tailwind's standard breakpoints — they cover real devices |
| Dark mode as an afterthought | Decide at project start: implement now or explicitly defer. Don't half-implement. |
| Tables on mobile with horizontal scroll only | Transform tables into card layouts on mobile, showing key data points |
| Modals on top of modals | One modal at a time. Use sheets/slide-outs for secondary actions from a modal. |
| Inconsistent spacing between sections | Pick a page section gap and stick with it: space-y-8 for page sections, space-y-4 within sections |
Component Reference
Use this reference when selecting and implementing UI components. Specs assume shadcn/ui + Tailwind CSS unless noted otherwise.
---
Buttons
When to use: Every user-initiated action. The most important component to get right — inconsistent buttons undermine the entire interface.
Variants:
- Primary (
default): Solid background, high contrast. One per visible section — the main action. - Secondary (
secondary): Muted background. Supporting actions: "Cancel", "Back", "Save Draft". - Destructive (
destructive): Red background. Delete, remove, revoke actions. Always require confirmation for irreversible operations. - Ghost (
ghost): No background, shows on hover. Toolbar actions, icon buttons, compact UIs. - Outline (
outline): Border only. Alternative to secondary when you need visual distinction. - Link (
link): Looks like a text link. Inline actions within text or for navigation-like actions.
Key specs:
- Default height:
h-10(40px). Small:h-9(36px). Large:h-11(44px). Icon-only:h-10 w-10. - Min width for text buttons: 80px. Don't let buttons be narrower than their label needs.
- Border radius:
rounded-md(6px) — matches shadcn/ui defaults. - Disabled state:
opacity-50 pointer-events-none. Never hide buttons to indicate disabled — show them disabled with a tooltip explaining why.
Placement rules:
- Primary action goes rightmost in a button group. Destructive actions go leftmost, separated by a spacer.
- Form submit buttons: right-aligned. "Cancel" to the left of "Submit".
- Sticky bottom bar for long forms:
fixed bottom-0withborder-t bg-background p-4. - Icon + label is better than icon alone for primary actions. Icon-only is fine for well-known actions (edit pencil, delete trash, close X) in toolbars.
Loading state: Replace label with spinner + "Loading..." or action-specific text ("Saving..."). Keep button width stable — don't let it resize when text changes. Use disabled during loading.
---
Forms
When to use: Any data collection — settings, onboarding, creation flows, filters.
Layout:
- Single column for most forms. Two-column only when fields are logically paired (first name / last name, city / state).
- Label above input, not beside it. Label font:
text-sm font-medium. Color:text-foreground. - Optional field indicator: "(optional)" text after label. Don't mark required fields with asterisks — mark optional ones instead since most fields are required.
- Field spacing:
space-y-4between fields.space-y-6between field groups. - Group related fields with a section heading:
text-lg font-semiboldwithspace-y-4below. - Max form width:
max-w-lg(512px) for single-column forms. Inputs shouldn't stretch wider than ~500px.
Validation display:
- Inline validation below the field:
text-sm text-destructive mt-1. - Validate on blur for individual fields. Validate on submit for cross-field rules.
- Show error state on the input:
border-destructive focus-visible:ring-destructive. - Summary error banner at form top for server-side errors:
bg-destructive/10 border border-destructive/20 rounded-md p-4. - Success feedback: toast notification on save, or inline success message. Don't navigate away without confirmation the action succeeded.
Multi-step forms:
- Show step indicator: numbered steps with labels, current step highlighted.
- Persist data between steps — never lose input on back navigation.
- Validate each step before allowing progression.
- Show summary/review step before final submission for important flows (checkout, onboarding).
---
Inputs
When to use: Individual form controls for specific data types.
Text input:
- Height:
h-10(40px). Font:text-sm. Padding:px-3. - Placeholder text: describe format or example, not the label. "jane@example.com" not "Enter your email".
- Prefix/suffix support: icon or text inside the input (search magnifier, currency symbol). Use
relativepositioning withpl-10for icon offset.
Select:
- Same height as text inputs (40px). Use shadcn/ui
Selectfor custom styling, native<select>only for very simple cases. - For 5+ options or searchable lists: use a
Combobox(searchable select). Provide type-ahead filtering. - Multi-select: use
Comboboxwith pills/tags showing selected items.
Checkbox:
- Size:
h-4 w-4(16px). Label to the right withml-2. - Group spacing:
space-y-2between checkbox items. - Use checkboxes for multi-select from a short list (2-7 items). Beyond 7, use a multi-select combobox.
Toggle/Switch:
- Use for binary on/off settings where the effect is immediate (no form submit needed).
- Label to the left, toggle to the right. Description text below the label in
text-sm text-muted-foreground. - Size:
h-6 w-11default. Never use toggles inside forms that have a submit button — use checkboxes instead.
Textarea:
- Min height:
h-20(80px). Allow vertical resize:resize-y. - Show character count for limited fields:
text-xs text-muted-foregroundaligned right below the textarea. - For rich text: integrate a lightweight editor (Tiptap) rather than building custom formatting controls.
---
Cards
When to use: Grouping related content into a distinct visual unit.
Metric cards (dashboard KPIs):
- Fixed height per row for visual consistency. Typical: 120-140px.
- Structure: label (
text-sm text-muted-foreground) top, value (text-2xl font-bold) center, trend/change indicator bottom. - Grid:
grid grid-cols-2 lg:grid-cols-4 gap-4. - Trend indicator: green up arrow / red down arrow with percentage. Use
text-emerald-600andtext-red-600.
Content cards (list items, feed entries):
- Padding:
p-4orp-6. Border:border rounded-lg. - Structure: title, metadata line (date, author, status badge), truncated description, action links.
- Hover state if clickable:
hover:bg-muted/50 cursor-pointer transition-colors.
Interactive cards (selectable options, plan selection):
- Selected state:
border-primary ring-2 ring-primary/20. - Unselected:
border hover:border-primary/50 cursor-pointer. - Radio-card pattern: wrap in a group, one selectable at a time.
---
Tables (Data Tables)
When to use: Displaying structured data with multiple attributes per item. 5+ items with 3+ columns.
Key specs:
- Row height: 48-52px. Header:
text-sm font-medium text-muted-foreground. Cells:text-sm. - Horizontal padding:
px-4per cell. First and last cells getpx-6. - Striped rows optional:
even:bg-muted/50. Hover:hover:bg-muted. - Border: bottom border on rows
border-b. No vertical cell borders. - Column alignment: text left, numbers right, status/badges center, actions right.
Sorting: Clickable column headers with sort direction indicator (chevron up/down). Active sort column gets font-semibold. Default sort on the most useful column (usually date, descending).
Filtering: Filter bar above the table. Search input + dropdown filters. Show active filter count: "3 filters applied" with clear-all option.
Pagination: Below the table. Show: "Showing 1-10 of 148 results". Page size selector: 10, 25, 50. Previous/Next buttons + page numbers for large sets.
Row actions: Right-most column. For 1-2 actions: inline icon buttons. For 3+: kebab menu (three dots) opening a dropdown. Always include the most common action as the first dropdown item.
Empty state: Replace the entire table body with a centered empty state message. Don't show column headers with "No results" in a single cell.
Mobile: Transform to card layout. Each card shows the 3-4 most important fields. Tap to expand or navigate to detail view. Provide a way to access the full table view if needed.
---
Modals / Dialogs
When to use: Focused tasks that require attention before proceeding. Don't use for information that could be inline.
Standard dialog:
- Max width:
sm:max-w-lg(512px). For forms with more content:sm:max-w-xlorsm:max-w-2xl. - Structure: title, optional description, content, footer with actions.
- Footer buttons right-aligned: secondary action left, primary action right.
- Close: X button top-right, click outside to close, Escape key. Prevent close-on-outside-click for forms with unsaved changes.
Confirmation dialog:
- Use for destructive actions. Short and direct.
- Title: "Delete project?" not "Are you sure you want to delete this project?"
- Description: state the consequence. "This will permanently delete the project and all its data."
- Buttons: "Cancel" (secondary) and "Delete" (destructive). Match the button label to the action.
Form dialog:
- Same as standard with a form inside. Disable submit until valid. Show validation inline within the modal.
- If the form is longer than the viewport: use a sheet/slide-out instead of a modal.
Rules:
- Never stack modals. If a modal needs to trigger another action, use inline content or close the first modal.
- On mobile (<640px), modals become full-screen sheets sliding up from bottom.
- Always trap focus within the modal. Return focus to the trigger element on close.
---
Navigation
Sidebar navigation:
- Width: 240-280px. Collapsible to icon-only: 64px.
- Structure: logo/brand top, primary nav items, spacer/divider, secondary items (settings, help), user menu bottom.
- Active state:
bg-muted font-medium. Hover:hover:bg-muted/50. - Icon + label for each item. Icons: 20px (
h-5 w-5). Gap between icon and label:gap-3. - Nested sections: collapsible groups with chevron indicator. Indent children by
pl-8. - On
lgbreakpoint and above: always visible. Below: overlay with backdrop, triggered by hamburger button.
Top navigation:
- Height: 56-64px.
border-bbottom border. - Left: logo + primary nav links. Right: search, notifications, user avatar menu.
- Use for apps with 4-6 top-level sections max. Beyond that, use sidebar.
Breadcrumbs:
- Use on detail pages and nested views. Show full path from section root.
- Separator:
/or chevron. Truncate middle items with...if path exceeds 4 levels. - Current page (last item): not a link,
text-foreground. Previous items:text-muted-foreground hover:text-foreground.
Tabs:
- Use for switching views within a page without navigation. Content stays on the same page.
- Bottom border style: active tab has
border-b-2 border-primary. Inactive:text-muted-foreground hover:text-foreground. - Don't use more than 6-7 tabs. If more needed, use a select dropdown or sidebar sub-nav.
Command palette (Cmd+K):
- Implement for power users in apps with many sections/actions. Use
cmdklibrary. - Search across: pages, recent items, actions, settings. Group results by category.
- Not needed for MVP unless the app has 15+ distinct pages.
---
Toast / Notifications
When to use: Feedback after user actions. Non-blocking confirmations.
Positioning: Top-right corner of viewport. Stack multiple toasts vertically with gap-2. Use sonner or shadcn/ui toast.
Auto-dismiss timing:
- Success toasts: 3-4 seconds.
- Error toasts: persist until dismissed (user needs time to read and act).
- Info toasts: 5 seconds.
Variants:
- Success: checkmark icon, green accent.
- Error: X icon, red accent. Include retry action if applicable.
- Info: info icon, blue accent.
- Warning: alert triangle, yellow accent.
Specs: Max width: 356px. Padding: p-4. Border radius: rounded-lg. Shadow: shadow-lg.
Rules: Don't use toasts for validation errors (those go inline on the form). Don't use toasts for information the user hasn't requested — that's a notification badge or banner.
---
Badges
When to use: Status indicators, counts, labels, tags.
Status badges: Small pills showing state. Active (green), Pending (yellow), Inactive (gray), Error (red).
- Height:
h-5orh-6. Font:text-xs font-medium. Padding:px-2. - Use dot indicator before text for status: colored circle (
h-1.5 w-1.5 rounded-full) + label.
Count badges: Notification counts on nav items or icons.
- Small:
h-5 w-5 text-xsfor single/double digit. Position:absolute -top-1 -right-1. - Show "99+" for counts over 99.
Label badges: Categorization tags. Used in tables, cards, filters.
- Outline variant for low emphasis. Filled variant for high emphasis.
- Removable tags: add X button inside the badge.
---
Dropdowns
Action menus: Triggered by a button (kebab menu, "More" button). Contains action items with optional icons, dividers between groups, and destructive items at the bottom separated by a divider.
Selection menus: Triggered by a button showing current selection. Contains selectable items with checkmarks. Use DropdownMenuCheckboxItem or DropdownMenuRadioGroup from shadcn/ui.
Specs: Min width: 160px. Max width: 320px. Item height: 36px. Padding: px-2 py-1.5. Keyboard navigation: arrow keys, Enter to select, Escape to close.
---
Sheets / Slide-outs
When to use: Instead of modals when content is long or when the user needs to reference the page behind it. Detail views, long forms, multi-step processes.
- Slide in from the right on desktop. Width:
sm:max-w-md(448px) orsm:max-w-lg(512px). - On mobile: full-screen, slide up from bottom.
- Include close button and optional back navigation for multi-step.
- Backdrop: semi-transparent overlay on the page behind.
---
Skeleton Loaders
When to use: While async content is loading. Replace each content block with a skeleton that matches its dimensions.
Implementation:
- Use
bg-muted animate-pulse rounded-mdblocks. - Text lines: height
h-4, varying widths (100%, 80%, 60%) to look natural. - Avatars:
rounded-fullwith matching size. - Cards: full card outline with skeleton blocks inside.
- Tables: full row outlines with cell-width-matched blocks. Render 5-8 skeleton rows.
- Images: exact aspect ratio container with skeleton fill.
Rules: Never mix skeleton loaders with real content in the same container. Load the entire section or show the entire skeleton. Remove skeletons all at once, not one by one (avoids layout shifts).
Page Layouts
Layout templates for common SaaS pages. Each includes structure, key decisions, and common mistakes. All layouts assume a sidebar navigation context — adjust for top-nav by replacing the sidebar column with a full-width top bar.
---
Dashboard
Structure:
┌──────────────────────────────────────────────┐
│ Page Header: "Dashboard" [Actions ▾]│
├──────────────────────────────────────────────┤
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │Metric│ │Metric│ │Metric│ │Metric│ │
│ └──────┘ └──────┘ └──────┘ └──────┘ │
├──────────────────────────────────────────────┤
│ ┌─────────────────────┐ ┌────────────────┐ │
│ │ │ │ │ │
│ │ Primary Chart / │ │ Quick Actions │ │
│ │ Activity Feed │ │ / Recent List │ │
│ │ │ │ │ │
│ └─────────────────────┘ └────────────────┘ │
└──────────────────────────────────────────────┘Grid: max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 space-y-6. Metrics row: grid grid-cols-2 lg:grid-cols-4 gap-4. Content area: grid grid-cols-1 lg:grid-cols-3 gap-6 with primary content spanning lg:col-span-2.
Key decisions:
- Which 3-4 metrics to surface (pick the ones the user checks daily, not every metric available)
- Whether the primary content is a chart, activity feed, or recent items list
- What quick actions go in the secondary panel (create new, recent drafts, pinned items)
- Time range selector: default to last 7 or 30 days
Common mistakes:
- Cramming every metric onto the dashboard. Limit to 4-6 KPI cards max.
- No loading state for metrics — each card needs its own skeleton.
- Charts without axis labels or context for what the numbers mean.
- Dashboard that's just a list of links to other pages — it should surface data, not navigation.
---
List / Table Page
Structure:
┌──────────────────────────────────────────────┐
│ Page Header: "Projects" [+ New Project]│
├──────────────────────────────────────────────┤
│ [Search........] [Status ▾] [Type ▾] [⋯] │
├──────────────────────────────────────────────┤
│ ┌──────────────────────────────────────────┐│
│ │ Name │ Status │ Date │ Actions││
│ ├─────────────┼─────────┼─────────┼────────┤│
│ │ Project A │ Active │ Mar 1 │ ⋯ ││
│ │ Project B │ Draft │ Feb 28 │ ⋯ ││
│ │ Project C │ Active │ Feb 25 │ ⋯ ││
│ └──────────────────────────────────────────┘│
├──────────────────────────────────────────────┤
│ Showing 1-10 of 48 [< 1 2 3 4 5 >] │
└──────────────────────────────────────────────┘Grid: max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 space-y-4. Filter bar: flex flex-wrap items-center gap-3. Table: full width within container.
Key decisions:
- Default sort column and direction (usually created date, descending)
- Which columns to show by default (5-7 max, hide less important ones behind column toggle)
- Whether rows are clickable (navigate to detail) or have inline actions only
- Bulk actions: checkbox column + sticky action bar on selection
- Search scope: which fields does the search input cover
Common mistakes:
- No empty state when filters produce zero results (show "No results match your filters" with clear-filters CTA)
- Pagination resets when applying a filter — keep the user on page 1 when filters change
- Missing the primary create action in the page header
- Table not sortable on any column — at minimum support sorting on the primary identifier and date
---
Detail Page
Structure:
┌──────────────────────────────────────────────┐
│ ← Back to Projects │
├──────────────────────────────────────────────┤
│ Project Name │
│ Status badge · Created Mar 1 · Owner │
│ [Edit] [Delete] [⋯] │
├──────────────────────────────────────────────┤
│ ┌─────────────────────┐ ┌────────────────┐ │
│ │ │ │ Metadata │ │
│ │ Primary Content │ │ Created: ... │ │
│ │ (description, │ │ Updated: ... │ │
│ │ content sections, │ │ Owner: ... │ │
│ │ nested items) │ │ Tags: ... │ │
│ │ │ │ │ │
│ └─────────────────────┘ └────────────────┘ │
└──────────────────────────────────────────────┘Grid: max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 space-y-6. Two-column layout: grid grid-cols-1 lg:grid-cols-3 gap-8 with primary lg:col-span-2. On mobile, metadata section moves below primary content.
Key decisions:
- Whether metadata lives in a right sidebar or in a top header area
- Tab navigation for sub-sections (overview, activity, settings) vs single scrollable page
- Inline editing vs dedicated edit mode/page
- Related items: show inline list or link to filtered list page
Common mistakes:
- No breadcrumb or back link — user can't navigate up
- Action buttons (edit, delete) buried in a menu instead of visible in the header
- Detail page that's just a form — separate read view from edit view
- Long pages with no content anchoring — use sticky header or section tabs for pages with 5+ sections
---
Settings Page
Structure:
┌──────────────────────────────────────────────┐
│ Settings │
├────────────┬─────────────────────────────────┤
│ │ │
│ General │ General Settings │
│ Profile │ ───────────────── │
│ Team │ │
│ Billing │ [Form section 1] │
│ Security │ │
│ API │ [Form section 2] │
│ Danger │ │
│ │ [Form section 3] │
│ │ │
│ │ [Save Changes] │
└────────────┴─────────────────────────────────┘Grid: Full width, no max-width on outer container. Sidebar: w-48 lg:w-56 with space-y-1 for nav items. Content: max-w-2xl space-y-8. On mobile: sidebar becomes horizontal scrolling tabs at top or a select dropdown.
Key decisions:
- Whether each settings tab auto-saves or has a "Save" button (auto-save for toggles, save button for forms)
- Danger zone section at the bottom of the appropriate tab: red-bordered area with delete account, transfer ownership, etc.
- Whether team/billing/API get their own top-level pages or stay as settings tabs
Common mistakes:
- All settings on one endless scrolling page — split into logical tabs
- No confirmation when navigating away with unsaved changes
- Danger zone actions too easy to trigger — require typing confirmation for destructive actions
- Settings that take effect immediately but look like they need a save button (or vice versa)
---
Billing Page
Structure:
┌──────────────────────────────────────────────┐
│ Billing │
├──────────────────────────────────────────────┤
│ ┌──────────────────────────────────────────┐│
│ │ Current Plan: Pro [Change Plan]││
│ │ $49/mo · Renews Apr 10 ││
│ └──────────────────────────────────────────┘│
├──────────────────────────────────────────────┤
│ Usage This Period │
│ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ API │ │Users │ │Store │ │
│ │23/100│ │ 4/10 │ │2/5GB │ │
│ └──────┘ └──────┘ └──────┘ │
├──────────────────────────────────────────────┤
│ Payment Method │
│ Visa ****4242 Exp 12/27 [Update] │
├──────────────────────────────────────────────┤
│ Invoice History │
│ Mar 1 │ Pro Plan │ $49.00 │ [Download] │
│ Feb 1 │ Pro Plan │ $49.00 │ [Download] │
└──────────────────────────────────────────────┘Grid: max-w-3xl mx-auto px-4 sm:px-6 lg:px-8 space-y-8. Each section is a card with p-6 border rounded-lg. Usage meters: grid grid-cols-2 sm:grid-cols-3 gap-4.
Key decisions:
- Whether plan change is inline or opens a dedicated plan comparison page
- Usage display: progress bars vs numeric (progress bars for visual impact, use both)
- Invoice download format (PDF, accessible from within the app)
- Proration display when mid-cycle plan changes occur
Common mistakes:
- No visual warning when approaching usage limits — show yellow at 80%, red at 95%
- Can't download invoices — always provide downloadable PDFs
- Plan comparison not showing what the user currently has highlighted
- Cancellation flow that's a single button — provide offboarding flow with reason collection and save offers
---
Auth Pages (Login, Signup, Forgot Password)
Structure:
┌──────────────────────────────────────────────┐
│ │
│ ┌────────────────┐ │
│ │ Logo │ │
│ │ │ │
│ │ Welcome back │ │
│ │ │ │
│ │ [Email ] │ │
│ │ [Password ] │ │
│ │ │ │
│ │ [Sign In ] │ │
│ │ │ │
│ │ ── or ── │ │
│ │ [Google] │ │
│ │ │ │
│ │ Don't have │ │
│ │ an account? │ │
│ └────────────────┘ │
│ │
└──────────────────────────────────────────────┘Grid: Full screen height: min-h-screen flex items-center justify-center. Card: w-full max-w-sm p-8. For split layouts (marketing + form): grid grid-cols-1 lg:grid-cols-2 with full viewport height — left side is marketing content / hero image, right side is the form.
Key decisions:
- Social auth providers: Google is nearly universal. Add GitHub for dev tools, Apple for consumer apps.
- Split layout (marketing left, form right) vs centered card — split is better for conversion, centered is simpler to build
- Magic link vs password-based auth — magic link is easier to implement (no password reset flow)
- Whether signup requires email confirmation before access
Common mistakes:
- No password visibility toggle — always include the eye icon
- Forgot password link not visible — place it below the password field, right-aligned
- Social auth buttons styled inconsistently with each other
- Error messages that reveal whether an email exists in the system ("Invalid email or password" is correct, "No account found" leaks information)
- Sign up form asking for too many fields — email and password only, collect profile info after
---
Empty / Zero-Data State
Structure:
┌──────────────────────────────────────────────┐
│ Page Header: "Projects" [+ New Project]│
├──────────────────────────────────────────────┤
│ │
│ │
│ [ Illustration ] │
│ │
│ No projects yet │
│ │
│ Create your first project to get │
│ started organizing your work. │
│ │
│ [+ Create Project] │
│ │
│ │
└──────────────────────────────────────────────┘Grid: Center the empty state content: flex flex-col items-center justify-center py-16 text-center max-w-md mx-auto. Icon/illustration: h-12 w-12 text-muted-foreground mb-4. Headline: text-lg font-semibold. Description: text-sm text-muted-foreground mt-1 mb-6. CTA button: primary variant.
Key decisions:
- Whether to use an illustration, icon, or emoji for the visual element (icon is safest — illustrations require design work)
- CTA should match the primary page action (same as the header button)
- For filtered-empty states: "No results match your filters" + "Clear filters" button (different from zero-data)
Common mistakes:
- Generic "No data" text with no guidance on what to do next
- Missing the CTA button — the empty state should reduce friction, not just acknowledge emptiness
- Same empty state for "no data yet" and "no search results" — these need different messages
- Empty state that looks broken rather than intentional — add enough visual design to make it feel considered
---
Landing Page
Structure:
┌──────────────────────────────────────────────┐
│ Logo Features Pricing [Login] │
├──────────────────────────────────────────────┤
│ │
│ Headline (value proposition) │
│ Subheadline (one sentence) │
│ [Primary CTA] [Secondary CTA] │
│ Hero image / screenshot │
│ │
├──────────────────────────────────────────────┤
│ Social proof: logos, testimonial, stat │
├──────────────────────────────────────────────┤
│ Feature 1 │ Feature 2 │ Feature 3 │
│ icon + text │ icon + text │ icon + text │
├──────────────────────────────────────────────┤
│ Detailed feature section (alternating │
│ image left/right with text) │
├──────────────────────────────────────────────┤
│ Testimonials (2-3 quotes with photos) │
├──────────────────────────────────────────────┤
│ Pricing table (plans side by side) │
├──────────────────────────────────────────────┤
│ FAQ (accordion) │
├──────────────────────────────────────────────┤
│ Final CTA section (repeat primary CTA) │
├──────────────────────────────────────────────┤
│ Footer (links, legal, social) │
└──────────────────────────────────────────────┘Grid: Full width, no sidebar. Each section: max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16 sm:py-24. Hero: text-center or grid grid-cols-1 lg:grid-cols-2. Features grid: grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8. Section spacing: py-16 sm:py-24 alternating with optional bg-muted/50 backgrounds.
Key decisions:
- Hero layout: centered text with screenshot below, or split (text left, image right)
- Number of pricing tiers: 2-3 is optimal. Highlight the recommended plan.
- Social proof type: customer logos (B2B), testimonials (B2C), usage stats ("10,000+ users")
- Whether to include a live demo or video
Common mistakes:
- Headline describes the product instead of the outcome ("AI-powered project management" vs "Ship projects 2x faster")
- No CTA above the fold — primary CTA must be visible without scrolling
- Feature sections that are walls of text — use icons, visuals, and short descriptions
- Footer links to every possible page — keep it focused: product, company, legal, social
---
Blog / Content Page
Structure:
┌──────────────────────────────────────────────┐
│ Nav [Login] │
├──────────────────────────────────────────────┤
│ │
│ Category badge │
│ Article Title (h1) │
│ Author · Date · Read time │
│ │
│ ┌──────────────────────────────────────┐ │
│ │ Featured Image │ │
│ └──────────────────────────────────────┘ │
│ │
│ Article body content │
│ with headings, paragraphs, │
│ images, and code blocks │
│ │
│ ───────────────────────── │
│ Author bio │
│ Related articles (3 cards) │
│ │
└──────────────────────────────────────────────┘Grid: Content column: max-w-prose mx-auto px-4 sm:px-6 (65ch width — optimal reading width). If adding a table of contents sidebar: grid grid-cols-1 lg:grid-cols-[1fr_250px] max-w-5xl mx-auto gap-8 with sticky ToC.
Key decisions:
- Table of contents: yes for articles with 4+ headings, sticky sidebar on desktop, hidden or collapsible on mobile
- Related articles: manual selection or auto-generated by tag/category
- Whether to include reading progress bar at top
- Comments section: usually not worth building — link to Twitter/social discussion instead
Common mistakes:
- Content column too wide — text lines beyond 75 characters are hard to read
- No heading hierarchy — jumping from h1 to h4
- Images without proper aspect ratio containers causing layout shift
- Missing meta tags (og:title, og:description, og:image) for social sharing