DocumentationComponentsTheme CreatorGitHub
Theme CreatorGitHubIntroduction
InstallationUsageTheming
ComponentsAccordionAction SheetAlertAlertDialogArea ChartAspectRatioAvatarBadgeBannerBar ChartBottom NavBreadcrumbButtonButtonGroupCalendarCardCarouselChartChatBubbleChatBubbleNewCheckboxChipCoachMarkCodeBlockCollapsibleColor PickerComboboxCommandContainerContextMenuDate PickerDividerDrawerDropdown MenuEmptyStateFABFieldFieldsetFile UploadFlexFormGridHoverCardIconButtonLabelLine ChartLinkMenubarMeterModalNavigation MenuNumberFieldOTP InputPaginationPickerPie ChartPopoverProgressPromptInputRadar ChartRadial ChartRadioRatingScroll AreaSearchSegmentedControlSelectShortcutSidebarSkeletonSliderCircularSliderMediaTrimmerSpacerSpinnerSplit ButtonStackStatusStepperSurfaceSwitchTableTabsTextareaTime PickerToastToggleButtonToggleGroupTokenizerToolbarTooltipTop Header DesktopTop Header Mobile
Contributing
Components

Divider

A hairline rule that separates content. Horizontal or vertical, solid / dashed / dotted, three thickness steps, tonal strength that adapts to coloured surfaces, an inline label, and an inset that aligns the line to the row's text edge.

Playground

Installation

pnpm add @tessinaui/ui

Usage

import { Divider } from "@tessinaui/ui";
<Divider />

When to use

Use a divider when whitespace alone cannot group content — a list of rows that would otherwise run together, a toolbar with two groups of icons, a total that must read as separate from the lines above it.

When not to use. Reach for space or a card first. Material, Carbon and Fluent all say the same thing, and the app survey agreed: nine of the surveyed products group content with a card and a gap and ship no rule at all. A divider between every item makes a list look busier, not clearer — divide groups, not peers.

A centred caption with no rule either side (a chat day-stamp, a settings section caption) is not a divider. That is a Label or a Badge.

Examples

Default

A horizontal rule separating two blocks of content.

Inset

The list separator every phone ships: the rule starts where the row's text starts, not at the frame, so a leading icon column reads as one stack. Set --divider-inset to the row's own inline padding. On a vertical divider the same prop shortens the rule along its own axis — the toolbar rule that stops short of the bar's edges.

Variants

Three line styles — solid, dashed, and dotted. Dashed and dotted read as receipts, tickets and pricing cards.

Sizes

Three thickness steps: md (1px hairline, the default), lg (2px) and xl (4px). Anything heavier than 4px is a filled band rather than a line — paint it with a Surface.

Tones

subtle, default and strong are steps of the same strength ladder. On a coloured plate the ladder is re-expressed in the surface's own ink, and Divider picks that up from the enclosing Surface on its own — you do not pass tone="on-color" by hand.

Emphasis

Weight carries meaning: a hairline separates peers, a heavier and darker rule marks the line a total sits below.

With label

An inline label between two flanking rules — the "or" of an auth screen. labelPosition moves it to the start or end.

Vertical

Vertical rules separating inline items. A vertical divider stretches to its flex parent, so that parent needs a height.

Semantic separator

Most dividers are decoration and stay silent. decorative={false} emits a real role="separator"; give it a label and the name is wired up by reference so it is actually announced.

Custom element

render swaps the host element — most usefully <li>, because a <div> between list items breaks the list for assistive tech.

Variants

VariantDescription
solidContinuous line. Default.
dashedEvenly spaced dashes.
dottedFine dotted pattern.

Sizes

SizeThicknessWhen to use
md1pxThe hairline. Default, and correct for almost every list.
lg2pxEmphasis — the rule above a checkout total, a table header.
xl4pxA hard section break.

Heavier than 4px is a band, not a line: in every surveyed app, thick section splits were a filled background showing between grouped blocks. Use a Surface.

Orientations

OrientationNotes
horizontalSpans the full width of its container. Default.
verticalStretches to a flex parent (align-self: stretch); that parent must have a height.

Tones

ToneOn a plain surfaceOn a coloured surfaceWhen to use
subtleborder/50current/10Barely-there separation inside grouped content.
defaultbordercurrent/20Standard separation. Default.
strongmuted-foregroundcurrent/40Emphasis — section breaks, checkout totals.
on-color—current/20Only needed when you paint a coloured plate yourself, without a Surface.

The on-color column is a tint of the surface's own ink, never a literal white: an on-color surface is not always dark (in dark mode --primary and --foreground are near-white), and a white rule on one of those is invisible.

Inset

inset pulls the ends of the rule in along its own axis, so the line can start at the row's text edge instead of the frame. This is the default separator shape on iOS and Material's inset / middle divider, and it is what CONVENTIONS §7b.1 asks for inside popups.

ValueEffect
noneFull-bleed, edge to edge. Default.
startInset at the leading end only — the iOS list separator.
endInset at the trailing end only.
bothInset at both ends — Material's "middle" divider.

