BMAD-METHOD/docs/learn/module-12-functional-compon.../module-12-functional-compon...

14 KiB

Module 12: Functional Components

Time: 45 min | Agent: Freya | Phase: Design | Focus: UX


From Pages to Patterns

You've specified your pages. You documented every element, every state, every interaction.

And now something happens.

You're writing the specification for your third page... and you notice you're describing the same button again. Same states. Same behavior. Same validation pattern.

You're repeating yourself.

This is good. This means patterns have emerged.


What Are Functional Components?

Functional Components are the reusable building blocks of your interface.

Not colors. Not fonts. Not individual buttons.

Widgets with functionality.

NOT a component:           IS a component:
- Blue (#0066FF)           - Login Form (email, password, validation, submit)
- A single button          - Search Widget (input, suggestions, keyboard nav)
- 16px font size           - Comment Section (textarea, submit, character count)
- A margin value           - Task Card (checkbox, title, status, actions)

The difference: Functional Components are widgets that DO something - they have behavior, validation, state management, and user interactions.


Where You Are in WDS

Module 11: Conceptual Specifications
You wrote complete specifications for each page.
You documented every element in detail.
    ↓
Module 12: Functional Components  ← YOU ARE HERE
You identify what's repeated across pages.
You extract patterns into reusable components.
    ↓
Module 13: Visual Design
You apply visual style to your components.

This module is the bridge between detailed specifications and actual design system.


Why This Matters

When you specify pages across your app, you might write the same widget specification multiple times.

  • Homepage: "Weather widget: location, current temp, 5-day forecast, icons..."
  • Dashboard: "Weather widget: location, current temp, 5-day forecast, icons..."
  • User profile: "Weather widget: location, current temp, 5-day forecast, icons..."

That's inefficient. And when something changes (like adding hourly forecast), you update multiple places.

Instead, you extract the pattern:

  • Component: Weather Widget (defined once with all variants, data sources, states)
  • Page specs: "Use Weather Widget (compact variant)"

Define once. Reference everywhere. Change once. Update everywhere.

How Functional Components Connect Pages

      PAGE                    COMPONENT                    PAGE
┌──────────────────┐   ┌────────────────────┐   ┌──────────────────┐
│ P01: Homepage    │   │    WEATHER         │   │ P04: Dashboard   │
├──────────────────┤   │    WIDGET          │   ├──────────────────┤
│                  │   ├────────────────────┤   │                  │
│ Hero Section:    │   │ Data:              │   │ Sidebar:         │
│ - Component:     │◄──┤  • Current temp    │──►│ - Component:     │
│   WeatherWidget  │   │  • Conditions      │   │   WeatherWidget  │
│   (detailed)     │   │  • Location        │   │   (compact)      │
│ - Location:      │   │  • 5-day forecast  │   │ - Location:      │
│   User's city    │   │  • Hourly (opt.)   │   │   Auto-detect    │
│                  │   │                    │   │                  │
│                  │   │ Variants:          │   │                  │
│                  │   │  • compact         │   │                  │
│                  │   │  • detailed        │   │                  │
│                  │   │  • minimal         │   │                  │
│                  │   │                    │   │                  │
│                  │   │ States:            │   │                  │
│                  │   │  • loading         │   │                  │
│                  │   │  • loaded          │   │                  │
│                  │   │  • error           │   │                  │
│                  │   │  • refreshing      │   │                  │
└──────────────────┘   └────────────────────┘   └──────────────────┘
                               │
                               ▼
                         INTEGRATION
┌──────────────────────────────────────────────────────────────────┐
│ When homepage renders hero weather widget:                      │
│                                                                  │
│ 1. Reads: Component = WeatherWidget (detailed)                  │
│ 2. Looks up: WeatherWidget definition                           │
│ 3. Applies: Detailed variant (current + 5-day + hourly)         │
│ 4. Configures: Location = User's city                           │
│ 5. Inherits: All states, API integration, refresh logic         │
│ 6. Renders: Full weather display with forecast                  │
│                                                                  │
│ Result: Fully-functional weather widget with detailed view      │
└──────────────────────────────────────────────────────────────────┘

The structure: Multiple pages reference one widget. Change the widget definition, all instances update.


What Happens in This Module

You'll learn to:

  1. Identify patterns — Spot elements that appear 2+ times
  2. Decide what to extract — Not everything should be a component
  3. Document components — Create reusable definitions
  4. Update page specs — Reference components instead of repeating specs

By the end, your specifications become modular and maintainable.


Why Extract Components?

Consistency

Same component = same behavior everywhere.

Efficiency

Specify once, reference many times.

Maintainability

Change once, update everywhere.

Communication

Shared vocabulary with developers.


When to Extract

A pattern becomes a component when:

  • Appears 2+ times — More than once signals a pattern worth extracting
  • Consistent behavior — Same interactions everywhere
  • Worth centralizing — Benefits from single definition
  • Meaningful abstraction — Not just "box with text"

Component Anatomy

## Search Widget

### Description
Reusable search interface with input, suggestions, and keyboard navigation.
Used in header navigation, content libraries, and admin panels.

### Variants
| Variant | Use Case |
|---------|----------|
| Global | Site-wide search (all content types) |
| Scoped | Category-specific search |
| Instant | Real-time filtering (no submit) |

### States
- Empty (placeholder visible)
- Typing (suggestions appear)
- Loading (fetching results)
- Results (dropdown with matches)
- No Results (helpful message)
- Selected (item chosen from suggestions)
- Error (API failure)

### Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| variant | string | global | Search scope |
| placeholder | string | "Search..." | Input placeholder |
| minChars | number | 3 | Min chars before search |
| debounce | number | 300 | Delay before API call (ms) |
| maxResults | number | 10 | Max suggestions shown |
| onSelect | function | - | Callback when item selected |

### Usage Rules
- Search triggers after 3 characters typed
- Shows max 10 suggestions
- Escape key clears and closes
- Enter selects first result
- Click outside closes suggestions

### Accessibility
- Role: combobox
- Keyboard: Arrow keys navigate, Enter selects, Escape closes
- Screen reader announces result count
- aria-autocomplete="list"
- aria-expanded when suggestions visible

The Freya Method

Freya notices patterns as you specify:

"This is the third time you've specified a search widget with autocomplete. Should we extract it as a component?"

"Your comment forms have different validation rules — should we standardize?"

She helps you see what's becoming a pattern.


Component vs. Instance

Component: The widget definition (Search Widget) Instance: A specific usage (header-global-search)

In your specs:

  • Define components in your Design System
  • Reference components in your page specs
### Global Search
- Component: Search Widget (global)
- Placeholder: "Search documentation..."
- OnSelect: Navigate to selected page

How Integration Works

COMPONENT DEFINITION (C-UX-Scenarios/Functional-Components/search-widget.md)
┌────────────────────────────────────────────────────────────┐
│ Search Widget                                              │
├────────────────────────────────────────────────────────────┤
│ Variants:                                                  │
│   - global: All content types                              │
│   - scoped: Category-specific                              │
│   - instant: Real-time filtering                           │
│                                                            │
│ States:                                                    │
│   - empty, typing, loading, results, no-results, error     │
│                                                            │
│ Props:                                                     │
│   - variant (global|scoped|instant)                        │
│   - placeholder (string)                                   │
│   - minChars (number, default: 3)                          │
│   - onSelect (function)                                    │
└────────────────────────────────────────────────────────────┘
                           ↓
              INHERITS ALL PROPERTIES
                           ↓
┌────────────────────────────────────────────────────────────┐
│ INSTANCE (P01-homepage/specification.md)                   │
├────────────────────────────────────────────────────────────┤
│ header-global-search:                                      │
│   - Component: Search Widget        ← References def       │
│   - variant: global                 ← Selects variant      │
│   - placeholder: "Search docs..."   ← Instance-specific    │
│   - onSelect: Navigate to page      ← Instance-specific    │
│                                                            │
│ INHERITS FROM COMPONENT:                                   │
│   ✓ All global variant behavior (searches all content)    │
│   ✓ All states (typing, loading, results, errors...)      │
│   ✓ All accessibility (keyboard nav, screen reader...)    │
│   ✓ All usage rules (min 3 chars, debounce, max results)  │
└────────────────────────────────────────────────────────────┘

The power: Change the Search Widget once, all instances update everywhere.


Not Everything Is a Component

Some things are just styled elements:

Component Not a Component
Button A specific color
Card A specific margin
Input A single label
Modal A page layout

Components have behavior. Styles have appearance.


Output

Functional Components documented:

C-UX-Scenarios/
└── Functional-Components/
    ├── weather-widget.md
    ├── search-widget.md
    ├── login-form.md
    └── task-card.md

Location: Functional Components live in the UX Scenarios folder, separate from the Design System (which handles visual styling only).


Design System

Mode Component Handling
None Inline in page specs
Building Extract to C-UX-Scenarios/Functional-Components/
Library Reference external (shadcn, etc.)
Existing Import from previous project

Common Mistakes

Mistake Fix
Extracting too early Wait for 2+ usages
Too specific Keep components generic
Too generic Must have clear purpose
Missing states Document all states
Inconsistent naming Use clear conventions

Practice

Review your specifications from Module 11:

  1. Identify repeated patterns
  2. List elements that appear 2+ times
  3. Document one as a component
  4. Update specs to reference it

Lessons

Lesson 1: When Patterns Emerge

Identifying reusable elements across your specifications

Lesson 2: Functional Component Anatomy

How to document Functional Components completely


Tutorial

Tutorial 12: Extract Your Components

Hands-on guide to identifying and documenting reusable patterns


Next Module

Module 13: Design System →

Choose your design system approach.


Part of the WDS Course: From Designer to Linchpin