mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
docs(core): add guidelines for naming CSS shadow parts (#30931)
Adds guidelines document establishing standardized naming conventions for CSS Shadow Parts in Ionic Framework components. --------- Co-authored-by: Brandy Smith <6577830+brandyscarney@users.noreply.github.com>
This commit is contained in:
@@ -23,6 +23,7 @@ It is based on <a href="https://www.webcomponents.org/introduction">Web Componen
|
||||
| [Contributing](./CONTRIBUTING.md) | How to contribute including creating pull requests, commit message guidelines, and more. |
|
||||
| [Component Guide](./component-guide.md) | Guidelines for implementing component states, accessibility, and more. |
|
||||
| [Sass Guidelines](./sass-guidelines.md) | Outlines scenarios where Sass members and comments should be used. |
|
||||
| [CSS Shadow Parts Guidelines](./shadow-parts-guidelines.md) | Guidelines for CSS shadow parts in components. |
|
||||
|
||||
## Packages
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
* [Example Components](#example-components-4)
|
||||
* [Component Structure](#component-structure-1)
|
||||
- [Converting Scoped to Shadow](#converting-scoped-to-shadow)
|
||||
- [Sass Variables](#sass-variables)
|
||||
- [CSS Shadow Parts](#css-shadow-parts)
|
||||
- [RTL](#rtl)
|
||||
- [Adding New Components with Native Input Support](#adding-new-components-with-native-input-support)
|
||||
* [Angular Integration](#angular-integration)
|
||||
@@ -722,6 +724,14 @@ There will be some CSS issues when converting to shadow. Below are some of the d
|
||||
:host-context(ion-toolbar:not(.ion-color)):host(:not(.ion-color)) ::slotted(ion-segment-button) {
|
||||
```
|
||||
|
||||
## Sass Variables
|
||||
|
||||
For guidelines on when to use Sass Variables, see the [Sass Guidelines](./sass-guidelines.md).
|
||||
|
||||
## CSS Shadow Parts
|
||||
|
||||
For guidelines on adding CSS shadow parts, see the [CSS Shadow Parts Guidelines](./shadow-parts-guidelines.md).
|
||||
|
||||
## RTL
|
||||
|
||||
When you need to support both LTR and RTL modes, try to avoid using values such as `left` and `right`. For certain CSS properties, you can use the appropriate mixin to have this handled for you automatically.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Sass Guidelines
|
||||
|
||||
<sub><b>TABLE OF CONTENTS</b></sub>
|
||||
- [Definitions](#definitions)
|
||||
- [Scope](#scope)
|
||||
- [Historical Usage](#historical-usage)
|
||||
|
||||
277
docs/shadow-parts-guidelines.md
Normal file
277
docs/shadow-parts-guidelines.md
Normal file
@@ -0,0 +1,277 @@
|
||||
# CSS Shadow Parts Guidelines
|
||||
|
||||
<sub><b>TABLE OF CONTENTS</b></sub>
|
||||
- [Definitions](#definitions)
|
||||
- [Scope](#scope)
|
||||
- [General Guidelines](#general-guidelines)
|
||||
- [Standard Parts](#standard-parts)
|
||||
- [Specialized Parts](#specialized-parts)
|
||||
- [Documentation](#documentation)
|
||||
|
||||
## Definitions
|
||||
|
||||
**CSS Shadow Parts:** The CSS shadow parts module defines the [::part()](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/::part) pseudo-element that can be set on a [shadow host](https://developer.mozilla.org/en-US/docs/Glossary/Shadow_tree). Using this pseudo-element, you can enable shadow hosts to expose the selected element in the shadow tree to the outside page for styling purposes. [^1]
|
||||
|
||||
## Scope
|
||||
|
||||
Ionic Framework components that use Shadow DOM expose CSS Shadow Parts to enable custom styling by end users.
|
||||
|
||||
This document establishes a standardized naming convention for CSS Shadow Parts in Ionic Framework components.
|
||||
|
||||
## General Guidelines
|
||||
|
||||
1. **Attempt to use standard parts first**: Use `native`, `wrapper`, `inner`, `container`, and `content` wherever they apply before inventing new names.
|
||||
2. **Use semantic, kebab-case names**: Choose descriptive names that communicate the role of the element (for example, `detail-icon`, `supporting-text`).
|
||||
3. **Reuse names for the same concept**: Use the same part name across components when the element serves the same role (for example, `backdrop`, `handle`, `label`).
|
||||
|
||||
## Standard Parts
|
||||
|
||||
**Name parts by what the element does, not where it appears.** Ask what role the element plays:
|
||||
|
||||
| Name | Role |
|
||||
| --- | --- |
|
||||
| `native` | Is it a native HTML element that the user interacts with (e.g. `<button>`, `<a>`, `<input>`, `<textarea>`)? |
|
||||
| `wrapper` | Is it a native `<label>` element that wraps the whole form control? |
|
||||
| `inner` | Is it the inner layout wrapper around the main content? It may wrap only the default slot (e.g. `ion-list-header`), or a container plus slot(s) (e.g. `ion-item`, `ion-item-divider`, `ion-item-option`) when present. |
|
||||
| `container` | Does it wrap the main content itself (default slot or native control)? |
|
||||
| `content` | Is it the main user-content area of an overlay or primary content region? |
|
||||
|
||||
The following examples show the correct usage for the standard parts.
|
||||
|
||||
### `native`
|
||||
|
||||
**What it does:** The element the user directly interacts with - the native button, anchor, or form control (e.g. `<button>`, `<a>`, `<textarea>`, `<input>`).
|
||||
|
||||
- **Use when**: The element receives click/focus/input from the user.
|
||||
- **Examples**: `ion-item`, `ion-button`, `ion-textarea`.
|
||||
|
||||
**ion-item** - the interactive element is the `<button>`, `<a>`, or `<div>` (`TagType`):
|
||||
|
||||
```tsx
|
||||
const TagType = clickable ? (href === undefined ? 'button' : 'a') : ('div' as any);
|
||||
|
||||
return (
|
||||
<Host>
|
||||
<TagType class="item-native" part="native">
|
||||
<slot name="start"></slot>
|
||||
<div class="item-inner" part="inner">
|
||||
<div class="input-wrapper" part="container">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<slot name="end"></slot>
|
||||
</div>
|
||||
</TagType>
|
||||
</Host>
|
||||
);
|
||||
```
|
||||
|
||||
**ion-textarea** - the interactive element is the native `<textarea>`:
|
||||
|
||||
```tsx
|
||||
<div class="native-wrapper" part="container">
|
||||
<textarea class="native-textarea" part="native">
|
||||
{value}
|
||||
</textarea>
|
||||
</div>
|
||||
```
|
||||
|
||||
### `wrapper`
|
||||
|
||||
**What it does:** The HTML `<label>` element that wraps the entire form control. Clicking anywhere on it focuses the control.
|
||||
|
||||
- **Use when**: The element is the `<label>` that wraps the form control.
|
||||
- **Examples**: `ion-select`, `ion-textarea`, `ion-input`, `ion-checkbox`, `ion-toggle`, `ion-radio`, `ion-range`.
|
||||
|
||||
**ion-select** - the `<label>` has `part="wrapper"`:
|
||||
|
||||
```tsx
|
||||
<label class="select-wrapper" part="wrapper">
|
||||
{this.renderLabelContainer()}
|
||||
<div class="select-wrapper-inner" part="inner">
|
||||
<slot name="start"></slot>
|
||||
<div class="native-wrapper" part="container">...</div>
|
||||
<slot name="end"></slot>
|
||||
</div>
|
||||
</label>
|
||||
```
|
||||
|
||||
**ion-textarea** - the `<label>` has `part="wrapper"`:
|
||||
|
||||
```tsx
|
||||
<label class="textarea-wrapper" part="wrapper">
|
||||
{this.renderLabelContainer()}
|
||||
<div class="textarea-wrapper-inner" part="inner">
|
||||
<slot name="start"></slot>
|
||||
<div class="native-wrapper" part="container">...</div>
|
||||
<slot name="end"></slot>
|
||||
</div>
|
||||
</label>
|
||||
```
|
||||
|
||||
### `inner`
|
||||
|
||||
**What it does:** The inner layout wrapper around the main content. It may wrap only the default slot (e.g. `ion-list-header`), or it may wrap a container and the slot(s) (e.g. `start`, `end`) that sit alongside the main content. In `ion-item`, and `ion-item-divider`, the `start` slot is a sibling of this element. In `ion-select`, both `start` and `end` slots are inside this element.
|
||||
|
||||
- **Use when**: The element is the inner layout wrapper (with or without a separate container and `start`/`end` slots).
|
||||
- **Examples**: `ion-list-header` (`.list-header-inner` wraps only the default slot), `ion-item` (`.item-inner`), `ion-item-divider` (`.item-divider-inner`), `ion-select` (`.select-wrapper-inner`).
|
||||
|
||||
**ion-list-header** - `.list-header-inner` wraps only the default slot (no container, no `start`/`end` slots):
|
||||
|
||||
```tsx
|
||||
<div class="list-header-inner" part="inner">
|
||||
<slot></slot>
|
||||
</div>
|
||||
```
|
||||
|
||||
**ion-item** - `.item-inner` wraps the container and `end` slot (`start` slot is a sibling):
|
||||
|
||||
```tsx
|
||||
<slot name="start"></slot>
|
||||
<div class="item-inner" part="inner">
|
||||
<div class="input-wrapper" part="container">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<slot name="end"></slot>
|
||||
</div>
|
||||
```
|
||||
|
||||
**ion-item-divider** - `.item-divider-inner` wraps the container and `end` slot (`start` slot is a sibling):
|
||||
|
||||
```tsx
|
||||
<slot name="start"></slot>
|
||||
<div class="item-divider-inner" part="inner">
|
||||
<div class="item-divider-wrapper" part="container">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<slot name="end"></slot>
|
||||
</div>
|
||||
```
|
||||
|
||||
**ion-select** - `.select-wrapper-inner` arranges `start` slot, container, `end` slot:
|
||||
|
||||
```tsx
|
||||
<div class="select-wrapper-inner" part="inner">
|
||||
<slot name="start"></slot>
|
||||
<div class="native-wrapper" part="container"></div>
|
||||
<slot name="end"></slot>
|
||||
</div>
|
||||
```
|
||||
|
||||
### `container`
|
||||
|
||||
**What it does:** Wraps the main content - either the default slot (for item-like components) or the native control and its immediate content (for form controls like select, textarea).
|
||||
|
||||
- **Use when**: The element wraps the default slot, or wraps the native control (and any immediate content like listbox or slots inside it).
|
||||
- **Don’t use when**: The element is the main content area of an overlay (use `content` instead).
|
||||
- **Examples**: `ion-item` (`.input-wrapper` around default slot), `ion-item-divider` (`.item-divider-wrapper`), `ion-select` (`.native-wrapper` around select text + listbox), `ion-textarea` (`.native-wrapper` around `<textarea>`).
|
||||
|
||||
From the examples above:
|
||||
|
||||
**ion-item** - `.input-wrapper` wraps the default `<slot>`:
|
||||
|
||||
```tsx
|
||||
<slot name="start"></slot>
|
||||
<div class="item-inner" part="inner">
|
||||
<div class="input-wrapper" part="container">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<slot name="end"></slot>
|
||||
</div>
|
||||
```
|
||||
|
||||
**ion-select** - `.native-wrapper` wraps the select text and listbox:
|
||||
|
||||
```tsx
|
||||
<div class="select-wrapper-inner" part="inner">
|
||||
<slot name="start"></slot>
|
||||
<div class="native-wrapper" part="container">
|
||||
{this.renderSelectText()}
|
||||
{this.renderListbox()}
|
||||
</div>
|
||||
<slot name="end"></slot>
|
||||
</div>
|
||||
```
|
||||
|
||||
**ion-textarea** - `.native-wrapper` wraps the `<textarea>`:
|
||||
|
||||
```tsx
|
||||
<label class="textarea-wrapper" part="wrapper">
|
||||
{this.renderLabelContainer()}
|
||||
<div class="textarea-wrapper-inner" part="inner">
|
||||
<slot name="start"></slot>
|
||||
<div class="native-wrapper" part="container">
|
||||
<textarea class="native-textarea" part="native">
|
||||
{value}
|
||||
</textarea>
|
||||
</div>
|
||||
<slot name="end"></slot>
|
||||
</div>
|
||||
</label>
|
||||
```
|
||||
|
||||
### `content`
|
||||
|
||||
**What it does:** The main user-content area of an overlay or the primary content region (e.g. modal body, toolbar’s main slot).
|
||||
|
||||
- **Use when**: The element is the primary content area where users see the main content (overlay body, or primary slot inside something like a toolbar).
|
||||
- **Examples**: `ion-modal`, `ion-popover`, `ion-accordion`, `ion-toolbar` (the div that wraps the default slot inside the toolbar container).
|
||||
|
||||
**ion-modal** - `content` wraps the default `<slot>` which is the primary content:
|
||||
|
||||
```tsx
|
||||
<div class="modal-content" part="content">
|
||||
<slot></slot>
|
||||
</div>
|
||||
```
|
||||
|
||||
**ion-toolbar** - `content` wraps the default `<slot>` which is the primary content:
|
||||
|
||||
```tsx
|
||||
<div class="toolbar-container" part="container">
|
||||
<slot name="start"></slot>
|
||||
<slot name="secondary"></slot>
|
||||
<div class="toolbar-content" part="content">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<slot name="primary"></slot>
|
||||
<slot name="end"></slot>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Specialized Parts
|
||||
|
||||
Components may also expose specialized parts for specific elements. The following parts are reused across multiple components:
|
||||
|
||||
| Name | Description |
|
||||
| --- | --- |
|
||||
| `background` | Background elements (e.g., `ion-content`, `ion-toolbar`) |
|
||||
| `backdrop` | Backdrop elements. **Must only be used on `<ion-backdrop>` components.** (e.g., `ion-modal`, `ion-popover`, `ion-menu`) |
|
||||
| `label` | Label text elements - not the HTML `<label>` (see standard part `wrapper`) |
|
||||
| `supporting-text` | Supporting text elements |
|
||||
| `helper-text` | Helper text elements |
|
||||
| `error-text` | Error text elements |
|
||||
| `icon` | Icon elements. **Must only be used on `<ion-icon>` components.** Use specific names like `detail-icon`, `close-icon` when the icon serves a distinct purpose (e.g., `ion-item` uses `detail-icon`, `ion-fab-button` uses `close-icon`) |
|
||||
| `handle` | Handle elements (e.g., `ion-modal`, `ion-toggle`) |
|
||||
| `track` | Track elements (e.g., `ion-toggle`, `ion-progress-bar`) |
|
||||
| `mark` | Checkmark or indicator marks (e.g., `ion-checkbox`, `ion-radio`) |
|
||||
|
||||
**When to create new specialized parts:**
|
||||
- Use standard parts (`native`, `wrapper`, `inner`, `container`, `content`) when they apply
|
||||
- Reuse existing specialized parts (listed above) when they match the element's role
|
||||
- Create component-specific specialized parts for elements that don't fit standard patterns or existing specialized parts
|
||||
- Use descriptive, semantic names (e.g., `header`, `text`, `arrow`, `scroll` for component-specific elements)
|
||||
|
||||
## Documentation
|
||||
|
||||
Shadow parts must be documented in the component's JSDoc comments using the `@part` tag. The following example demonstrates the proper documentation format:
|
||||
|
||||
```tsx
|
||||
/**
|
||||
* @part native - The native HTML button, anchor or div element that wraps all child elements.
|
||||
* @part inner - The inner wrapper element that arranges the item content.
|
||||
* @part container - The wrapper element that contains the default slot.
|
||||
* @part detail-icon - The chevron icon for the item. Only applies when `detail="true"`.
|
||||
*/
|
||||
```
|
||||
|
||||
[^1]: MDN Documentation - CSS shadow parts, https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Shadow_parts
|
||||
Reference in New Issue
Block a user