The distance is the --divider-inset custom property, default 1rem. Set it to whatever your row actually uses:

<Divider inset="start" className="[--divider-inset:3.25rem]" />

Insets use logical properties, so they follow dir without a second map.

Labels

Pass label (or children) to render text between two flanking rules. labelPosition controls where the label sits.

<Divider label="or" />
<Divider label="Recently" labelPosition="start" />
<Divider label="Archive" labelPosition="end" />

For a section caption above a rule — the form-section pattern — compose a heading and a plain divider instead. That keeps the heading a real heading in the accessibility tree, which a label inside a divider can never be.

<h3 className="text-sm font-medium">Billing</h3>
<Divider spacing="sm" />

Accessibility

By default a Divider is decorative: it emits no role at all, both rules are aria-hidden, and screen readers skip it. Set decorative={false} when the boundary carries meaning, and it emits role="separator" with the matching aria-orientation.

Emitting no role rather than role="none" is deliberate. On the default <div> the two are equivalent — a <div> is already generic — but role="none" overwrites whatever host render supplies, and a render={<li />} divider carrying role="none" strips the list item's role and breaks the whole list. Pass role="none" explicitly if you want it.

<Divider />                                            {/* silent */}
<Divider decorative={false} />                         {/* semantic separator */}
<Divider decorative={false} orientation="vertical" />  {/* vertical separator */}

A labelled semantic separator is a trap, and this component steps around it. WAI-ARIA marks separator as children presentational: any text nested inside one is erased for assistive tech, so a labelled divider that puts its text in the separator element announces nothing at all. (Ant Design and Mantine both ship exactly that bug.) separator does still take a name from the author, so Divider gives the label an id and points aria-labelledby at it — the rule is announced with its name. A consumer's own aria-label or aria-labelledby always wins.

<Divider decorative={false} label="Archive" />
{/* announced as "Archive, separator" */}

Inside a list, use render={<li />}. A <div> between <li>s is invalid list structure and assistive tech will report the list length wrongly.

Contrast here is a non-text concern (WCAG 1.4.11). subtle is deliberately a low-contrast tone: it is for decorative separation inside content that is already grouped, and must never be the sole boundary of a control. Measured ratios for every tone in both themes are in the component's variant ledger.

Assistive technology matrix

ATStatus
VoiceOver (macOS)Passed (2026-09-10, Safari) — a decorative divider is silent; decorative={false} announces "horizontal splitter"; with a label, "horizontal splitter Kestrel". Transcript in the variant ledger
NVDAUntested — not runnable on the maintainers' machines
JAWSUntested — not runnable on the maintainers' machines

RTL

The divider uses logical properties throughout — border-inline-start for the vertical rule, margin-inline for spacing and insets — so vertical rules and start/end insets flip correctly in right-to-left layouts. Pass dir="rtl" when you need explicit direction context.

<Divider label="أو" labelPosition="start" dir="rtl" />

API Reference

Divider

PropTypeDefaultDescription
orientation"horizontal" | "vertical""horizontal"Layout axis
variant"solid" | "dashed" | "dotted""solid"Line style
size"md" | "lg" | "xl""md"Thickness: 1 / 2 / 4 px
tone"default" | "subtle" | "strong" | "on-color""default"Strength of the rule; adapts to a coloured surface
spacing"none" | "xs" | "sm" | "md" | "lg" | "xl""none"Gap opened across the rule
inset"none" | "start" | "end" | "both""none"Pull the rule's ends in along its own axis
labelReactNode—Inline label between two flanking rules
labelPosition"start" | "center" | "end""center"Where the label sits along the axis
decorativebooleantrueIf false, emits role="separator" + aria-orientation
renderRenderProp—Swap the host element (e.g. <li />)
dir"ltr" | "rtl"—Text direction
roleAriaRole—Explicit role override
classNamestring—Extra classes on the root element

The component extends all standard div HTML attributes.

Exports

ExportTypeDescription
DividercomponentThe rule itself
dividerVariantscvaThe line variants, for composing a rule elsewhere

Data attributes

Stable hooks for styling, testing and automation.

AttributeOnValue
data-slot="divider"root—
data-slot="divider-line"each rule—
data-slot="divider-label"label—
data-orientationroothorizontal | vertical
data-variantrootsolid | dashed | dotted
data-tonerootdefault | subtle | strong | on-color
data-on-colorroot"true" when on a coloured surface; absent otherwise
data-insetrootnone | start | end | both

CSS custom properties

PropertyDefaultEffect
--divider-inset1remDistance the inset prop pulls each end in
Date PickerDrawer

On this page

PlaygroundInstallationUsageWhen to useExamplesDefaultInsetVariantsSizesTonesEmphasisWith labelVerticalSemantic separatorCustom elementVariantsSizesOrientationsTonesInsetLabelsAccessibilityAssistive technology matrixRTLAPI ReferenceDividerExportsData attributesCSS custom properties