Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
sap avatar

Component Composition

  • 9 installs
  • 231 repo stars
  • Updated August 3, 2026
  • sap/fundamental-styles

Helps with ai & agent building tasks.

About

component-composition is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.

  • component-composition
  • AI & Agent Building
  • AI-coding skill

Component Composition by the numbers

  • 9 all-time installs (skills.sh)
  • +3 installs in the week ending Jul 19, 2026 (Skillselion tracking)
  • Ranked #12,152 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
  • Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/sap/fundamental-styles --skill component-composition

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs9
repo stars231
Last updatedAugust 3, 2026
Repositorysap/fundamental-styles

What it does

Helps with ai & agent building tasks.

Files

SKILL.mdMarkdownGitHub ↗

Component Composition

This skill explains how to combine fundamental-styles components correctly, including required wrappers, nesting rules, and common composition patterns.

When to Use This Skill

Use this skill when:

  • The user asks "How do I combine [component A] with [component B]?"
  • The user needs to understand component structure and nesting
  • The user asks about required wrapper elements
  • The user's component isn't working due to incorrect composition
  • The user asks "Why do I need this wrapper element?"
  • The user wants to understand parent-child relationships between components

---

Core Composition Principles

1. Always Include Required Wrappers

Many components require specific wrapper elements to function correctly:

<!-- ❌ Wrong - missing wrapper -->
<label class="fd-form-label">Name</label>
<input class="fd-input" />

<!-- ✅ Correct - wrapped in form-item -->
<div class="fd-form-item">
    <label class="fd-form-label">Name</label>
    <input class="fd-input" />
</div>

---

2. Follow the Component Hierarchy

Each component has a specific structure - don't skip levels:

<!-- ❌ Wrong - missing table__row -->
<table class="fd-table">
    <tbody class="fd-table__body">
        <td class="fd-table__cell">Data</td>
    </tbody>
</table>

<!-- ✅ Correct - proper hierarchy -->
<table class="fd-table">
    <tbody class="fd-table__body">
        <tr class="fd-table__row">
            <td class="fd-table__cell">Data</td>
        </tr>
    </tbody>
</table>

---

3. Understand Block Independence

Each component (block) is independent. Nesting blocks doesn't create a hierarchy:

<!-- Both are independent blocks, not parent-child -->
<button class="fd-button">
    <i class="sap-icon--edit"></i>
</button>

.fd-button and .sap-icon--edit are two separate blocks living together, not a parent-child BEM relationship.

---

Common Composition Patterns

Form Item Composition

Components: Form Item + Form Label + Input + Form Message

Structure:

<div class="fd-form-item">
    <label class="fd-form-label" for="input-id">Label Text</label>
    <input class="fd-input" id="input-id" type="text" />
</div>

With validation message:

<div class="fd-form-item">
    <label class="fd-form-label fd-form-label--required" for="email">Email</label>
    <input class="fd-input is-error" id="email" type="email" aria-describedby="email-error" />
    <span class="fd-form-message fd-form-message--error" id="email-error"> Invalid email format </span>
</div>

Why these wrappers?

  • .fd-form-item provides spacing and layout structure
  • Keeps label, input, and message grouped together
  • Enables proper responsive behavior

---

Input Group Composition

Components: Input Group + Input + Button

Basic input group:

<div class="fd-input-group">
    <input class="fd-input fd-input-group__input" type="text" placeholder="Search" />
    <span class="fd-input-group__addon fd-input-group__addon--button">
        <button class="fd-button fd-button--transparent" aria-label="Search">
            <i class="sap-icon--search"></i>
        </button>
    </span>
</div>

With text addon:

<div class="fd-input-group">
    <span class="fd-input-group__addon fd-input-group__addon--before">$</span>
    <input class="fd-input fd-input-group__input" type="number" />
    <span class="fd-input-group__addon fd-input-group__addon--after">.00</span>
</div>

Why these wrappers?

  • .fd-input-group establishes flex layout
  • .fd-input-group__input modifier allows input to flex-grow
  • .fd-input-group__addon positions addons correctly
  • Ensures seamless visual connection between elements

---

Popover + Menu (Contextual Menu)

