Skip to content

Pill

A floating lozenge holding a small amount of live information — a recording timer, a boarding gate, a build that is still running.

tsx
import { Pill } from 'neba';

<Pill startIcon={<DotIcon />} color="danger">
  Recording
</Pill>;

Three slots: a leading glyph, a middle, a trailing one. Give it onClick and the middle becomes a real button; give it details and it grows downward into a second half.

Props

PropTypeDefaultDescription
variantshared'solid' | 'outline' | 'text''solid'Weight of the surface, said the way a *control* says it: the surface takes the tint, as on Button and Chip
sizeshared'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Height and type scale
colorshared'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info''secondary'Semantic colour role. `secondary` here rather than `primary`, because the object this shape is borrowed from is very nearly neutral black
densityshared'default' | 'compact''default'Padding only — never the height, never the type scale
elevationshared0 | 1 | 2 | 32Drop shadow depth. `2` against the `0` everything else defaults to, and not an inconsistency: a Pill is defined by not being part of the page, and a lozenge floating flat on the content it floats over reads as a mistake
startIconReactNodeThe leading slot — a glyph, an avatar, a status dot, a small image
endIconReactNodeThe trailing slot. Outside the pressable area, so it can be a control of its own
detailsReactNodeThe second half, revealed when `expanded`. The pill grows downward into it rather than swapping to a different shape: one object saying more, which is the whole idea being borrowed
expandedbooleanfalseWhether `details` is showing
positionshared'static' | 'sticky' | 'fixed''static'How it sits in the page’s scroll. `fixed` pins it against the viewport and centres it horizontally, which is the arrangement this shape exists for
sideshared'top' | 'bottom''top'Which edge it is held against when `position` is not `static`
onClickMouseEventHandlerPassing it makes the row a real button. `endIcon` stays outside it: a `<button>` inside a `<button>` is markup the browser rewrites on parse
childrenReactNodeThe middle: a line of text, a pair of small readouts

Every native <div> attribute passes through.

Examples

Compact and expanded

The pair is the whole idea being borrowed. The pill grows into its second half by animating a measured height — exactly as an Accordion panel does, and for the same reason: nothing is transformed, so no text is ever resampled on the way.

The details area is inert while it is closed, not merely aria-hidden. A zero-height box is one its content is still perfectly focusable inside, and aria-hidden alone would leave a keyboard reader tabbing into something their screen reader has been told does not exist.

Weights and scales

A Pill is a control as far as colour goes, not a container: its surface is the thing being coloured, exactly as on a Button or a Chip. color defaults to secondary here rather than primary, because the object this shape is borrowed from is very nearly neutral black.

Pinned

position and side are the same vocabulary Toolbar uses. fixed pins it against the viewport and centres it horizontally, which is the arrangement this shape exists for — and it is centred with auto margins rather than by translating it half its own width, so it stays centred under RTL.

tsx
<Pill position="fixed" side="top" startIcon={<BuildIcon />} color="info">
  Building — 2 of 7
</Pill>

The round shape

Every other control in the library is held just short of the 50% radius that would make it a pill, because the flat run along its top and bottom edge is what still reads as a sheet with the corners cut off it. This is the exception the rule is drawn against, and it works for the same reason the rule does: a Pill is not a sheet lying on the page. It is an object hovering over one, and an object hovering over the page should not look like it was cut from the same material.

elevation defaults to 2 here for that reason, against the 0 everything else defaults to. It is not an inconsistency — a lozenge floating flat on the content it is floating over reads as a mistake.

When to reach for something else

  • A token inside a run of content — a tag, a filter, a status — is a Chip.
  • A bar of controls along the top of a page is a Toolbar.
  • Something the page is waiting on, that the reader cannot dismiss, is an Overlay; something they can, a Toast.

Released under the MIT License