
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-compositionAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 9 |
|---|---|
| repo stars | ★ 231 |
| Last updated | August 3, 2026 |
| Repository | sap/fundamental-styles ↗ |
What it does
Helps with ai & agent building tasks.
Files
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-itemprovides 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-groupestablishes flex layout.fd-input-group__inputmodifier allows input to flex-grow.fd-input-group__addonpositions 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-popovermanages positioning and visibility.fd-popover__controlwraps the trigger element.fd-popover__bodycontains 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-dialogis the overlay container.fd-dialog__contentcontains all dialog content.fd-dialog__headeruses.fd-barfor consistent header layout.fd-dialog__footeruses.fd-barfor consistent footer layout.fd-bar__left/.fd-bar__rightposition elements.fd-bar__elementwraps 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-labelfor accessibility (no visible text) - Icon + text: text provides label, icon is decorative (
aria-hidden="true") .fd-button__textwrapper 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
:checkedCSS selectors) - Label wraps visual elements
.fd-checkbox__checkmarkis the custom styled checkbox.fd-checkbox__label-containerand.fd-checkbox__textallow 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-panelis the container.fd-panel__headercontains title and expand button.fd-panel__contentholds the body content--expandablemodifier 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-listestablishes the list container.fd-list__itemwraps each list entry.fd-list__contentgroups title and byline.fd-list__titleis the main text.fd-list__bylineis 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-cardis the container with shadow/border.fd-card__headergroups title and subtitle.fd-card__contentholds 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>witharia-label="Breadcrumb"for accessibility.fd-breadcrumbis the list container.fd-breadcrumb__itemwraps each crumb.fd-breadcrumb__linkfor 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-stripis the container with semantic colorrole="alert"announces to screen readers.fd-message-strip__textcontains the message.fd-message-strip__close(optional) allows dismissal
---
Required vs Optional Wrappers
Always Required
These wrappers are mandatory for the component to work:
| Component | Required Wrapper | Why |
|---|---|---|
| Form fields | .fd-form-item | Layout and spacing |
| Input group | .fd-input-group | Flex layout for addons |
| Popover | .fd-popover__control + .fd-popover__body | Positioning logic |
| Dialog | .fd-dialog__content | Centering and sizing |
| Table cells | .fd-table__row | Table structure |
| List items | .fd-list__item | List styling |
| Menu items | .fd-menu__item | Menu structure |
---
Optional Wrappers
These enhance functionality but aren't strictly required:
| Component | Optional Wrapper | Benefit |
|---|---|---|
| Panel | .fd-panel__header | Structured header area |
| Card | .fd-card__header | Organized title area |
| Button | .fd-button__text | Icon-text spacing |
| Checkbox | .fd-checkbox__label-container | Text 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
| Pattern | Structure |
|---|---|
| Form Field | fd-form-item > fd-form-label + fd-input + fd-form-message |
| Input Group | fd-input-group > fd-input-group__addon + fd-input + fd-input-group__addon |
| Button + Icon | fd-button > sap-icon + fd-button__text |
| Contextual Menu | fd-popover > fd-popover__control (button) + fd-popover__body (menu) |
| Dialog | fd-dialog > fd-dialog__content > header + body + footer |
| Card | fd-card > fd-card__header + fd-card__content + fd-card__footer |
| Panel | fd-panel > fd-panel__header + fd-panel__content |
| Table | fd-table > fd-table__header/body > fd-table__row > fd-table__cell |
| List | fd-list > fd-list__item > fd-list__content > fd-list__title |
| Checkbox | input.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