Components: Popover (container) + Button (control/trigger) + Menu (body content)

Structure:

<div class="fd-popover">
    <!-- Control/Trigger -->
    <div class="fd-popover__control">
        <button class="fd-button fd-button--transparent" aria-label="More actions">
            <i class="sap-icon--overflow"></i>
        </button>
    </div>

    <!-- Body with Menu -->
    <div class="fd-popover__body" aria-hidden="true">
        <nav class="fd-menu">
            <ul class="fd-menu__list">
                <li class="fd-menu__item">
                    <a class="fd-menu__link" href="#">
                        <span class="fd-menu__title">Edit</span>
                    </a>
                </li>
                <li class="fd-menu__item">
                    <a class="fd-menu__link" href="#">
                        <span class="fd-menu__title">Delete</span>
                    </a>
                </li>
            </ul>
        </nav>
    </div>
</div>

Why this composition?

  • .fd-popover manages positioning and visibility
  • .fd-popover__control wraps the trigger element
  • .fd-popover__body contains the content (menu, form, etc.)
  • Separates trigger from content for proper overlay behavior

---

Dialog Composition

Components: Dialog + Bar (header/footer) + Title + Button

Structure:

<div class="fd-dialog fd-dialog--active">
    <div class="fd-dialog__content">
        <!-- Header -->
        <header class="fd-dialog__header fd-bar fd-bar--header">
            <div class="fd-bar__left">
                <div class="fd-bar__element">
                    <h3 class="fd-title fd-title--h5">Dialog Title</h3>
                </div>
            </div>
            <div class="fd-bar__right">
                <div class="fd-bar__element">
                    <button class="fd-button fd-button--transparent" aria-label="Close">
                        <i class="sap-icon--decline"></i>
                    </button>
                </div>
            </div>
        </header>

        <!-- Body -->
        <div class="fd-dialog__body">
            <p>Dialog content goes here</p>
        </div>

        <!-- Footer -->
        <footer class="fd-dialog__footer fd-bar fd-bar--footer">
            <div class="fd-bar__right">
                <div class="fd-bar__element">
                    <button class="fd-button fd-button--emphasized">Save</button>
                </div>
                <div class="fd-bar__element">
                    <button class="fd-button fd-button--transparent">Cancel</button>
                </div>
            </div>
        </footer>
    </div>
</div>

Why this structure?

  • .fd-dialog is the overlay container
  • .fd-dialog__content contains all dialog content
  • .fd-dialog__header uses .fd-bar for consistent header layout
  • .fd-dialog__footer uses .fd-bar for consistent footer layout
  • .fd-bar__left / .fd-bar__right position elements
  • .fd-bar__element wraps each item in the bar

---

Button + Icon + Text

Components: Button + Icon + Button Text

Icon-only button (no text):

<button class="fd-button fd-button--transparent" aria-label="Edit">
    <i class="sap-icon--edit"></i>
</button>

Button with icon and text:

<button class="fd-button fd-button--emphasized">
    <i class="sap-icon--save" role="presentation" aria-hidden="true"></i>
    <span class="fd-button__text">Save</span>
</button>

Why the difference?

  • Icon-only: needs aria-label for accessibility (no visible text)
  • Icon + text: text provides label, icon is decorative (aria-hidden="true")
  • .fd-button__text wrapper allows proper spacing between icon and text

---

Table + Toolbar + Pagination

Components: Toolbar + Table + Pagination

Structure:

<!-- Toolbar above table -->
<div class="fd-toolbar">
    <div class="fd-toolbar__group">
        <button class="fd-button fd-button--transparent fd-button--compact">
            <i class="sap-icon--add" role="presentation" aria-hidden="true"></i>
            <span class="fd-button__text">Add</span>
        </button>
    </div>
</div>

<!-- Table -->
<table class="fd-table">
    <thead class="fd-table__header">
        <tr class="fd-table__row">
            <th class="fd-table__cell">Name</th>
            <th class="fd-table__cell">Status</th>
        </tr>
    </thead>
    <tbody class="fd-table__body">
        <tr class="fd-table__row">
            <td class="fd-table__cell">John Doe</td>
            <td class="fd-table__cell">Active</td>
        </tr>
    </tbody>
