- 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"]}
# 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:
```html
<!-- ❌ 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:
```html
<!-- ❌ 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:
```html
<!-- 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:**
```html
<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:**
```html
<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:**
```html
<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:**
```html
<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:**
```html
<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:**
```html
<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):**
```html
<button class="fd-button fd-button--transparent" aria-label="Edit">
<i class="sap-icon--edit"></i>
</button>
```
**Button with icon and text:**
```html
<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:**
```html
<!-- 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
Ver en GitHub