Skip to content
Highlight

FoundationsUpdated Oct 3, 2026

Border

A border says where one thing ends inside the page. Two widths and a handful of colours: the resting edges are alpha, so one line reads on any ground, and the edges that signal interaction or structure are solid. Anything that floats has no border: its shadow draws the edge.

Pick the colour by what the line separates, and the width by how loud it should be.

tsx
// A divider or an inline card: a hairline in the default border colour<div className="border-b" /><div className="border rounded-xl corner-smooth" /> // Controls carry their own edge. Never add one to Button, Input or Select<Input />                          // border, hover:border-hover<Button variant="outline" />       // border border-strong // Structure, not a divider: under a table head or a section head<thead className="border-b-1 border-emphasis" /> // The current item in a list of navigable things<a className="border-b-2 border-marker" aria-current="page" /> // A bare `border` takes the default colour, so it can be left off<div className="border rounded-xl" /> // A toned edge goes with its tone<div className="border border-danger bg-danger-subtle" /> // Raised or floating: the shadow is the edge. No border<div className="shadow-lg rounded-2xl corner-smooth" />

The hairline is the default. A full pixel is for a line that has to hold its own.

border1 device pixel · hairline, the default
border-11px

A bare border is the hairline: one device pixel, and it is what dividers, cards, fields and the outline button use. On a 2x screen that is half a CSS pixel. At 1.5x and below (a 1x monitor, Windows at 125% or 150%) it is a whole pixel, and the resting edges drop to 60% ink so the wider line keeps the weight it has on Retina. Reach for border-1 (1px) when the line carries meaning on its own: an emphasis rule, a toned callout. The side forms follow Tailwind: border-b, border-x-1, divide-y.

Each edge in both themes. The resting neutrals are alpha, so they tint toward whatever they sit on; hover, emphasis and marker are solid.

border-border
border-hover
border-strong
border-emphasis
border-marker
border-disabled
border-brand
border-danger
border-success
border-warning
border-info
border-border
border-hover
border-strong
border-emphasis
border-marker
border-disabled
border-brand
border-danger
border-success
border-warning
border-info
  • border-borderThe default border; already alpha
  • border-hoverThe border becoming a 3:1 boundary
  • border-strongThe hairline an outline button carries
  • border-emphasisStructure: under a table or section head
  • border-markerThe current item: active tab, selected row
  • border-disabledExempt from 3:1; never the only signal

Inline content takes a hairline. Raised or floating content takes a shadow. Never both.

borderInline: a hairline
shadow-smRaised: the shadow is the edge
border shadow-smBoth: the edge twice

Every shadow step opens with a 1px edge in the same alpha neutral, so a shadowed element already has its outline. Adding a border draws it twice and thickens the corner. Where a component needs an edge only on hover or focus, keep the border at rest as border-transparent so the box does not shift.

Instead ofUse
border border-borderborder
border-[0.5px], border-[1px]border, border-1
border-gray-200, border-[#222]border, border-strong
border-red-500/20border-danger
border shadow-lgshadow-lg
focus:border-blue-500Nothing: base.css draws the focus ring

An opacity tint on border-border is the old way of getting a fainter line, and it makes every divider a different strength. The hairline is that fainter line, at one value.

What the package defines.

css
/* packages/ui/src/styles/tokens.resolution.css: border, the default width, on a 2x screen.   At 1.5x and below it is 1px at 60% ink */:root {  --border-hairline: 0.5px;  --border-ink: 100%;} /* packages/ui/src/styles: tokens.semantic.css and base.css */--border: color-mix(in oklab, var(--gray-a-6) var(--border-ink), transparent);           /* border: The default border; already alpha */--border-hover: var(--gray-9);                                                           /* border-hover: The border becoming a 3:1 boundary */--border-strong: color-mix(in oklab, var(--gray-a-7) var(--border-ink), transparent);    /* border-strong: The hairline an outline button carries */--border-emphasis: var(--gray-11);                                                       /* border-emphasis: Structure: under a table or section head */--border-marker: var(--gray-12);                                                         /* border-marker: The current item: active tab, selected row */--border-disabled: color-mix(in oklab, var(--gray-a-5) var(--border-ink), transparent);  /* border-disabled: Exempt from 3:1; never the only signal */--brand-border: var(--brand-7);                                                          /* border-brand: Step 7, the outline edge */--danger-border: var(--red-7);                                                           /* border-danger: Step 7, the outline edge */--success-border: var(--green-7);                                                        /* border-success: Step 7, the outline edge */--warning-border: var(--orange-7);                                                       /* border-warning: Step 7, the outline edge */--info-border: var(--blue-7);                                                            /* border-info: Step 7, the outline edge */--on-light-border: var(--black-7);                                                       /* border-on-light, fixed in both themes */--on-dark-border: var(--white-7);                                                        /* border-on-dark */