</table>

<!-- Pagination below table -->
<div class="fd-pagination">
    <nav class="fd-pagination__nav">
        <a href="#" class="fd-button fd-button--transparent" aria-label="Previous">
            <i class="sap-icon--navigation-left-arrow"></i>
        </a>
        <a href="#" class="fd-button fd-button--transparent">1</a>
        <a href="#" class="fd-button fd-button--transparent is-active" aria-current="page">2</a>
        <a href="#" class="fd-button fd-button--transparent">3</a>
        <a href="#" class="fd-button fd-button--transparent" aria-label="Next">
            <i class="sap-icon--navigation-right-arrow"></i>
        </a>
    </nav>
</div>

Why separate components?

  • Each is an independent block
  • Toolbar provides actions for the table
  • Table displays data
  • Pagination controls data navigation
  • Keeping them separate allows flexible positioning

---

Checkbox/Radio + Label

Components: Checkbox/Radio + Label

Checkbox structure:

<input type="checkbox" class="fd-checkbox" id="checkbox-1" />
<label class="fd-checkbox__label" for="checkbox-1">
    <span class="fd-checkbox__checkmark" aria-hidden="true"></span>
    <span class="fd-checkbox__label-container">
        <span class="fd-checkbox__text">Accept terms and conditions</span>
    </span>
</label>

Radio structure:

<input type="radio" class="fd-radio" id="radio-1" name="group" />
<label class="fd-radio__label" for="radio-1">
    <span class="fd-radio__text">Option 1</span>
</label>

Why this structure?

  • Input comes first (required for :checked CSS selectors)
  • Label wraps visual elements
  • .fd-checkbox__checkmark is the custom styled checkbox
  • .fd-checkbox__label-container and .fd-checkbox__text allow proper text wrapping

---

Panel + Content

Components: Panel + Panel Header + Panel Body

Structure:

<div class="fd-panel">
    <div class="fd-panel__header">
        <h3 class="fd-panel__title">Panel Title</h3>
        <button class="fd-button fd-button--transparent fd-panel__button" aria-label="Expand">
            <i class="sap-icon--slim-arrow-down"></i>
        </button>
    </div>
    <div class="fd-panel__content">
        <p>Panel content goes here</p>
    </div>
</div>

Expandable panel:

<div class="fd-panel fd-panel--expandable">
    <div class="fd-panel__header">
        <h3 class="fd-panel__title">Expandable Panel</h3>
        <button class="fd-button fd-button--transparent fd-panel__button" aria-label="Expand" aria-expanded="false">
            <i class="sap-icon--slim-arrow-right"></i>
        </button>
    </div>
    <div class="fd-panel__content" hidden>
        <p>Collapsible content</p>
    </div>
</div>

Why this structure?

  • .fd-panel is the container
  • .fd-panel__header contains title and expand button
  • .fd-panel__content holds the body content
  • --expandable modifier enables collapse/expand behavior

---

List + List Items

Components: List + List Item + List Title + List Text

Basic list:

<ul class="fd-list">
    <li class="fd-list__item">
        <span class="fd-list__title">Item 1</span>
    </li>
    <li class="fd-list__item">
        <span class="fd-list__title">Item 2</span>
    </li>
</ul>

List with byline (additional text):

<ul class="fd-list fd-list--byline">
    <li class="fd-list__item">
        <div class="fd-list__content">
            <div class="fd-list__title">John Doe</div>
            <div class="fd-list__byline">Software Engineer</div>
        </div>
    </li>
    <li class="fd-list__item">
        <div class="fd-list__content">
            <div class="fd-list__title">Jane Smith</div>
            <div class="fd-list__byline">Product Manager</div>
        </div>
    </li>
</ul>

Why these elements?

  • .fd-list establishes the list container
  • .fd-list__item wraps each list entry
  • .fd-list__content groups title and byline
  • .fd-list__title is the main text
  • .fd-list__byline is secondary/supporting text

---

Card Structure

Components: Card + Card Header + Card Content

Structure:

