一键导入
component-composition
// How to combine fundamental-styles components correctly - required wrappers, nesting rules, and common composition patterns
// How to combine fundamental-styles components correctly - required wrappers, nesting rules, and common composition patterns
| name | component-composition |
| description | How to combine fundamental-styles components correctly - required wrappers, nesting rules, and common composition patterns |
| metadata | {"tags":["composition","structure","nesting","wrappers","patterns"],"keywords":["composition","combine-components","nesting","wrapper-elements","form-item","input-group","popover","dialog-structure","required-wrappers","component-hierarchy","parent-child","structure"]} |
This skill explains how to combine fundamental-styles components correctly, including required wrappers, nesting rules, and common composition patterns.
Use this skill when:
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>
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>
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.
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 structureComponents: 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 correctlyComponents: 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.)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 barComponents: 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?
aria-label for accessibility (no visible text)aria-hidden="true").fd-button__text wrapper allows proper spacing between icon and textComponents: 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?
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?
:checked CSS selectors).fd-checkbox__checkmark is the custom styled checkbox.fd-checkbox__label-container and .fd-checkbox__text allow proper text wrappingComponents: 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 behaviorComponents: 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 textComponents: 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 actionsComponents: 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 itemsaria-current="page"--backslash, --double-greater-thanComponents: 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 colorrole="alert" announces to screen readers.fd-message-strip__text contains the message.fd-message-strip__close (optional) allows dismissalThese 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 |
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 |
<!-- Wrong -->
<button class="fd-button--emphasized">Save</button>
<!-- Correct -->
<button class="fd-button fd-button--emphasized">Save</button>
<!-- 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 - 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>
<!-- 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>
<!-- 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>
.fd-component__element means it must be inside .fd-component.fd-form-item.fd-input-group.fd-popover__body.fd-dialog__content.fd-bar__elementContainer → 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
| 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 |
Key Principles:
aria-labelfd-button__text wrapperWhen in doubt:
__ (double underscore) - indicates element must be inside its blockBEM (Block Element Modifier) methodology and naming conventions used in fundamental-styles with real examples
Fiori guidelines for action components Button, Product Switch, User Menu, Scrollbar
Fiori guidelines for data display components such as Table, List, Tree, Avatar, Calendar, Icons, etc
Fiori guidelines for feedback components such as Message Strip, Notification, Dialog, Progress Indicator, etc
Fiori guidelines for form controls and input components such as Checkbox, Input, Select, Textarea, Date Picker, etc
Fiori guidelines for layout structures such as Card, Panel, Dynamic Page, Flexible Column Layout, etc