opsinjs

Divider

A thin line that separates two groups of content, with an optional centred label, drawn as a neutral hairline and carrying no status or category colour.

Preview

Theme
Density
Text
Status
divider · base base · style base-lyraOpen under the product theme

Installation

pnpm dlx shadcn@latest add @opsinjs/divider

The @opsinjs namespace is declared in your components.json. Everything it installs is code you then own. There is no runtime package to keep in step.

Usage

import { Divider } from "@/components/ui/divider"
<Divider />
<Divider label="Earlier" />
<Divider orientation="vertical" />

When to use it

Use it when

  • Between two stacked groups that are already distinct, to make the seam visible without giving either group a surface of its own.
  • As a named boundary in a list, with a short label such as "Earlier" or "Today", echoing a grouping the list structure already carries.
  • As a thin vertical rule between a few inline facts, inside a flex row that gives the rule its height.

Do not use it when

  • You are grouping content that needs its own background, edge or padding, rather than a line between peers.Use card instead.
  • You are separating layers of the interface by depth, such as a panel above the page.Use surface instead.
  • You are offering a choice between mutually exclusive views and the lines are really the seams between options.Use segmented-control instead.

Anatomy

  1. DividerThe root. A neutral hairline on neither colour axis. Unlabelled, it is one element with role separator.Controlled by orientation
  2. Divider labelOptional centred text on a horizontal rule, in muted ink. Its presence drops the separator role.Controlled by label

Examples

A labelled boundary in a list

label marks the seam between today's items and the earlier ones. The two lists carry the grouping on their own; the divider is the visible echo, not the only signal.

Theme
Density
Text
Status
divider-a-labelled-group-boundary · base base · style base-lyraOpen under the product theme

A vertical rule between inline facts

orientation="vertical" draws a hairline that takes its height from the flex row around it. In a block that gives it no height it collapses to nothing.

Theme
Density
Text
Status
divider-vertical-between-inline-facts · base base · style base-lyraOpen under the product theme

Content guidelines

A label is a short plain noun for the boundary it marks: Earlier, Today, Archived. It is not a heading and not a sentence, so no verb and no full stop. Never let a divider be the only thing separating two groups; group with a list, a heading or a region first, then add the line.

Do

A divider between two lists that already carry their own structure, so the line is an echo.

Don’t

A divider between two runs of paragraphs to imply sections a screen reader cannot hear.

Accessibility

Checked by pnpm run check:a11y on every commit. Every colour is a role token, no type size is in px, and no banned word appears anywhere.

Audited against WCAG 2.2 AA, in a source pass over divider.tsx and its two examples and a rendered pass over the shipped preview. The audit is author run, not an independent review, and clinical review is still pending. It found nothing to change.

  • Not focusable and captures no key, in either orientation and whether or not it is labelled.
  • Unlabelled, the root exposes role="separator" with aria-orientation, so a reader navigating by region can skip it.
  • Labelled, the root has no role, because a separator cannot carry an accessible name. The label is plain text between two aria-hidden hairlines.
  • A divider is never the only carrier of grouping for a screen reader; the groups either side must be separated by structure too.

Residual, not yet measured. The hairline's contrast against a card, a sheet and the page, and the labelled form's footnote text against --muted-foreground, are both shared role token pairs missing from the generated contrast table; fixing either means editing the token set, which sits outside this component's own file.

KeyActionNotes
TabSkips the dividerNot focusable and not a tab stop, in either orientation, labelled or not.
Shift+TabSkips the divider in reverseFocus moves straight to the interactive element before it, in visual order.
PairThemeAPCA LcWCAG 2.2Floor
body text on the pagelight101.617.18:1Pass
body text on the pagedark-100.518.00:1Pass
body text on a cardlight104.717.96:1Pass
body text on a carddark-99.616.32:1Pass
body text on the muted groundlight98.116.32:1Pass
body text on the muted grounddark-97.513.77:1Pass
secondary text on the pagelight83.97.42:1Pass
secondary text on the pagedark-80.713.44:1Pass
secondary text on a cardlight87.07.76:1Pass
secondary text on a carddark-79.812.19:1Pass
secondary text on the muted groundlight80.47.05:1Pass
secondary text on the muted grounddark-77.810.28:1Pass
a hairline boundary on the pagelight19.31.41:1Below floor
a hairline boundary on the pagedark-8.21.90:1Below floor
a hairline boundary on a cardlight22.41.47:1Below floor
a hairline boundary on a carddark-7.31.72:1Below floor
a hairline boundary on the muted groundlight15.81.34:1Below floor
a hairline boundary on the muted grounddark0.01.45:1Below floor
a placeholder boundary on the pagelight69.84.52:1Pass
a placeholder boundary on the pagedark-51.07.62:1Pass
a placeholder boundary on a cardlight72.84.72:1Pass
a placeholder boundary on a carddark-50.16.91:1Pass
a placeholder boundary on the muted groundlight66.24.29:1Pass
a placeholder boundary on the muted grounddark-48.15.83:1Pass
a placeholder fill on the pagelight8.31.18:1Below floor
a placeholder fill on the pagedark-8.21.90:1Below floor
a placeholder fill on a cardlight11.41.23:1Below floor
a placeholder fill on a carddark-7.31.72:1Below floor
a placeholder fill on the muted groundlight0.01.12:1Below floor
a placeholder fill on the muted grounddark0.01.45:1Below floor
a placeholder boundary on its own filllight58.93.84:1Pass
a placeholder boundary on its own filldark-41.54.01:1Below floor
the card hairline on the pagelight69.84.52:1Pass
the card hairline on the pagedark-28.74.19:1Below floor
the card hairline on a cardlight72.84.72:1Pass
the card hairline on a carddark-27.93.80:1Below floor

These are the measured token pairs this component draws colour from, not a measurement of the component itself.

API reference

Prop

Type

Generated from DividerProps in registry/bases/base/divider.tsx.

orientation defaults to horizontal; an unknown value is drawn horizontal with a development warning. label applies to the horizontal form only and is dropped with a warning on a vertical rule. A whitespace-only label counts as none. className is the only route by which colour can reach the component, and both colour axes stay off it.

  • Card groups content by giving it a background, an edge and padding. A divider only draws a line.
  • Surface places a group on its own material at a chosen depth. A divider marks a seam between peers.
  • SegmentedControl offers a choice between views. Its seams look like dividers but carry a selection.

On this page