<div class="fd-card">
    <div class="fd-card__header">
        <h3 class="fd-card__title">Card Title</h3>
        <div class="fd-card__subtitle">Subtitle text</div>
    </div>
    <div class="fd-card__content">
        <p>Card content goes here</p>
    </div>
    <div class="fd-card__footer">
        <button class="fd-button fd-button--transparent">Action</button>
    </div>
</div>

Why this structure?

  • .fd-card is the container with shadow/border
  • .fd-card__header groups title and subtitle
  • .fd-card__content holds the main content
  • .fd-card__footer (optional) contains actions

---

Breadcrumb Composition

Components: Breadcrumb + Breadcrumb Item + Link

Structure:

<nav aria-label="Breadcrumb">
    <ul class="fd-breadcrumb">
        <li class="fd-breadcrumb__item">
            <a class="fd-breadcrumb__link" href="#">Home</a>
        </li>
        <li class="fd-breadcrumb__item">
            <a class="fd-breadcrumb__link" href="#">Products</a>
        </li>
        <li class="fd-breadcrumb__item">
            <a class="fd-breadcrumb__link" aria-current="page">Details</a>
        </li>
    </ul>
</nav>

With custom separator:

<ul class="fd-breadcrumb fd-breadcrumb--backslash">
    <li class="fd-breadcrumb__item">
        <a class="fd-breadcrumb__link" href="#">Home</a>
    </li>
    <li class="fd-breadcrumb__item">
        <a class="fd-breadcrumb__link" href="#">Products</a>
    </li>
    <li class="fd-breadcrumb__item">
        <span class="fd-breadcrumb__link" aria-current="page">Details</span>
    </li>
</ul>

Why this structure?

  • <nav> with aria-label="Breadcrumb" for accessibility
  • .fd-breadcrumb is the list container
  • .fd-breadcrumb__item wraps each crumb
  • .fd-breadcrumb__link for clickable items
  • Current page uses aria-current="page"
  • Separator modifiers: --backslash, --double-greater-than

---

Message Strip Composition

Components: Message Strip + Message Strip Text

Basic message:

<div class="fd-message-strip fd-message-strip--error" role="alert">
    <p class="fd-message-strip__text">Error: Unable to save changes</p>
</div>

With dismiss button:

<div class="fd-message-strip fd-message-strip--warning" role="alert">
    <p class="fd-message-strip__text">Warning: Changes are not saved</p>
    <button class="fd-button fd-button--transparent fd-message-strip__close" aria-label="Close">
        <i class="sap-icon--decline"></i>
    </button>
</div>

Why this structure?

  • .fd-message-strip is the container with semantic color
  • role="alert" announces to screen readers
  • .fd-message-strip__text contains the message
  • .fd-message-strip__close (optional) allows dismissal

---

Required vs Optional Wrappers

Always Required

These wrappers are mandatory for the component to work:

ComponentRequired WrapperWhy
Form fields.fd-form-itemLayout and spacing
Input group.fd-input-groupFlex layout for addons
Popover.fd-popover__control + .fd-popover__bodyPositioning logic
Dialog.fd-dialog__contentCentering and sizing
Table cells.fd-table__rowTable structure
List items.fd-list__itemList styling
Menu items.fd-menu__itemMenu structure

---

Optional Wrappers

These enhance functionality but aren't strictly required:

ComponentOptional WrapperBenefit
Panel.fd-panel__headerStructured header area
Card.fd-card__headerOrganized title area
Button.fd-button__textIcon-text spacing
Checkbox.fd-checkbox__label-containerText wrapping

---

Common Composition Mistakes

❌ Missing Base Class with Modifier

<!-- Wrong -->
<button class="fd-button--emphasized">Save</button>

<!-- Correct -->
<button class="fd-button fd-button--emphasized">Save</button>

---

❌ Skipping Required Wrapper

<!-- Wrong - missing form-item wrapper -->
<label class="fd-form-label">Name</label>
<input class="fd-input" />

<!-- Correct -->
<div class="fd-form-item">
    <label class="fd-form-label">Name</label>
    <input class="fd-input" />
</div>

---

❌ Wrong Element Order

