Pill
A pill-shaped button that holds a label with optional inline-start and inline-end icons.
Show codeHide code
Installation
import { Icon } from "@makeplane/propel/components/icon";
import { Menu, MenuContent, MenuItem, MenuTrigger } from "@makeplane/propel/components/menu";
import { IconPill, Pill, PillGroup } from "@makeplane/propel/components/pill";
import { Swatch } from "@makeplane/propel/elements/swatch";Usage
import { Icon } from "@makeplane/propel/components/icon";
import { Pill } from "@makeplane/propel/components/pill";
import { ReleaseTagOutline, CloseOutline } from "@makeplane/propel/icons";
export default function BasicDemo() {
return (
<div className="flex flex-wrap items-center gap-3">
<Pill size="sm" startIcon={<Icon icon={ReleaseTagOutline} />} label="Design" />
<Pill size="sm" endIcon={<Icon icon={CloseOutline} />} label="Dismissible" />
</div>
);
}Examples
Sizes
size runs xs 20 / sm 24 / md 28 px — the same floor as Button, stopping one rung below
its top, because a pill is inline chrome rather than a primary action. Required on both
Pill and IconPill, except inside a PillGroup, which shares one size
with every segment. See Sizing for the full ladder.
Show codeHide code
Variants
variant selects the fill treatment: outline (the default, ≈ Button secondary), soft (≈ Button tertiary) or ghost (borderless and transparent at rest — for a trigger whose content is its own box, such as an avatar stack).
Show codeHide code
Placeholder
placeholder marks a trigger with no value yet. The chrome stays the same; the label and any glyph that inherits the pill’s colour dim to the placeholder colour, and stay dim while the picker is open.
Show codeHide code
As a picker trigger
Pass render={<MenuTrigger />} (or a PopoverTrigger) and the pill opens the picker. While the popup is up the trigger carries Base UI’s data-popup-open and wears its active look, so the open property reads as pressed for as long as its menu is showing. The reverse graft, <MenuTrigger render={<Pill … />}>, works the same way. SelectTrigger and ComboboxTrigger are not graft targets: use their pill variant when a Select or Combobox needs the same compact label anatomy and active-open treatment, without an automatic chevron.
Show codeHide code
Colour swatch
Swatch is a filled 8px disc that takes the same box an Icon would, so label chips line up with icon chips. fill is any CSS colour — the label’s stored value, not a token. The same part fits a MenuItem’s icon slot.
Show codeHide code
Label-less triggers
label may be omitted when aria-label is given: the label part is skipped entirely, so a pill can hold an avatar stack or an icon alone. Omitting both is a type error.
Show codeHide code
A property row
The compact row of pickers on a work item — state, priority, dates, assignees, labels, cycle — built only from propel parts. One chrome at one size; every leading node fills the same glyph box; colour lives on the glyph and the label stays the pill’s text colour; an empty property is a placeholder; a filled assignee stack sits in a ghost pill so avatars are not boxed.
Show codeHide code
States
disabled and loading both drop the pill to a transparent fill with a dimmed label; loading also hides the icons, shows a spinner after the label, and blocks clicks while staying focusable.
Show codeHide code
Icon only
IconPill is a square, icon-only pill and requires an aria-label for its accessible name.
Show codeHide code
A connected group
PillGroup joins pills that read as one value — a filter condition, a compound property. The segments stay ordinary Pills and IconPills: the frame overlaps their borders into a single divider, keeps only the outermost corners rounded, and lifts whichever segment is hovered, focused, or holding an open picker so it owns the seam it shares. Each segment is still an independent button with its own picker, so the group needs no roving focus and no selection — name it with aria-label.
size is shared through the group, so a segment can omit it. The scale runs sm and md: there is no 20px step, because a contiguous segment cannot carry the hit ring the standalone 20px pill uses, and shrinking the target below 24px would fail WCAG 2.5.8. For several independent actions that merely share chrome, reach for ButtonGroup instead.
Show codeHide code
API Reference
Pill
The ready-made pill button: grafts Base UI's Button behavior onto the styled Pill container
(behavior outer, the styled button as the render target), with an optional leading node, the
PillLabel, an optional trailing node, and a trailing spinner while loading. loading
disables the button while keeping it focusable (aria-busy). With no label the label part is
skipped entirely (an empty flex child would still consume the pill's gap). A consumer render —
render={<MenuTrigger />} to make the pill open a picker — is threaded onto the styled element
so the trigger behavior and its data-popup-open land on the real <button>; the reverse graft
(<MenuTrigger render={<Pill … />}>) works as well. nativeButton rides along the way it does
on Button, for a render that is not a button.
IconPill
The ready-made icon-only pill: grafts Base UI's Button behavior onto the square IconPill
container (behavior outer, the styled button as the render target), filling it with the icon (or
a spinner while loading). loading disables the button while keeping it focusable
(aria-busy). An aria-label is required for the accessible name. A consumer render
(render={<MenuTrigger />}) is threaded onto the styled element, so the trigger behavior and its
data-popup-open land on the real <button> and the pill wears its open look.
PillGroup
The ready-made connected pill group: a group-role frame that shares size with every Pill
and IconPill inside it via context (a segment's own size still wins). There is no Base UI
group primitive — the segments are independent Base UI Buttons, each free to open its own
picker; the frame contributes only the shared chrome, the shared rung, and the role. Name the
group for assistive tech via aria-label.
Built for a compound control whose parts read as one value — a filter condition (Parent type ·
is · Task · ✕), a property row. For several independent actions that merely share chrome, reach
for ButtonGroup.
Swatch
A filled 8px colour disc centred in the inherited --node-size box, so it sits exactly where an
icon would: the leading node of a label pill, or a MenuItem's icon slot. Decorative
(aria-hidden) — the adjacent label carries the name. fill is the one styling input a consumer
supplies, because the colour is data rather than a token; the part applies it itself as
--swatch-fill, so no style prop is exposed.