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.
// 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 defaultborder-11pxA 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-borderborder-hoverborder-strongborder-emphasisborder-markerborder-disabledborder-brandborder-dangerborder-successborder-warningborder-infoborder-borderborder-hoverborder-strongborder-emphasisborder-markerborder-disabledborder-brandborder-dangerborder-successborder-warningborder-infoborder-borderThe default border; already alphaborder-hoverThe border becoming a 3:1 boundaryborder-strongThe hairline an outline button carriesborder-emphasisStructure: under a table or section headborder-markerThe current item: active tab, selected rowborder-disabledExempt from 3:1; never the only signal
Inline content takes a hairline. Raised or floating content takes a shadow. Never both.
borderInline: a hairlineshadow-smRaised: the shadow is the edgeborder shadow-smBoth: the edge twiceEvery 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 of | Use |
|---|---|
border border-border | border |
border-[0.5px], border-[1px] | border, border-1 |
border-gray-200, border-[#222] | border, border-strong |
border-red-500/20 | border-danger |
border shadow-lg | shadow-lg |
focus:border-blue-500 | Nothing: 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.
/* 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 */