<!-- Wrong - label before input breaks :checked selector -->
<label class="fd-checkbox__label" for="cb1">
    <span class="fd-checkbox__checkmark"></span>
    <span class="fd-checkbox__text">Option</span>
</label>
<input type="checkbox" class="fd-checkbox" id="cb1" />

<!-- Correct - input first -->
<input type="checkbox" class="fd-checkbox" id="cb1" />
<label class="fd-checkbox__label" for="cb1">
    <span class="fd-checkbox__checkmark"></span>
    <span class="fd-checkbox__text">Option</span>
</label>

---

❌ Missing Icon Accessibility

<!-- Wrong - icon-only button without label -->
<button class="fd-button">
    <i class="sap-icon--edit"></i>
</button>

<!-- Correct - with aria-label -->
<button class="fd-button" aria-label="Edit">
    <i class="sap-icon--edit"></i>
</button>

---

❌ Incorrect Hierarchy

<!-- Wrong - missing table__row -->
<table class="fd-table">
    <tbody class="fd-table__body">
        <td class="fd-table__cell">Data</td>
    </tbody>
</table>

<!-- Correct -->
<table class="fd-table">
    <tbody class="fd-table__body">
        <tr class="fd-table__row">
            <td class="fd-table__cell">Data</td>
        </tr>
    </tbody>
</table>

---

Composition Decision Tree

"Do I need a wrapper?"

1. Check the component documentation - Does it show a wrapper? 2. Look for double underscores - .fd-component__element means it must be inside .fd-component 3. Test without it - If layout breaks, you need it 4. Common wrappers:

  • Form fields → .fd-form-item
  • Input with addons → .fd-input-group
  • Popover content → .fd-popover__body
  • Dialog content → .fd-dialog__content
  • Bar items → .fd-bar__element

---

"Which component goes inside which?"

Container → Content relationship:

fd-form-item
  └─ fd-form-label
  └─ fd-input
  └─ fd-form-message

fd-input-group
  └─ fd-input-group__addon
  └─ fd-input-group__input (with fd-input)
  └─ fd-input-group__addon

fd-popover
  └─ fd-popover__control
      └─ fd-button (trigger)
  └─ fd-popover__body
      └─ fd-menu (or any content)

fd-dialog
  └─ fd-dialog__content
      └─ fd-dialog__header (fd-bar)
      └─ fd-dialog__body
      └─ fd-dialog__footer (fd-bar)

fd-card
  └─ fd-card__header
  └─ fd-card__content
  └─ fd-card__footer

fd-panel
  └─ fd-panel__header
  └─ fd-panel__content

fd-table
  └─ fd-table__header
      └─ fd-table__row
          └─ fd-table__cell
  └─ fd-table__body
      └─ fd-table__row
          └─ fd-table__cell

---

Quick Reference: Common Compositions

PatternStructure
Form Fieldfd-form-item > fd-form-label + fd-input + fd-form-message
Input Groupfd-input-group > fd-input-group__addon + fd-input + fd-input-group__addon
Button + Iconfd-button > sap-icon + fd-button__text
Contextual Menufd-popover > fd-popover__control (button) + fd-popover__body (menu)
Dialogfd-dialog > fd-dialog__content > header + body + footer
Cardfd-card > fd-card__header + fd-card__content + fd-card__footer
Panelfd-panel > fd-panel__header + fd-panel__content
Tablefd-table > fd-table__header/body > fd-table__row > fd-table__cell
Listfd-list > fd-list__item > fd-list__content > fd-list__title
Checkboxinput.fd-checkbox + label.fd-checkbox__label > checkmark + text

---

Summary

Key Principles:

1. Always include required wrappers (form-item, input-group, popover\_\_body, etc.) 2. Follow the documented component hierarchy (don't skip levels) 3. Icon-only buttons need aria-label 4. Icon + text buttons need fd-button__text wrapper 5. Always include base class before modifiers 6. Check component documentation for correct structure

When in doubt:

  • Look for __ (double underscore) - indicates element must be inside its block
  • Check if layout/spacing looks wrong - you probably need a wrapper
  • Reference the component guidance or layout patterns skills for examples

Related skills

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.