Dividers
components specs/components/dividers.kmd
Thin line separating content groups within a single surface. Material parity (`/components/dividers`). Covers full-width, inset, middle (with text), and sub-header dividers. Defines when to use vs whitespace alone.
When this spec applies
Primary triggers
- Add a divider
All triggers
- Separate list rows
- Group items inside a card or menu
- Introduce a labeled section in a long form
Specification body
Spec — Dividers
Facet Visual of Koder Design. Material parity: https://m3.material.io/components/dividers.
When to use a divider vs whitespace
| Need | Use |
|---|---|
| Visually group ungrouped rows | Divider |
| Already grouped by indent / card / surface | Whitespace only |
| Separate sections with title | Subheader divider |
| Inline content split (mid-paragraph) | Inline divider with text |
| Compact list with many rows | Divider (otherwise hard to scan) |
| Spacious layout, few items | Whitespace |
Rule: divider answers "where does one group end and the next begin". If whitespace already makes that clear, skip the divider.
4 variants
| Variant | Width | Padding | Use |
|---|---|---|---|
| Full-bleed | 100% of container | 0 | Separates major sections at surface edge |
| Inset | Indented from left | 16 px (matches list inset) | List rows with leading icon/avatar |
| Middle (with content) | Two segments around centered text/icon | n/a | Section heading inline |
| Subheader | Full-bleed + label above | 0 | Labeled section break |
Anatomy
Full-bleed:
────────────────────────────────────────
Inset:
───────────────────────────────────── (← 16 px left padding)
Middle with text:
────────────── OR ──────────────────
Subheader:
Section title
────────────────────────────────────
- Line thickness: 1 px (logical pixel)
- Color:
outline-variant(low-emphasis surface line) - Subheader label:
label-large(14/20, weight 500),text-muted - Inline text:
label-medium, 8-12 px padding around text
R1 — Color and emphasis
| Emphasis | Color | Use |
|---|---|---|
| Low (default) | outline-variant | Most dividers |
| High | outline | Rare — only when divider is the primary visual structure |
| Decorative | primary 12% | Highlight a section break (e.g., onboarding) |
Never use full saturation (primary solid) — divider should be
subtle background, not foreground.
R2 — Orientation
| Orientation | Use |
|---|---|
| Horizontal (default) | Between rows / sections in a vertical layout |
| Vertical | Between items in a horizontal layout (toolbar, segmented controls) |
Vertical divider:
- Height: 24 px (matches surrounding control height OR set explicitly)
- Width: 1 px
- Margin: 8 px horizontal
R3 — Inset rules
Inset divider's left edge aligns with the START of meaningful content in the row above. Common alignments:
| Inset distance | Aligns with |
|---|---|
| 16 px | First text character (list item with no leading element) |
| 56 px | After a 24 px icon + 16 px gap |
| 72 px | After a 40 px avatar + 16 px gap |
Goal: divider visually splits the content into discrete rows without crossing into the leading-element column. The leading column reads as "continuous" while the text column reads as "discrete rows".
R4 — Middle (with content) divider
Inline text divider for section breaks within text content:
────────────── OR ──────────────
────────── CONTINUED ──────────
───────── • • • ─────────
- Text padded 8-12 px on either side
- Text is
label-medium,text-muted(subtle) - Line on each side, equal length
- Used for: onboarding step breaks, "Show more" pagination, modal alternate-action separator ("OR sign in with…")
R5 — Subheader divider
Pairs a label-large heading with a full-bleed divider below:
System
────────────────────────────────────
Notifications ⌄
Privacy ⌄
Storage ⌄
Account
────────────────────────────────────
Profile ⌄
Sign out
- Used for grouped list sections (Settings is the canonical example)
- Label sits at standard list left-padding (16 px); divider is full-bleed below
- Label is
text-mutedweight 500 — secondary emphasis vs row labels
R6 — Spacing
Standard vertical spacing around a divider:
| Density | Above | Below |
|---|---|---|
| Compact | 4 px | 4 px |
| Default | 8 px | 8 px |
| Comfortable | 12 px | 12 px |
If dividers are between list rows of fixed height, NO extra spacing — divider sits at the row boundary. Spacing applies for free-form dividers (between cards, sections, etc.).
R7 — Accessibility
- Decorative divider:
role="separator"+aria-orientationif vertical (default horizontal); no extra label needed - Subheader: heading semantics drive the divider's meaning — divider
itself remains
role="separator"; heading is a real<h2>/<h3>element - Middle-text divider: same — text inside is the meaning; divider is decorative
- DO NOT add
aria-labelto a divider with descriptive text — it's visual scaffolding, not content
R8 — Per-preset variation
| Preset | Color | Thickness |
|---|---|---|
material3 | outline-variant (1 px low-emphasis) | 1 px |
material2 | 12% on-surface alpha (sharper) | 1 px |
ios_cupertino | Hairline .separator (0.5 px on retina) | 0.5 px |
gnome | Subtle 1 px gray | 1 px |
windows_11 | Mica-aware subtle 1 px | 1 px |
brutalist | Solid black, thick | 2-3 px |
terminal_classic | ASCII ──────────────── | n/a |
R9 — Forbidden patterns
- ❌ Multiple consecutive dividers (no double / triple lines)
- ❌ Divider thicker than 2 px (becomes a border / rule)
- ❌ Saturated primary color as divider (visual noise)
- ❌ Divider where whitespace alone would do (over-decoration)
- ❌ Divider between EVERY card in a card list (cards are already self-grouped)
- ❌ Divider crossing into the leading-element column (icon / avatar) of inset list rows
- ❌ Vertical divider taller than its surrounding controls (looks detached)
- ❌ Decorative divider with
aria-label(clutters screen reader output)
Cross-link
themes/color-roles.kmd—outline-variant/outlinetokenthemes/typography.kmd—label-largefor subheader labelcomponents/lists.kmd— most common host contextfoundations/elements.kmd— Container family (divider is a sub-class of structural element)
Requirements (testable)
Requirement: Color and emphasis default {#req-divider-color-emphasis}
A divider SHALL render at low emphasis using the outline-variant color token by default, and SHALL NOT use a fully saturated foreground color (solid primary) as its line color; higher-emphasis (outline) or decorative (primary 12%) colors MAY be applied only for the documented exceptional cases.
Scenario: Default divider uses outline-variant
- GIVEN a horizontal divider added with no explicit emphasis override
- WHEN the divider is rendered
- THEN its line color resolves to the
outline-varianttoken - AND its line thickness is 1 logical pixel
Scenario: Saturated primary is rejected
- GIVEN a divider configured with a solid saturated
primaryline color - WHEN the component validates its styling
- THEN the configuration is rejected as a forbidden pattern
- AND the divider does not render with the solid
primarycolor
Requirement: Orientation {#req-divider-orientation}
A divider SHALL default to horizontal orientation for separating rows or sections in a vertical layout, and SHALL support an explicit vertical orientation for separating items in a horizontal layout, where the vertical divider is 1 px wide with 8 px horizontal margin and a height not exceeding its surrounding controls.
Scenario: Default orientation is horizontal
- GIVEN a divider created without an orientation argument
- WHEN it is rendered between two stacked rows
- THEN its computed orientation is horizontal
Scenario: Vertical divider matches control height
- GIVEN a vertical divider placed inside a toolbar of 24 px control height
- WHEN it is rendered
- THEN its width is 1 px
- AND its horizontal margin is 8 px on each side
- AND its height does not exceed the surrounding control height
Requirement: Inset alignment {#req-divider-inset-alignment}
An inset divider SHALL align its left edge with the START of the meaningful content in the row above (16 px with no leading element, 56 px after a 24 px icon, 72 px after a 40 px avatar), and SHALL NOT extend into the leading-element (icon/avatar) column.
Scenario: Inset aligns after a leading avatar
- GIVEN an inset divider under a list row with a 40 px leading avatar and a 16 px gap
- WHEN the divider is rendered
- THEN its left edge starts at 72 px from the row's left edge
- AND no part of the line crosses into the leading avatar column
Requirement: Spacing at fixed-height row boundaries {#req-divider-spacing}
A divider placed between list rows of fixed height SHALL sit exactly at the row boundary with NO extra vertical spacing added; a free-form divider between cards or sections SHALL apply the density-appropriate spacing above and below (4 px compact, 8 px default, 12 px comfortable).
Scenario: No extra spacing between fixed-height rows
- GIVEN a divider between two fixed-height list rows
- WHEN the divider is rendered
- THEN it sits at the shared row boundary
- AND no additional vertical spacing is inserted above or below it
Scenario: Free-form divider uses default density spacing
- GIVEN a free-form divider between two cards at default density
- WHEN the divider is rendered
- THEN 8 px of spacing is applied above the line
- AND 8 px of spacing is applied below the line
Requirement: Accessibility semantics {#req-divider-a11y}
A divider SHALL expose role="separator", SHALL set aria-orientation when it is vertical (horizontal being the implicit default), and SHALL NOT carry an aria-label even when it displays descriptive middle text, because the divider is visual scaffolding rather than content.
Scenario: Vertical divider exposes orientation
- GIVEN a vertical decorative divider
- WHEN its accessibility tree is inspected
- THEN it exposes
role="separator" - AND it exposes
aria-orientation="vertical"
Scenario: Middle-text divider carries no aria-label
- GIVEN a middle divider showing the inline text "OR"
- WHEN its accessibility tree is inspected
- THEN the separator element has
role="separator" - AND the separator element has no
aria-labelattribute
Requirement: Forbidden structural patterns {#req-divider-forbidden}
A divider SHALL NOT be rendered as two or more consecutive lines, and SHALL NOT exceed 2 px thickness (beyond which it reads as a border/rule rather than a divider).
Scenario: Consecutive dividers are rejected
- GIVEN two dividers placed directly adjacent with no content between them
- WHEN the layout is validated
- THEN the doubled divider is flagged as a forbidden pattern
- AND only a single divider line is emitted
Scenario: Over-thick divider is rejected
- GIVEN a divider configured with a 4 px line thickness
- WHEN the component validates its styling
- THEN the thickness is rejected for exceeding the 2 px maximum
References
specs/foundations/elements.kmdspecs/themes/color-roles.kmdspecs/themes/typography.kmd