Button
Eight button types, icon buttons and links, in seven sizes, squircle and pill, with their states.
- Source
components/button.json- Figma
- Build by hand: see Build it in Figma
- In code
<Button size shape icon>
Playground
Pick any type, size, shape, content and state. Sizes keep their real proportions; zoom with − and +. Anatomy marks every part with its token.
- 1Frame · fill/secondary · border/secondary · stroke/1Auto layout, horizontal. Carries fill, border, radius, inner shade.
- 2Leading icon frame · 16 frame · 12 vector · size/icon/smSquare, from the icon scale by text size (16, 20 or 24). The icon grid is drawn inside: the vector fills the key square (75%), so at least 1/8 of the frame stays clear. Optional.
- 3Label · body/md/medium · 14/20Text style body/{size}/medium. Buttons use the body role only.
- 4Trailing icon frame · 16 frame · 12 vectorSame as the leading one. Optional; becomes the spinner when loading.
- 5Padding x · 12px · space/12Left and right. Top and bottom are not set: the height is fixed and children are centred.
- 6Gap · 4px · space/4Between every child.
- 7Height · 32px · size/control/xssize/control/{size}, fixed.
- 8Radius · 12px · 32 × 0.375, to the nearest 4Squircle: height × 0.375, rounded to the nearest 4. Pill: radius/full.
Types
Eight types. Every type has the same sizes, shapes and states; only the colours change. Shown at xs, the everyday size.
Links
Links have no frame: text (and an optional icon) in the button's label style for that size, no fill, no border, no padding. Colour changes per state.
Sizes
Seven sizes, one per size/control token. Each comes as a squircle and as a pill, with the same padding. Icon-only buttons are square at the same height.
Build it in Figma
Eight steps, drawn at 3× for xs. Every other size is the same structure with the values from the Size spec.
- Text layer "label".
- Text style body/md/medium (14/20). Fill content/primary.
- Resizing: hug width, hug height.
- Frame 16 × 16 (size/icon/sm), chosen by text size. No fill. Clip content on.
- Use the icon component as is: it is drawn on the icon grid, the vector filling the key square, 12 × 12 centred. Constraints Scale.
- Icon colour content/primary. One on each side of the label.
- Select icon, label, icon → Shift + A.
- Direction horizontal. Gap 4 (space/4).
- Alignment: centre (middle-centre).
- Height fixed 32 (size/control/xs). Width hug.
- Padding left and right 12 (space/12).
- Padding top and bottom 0: the fixed height and centring do the rest.
- Fill fill/secondary. Stroke border/secondary, 1, inside.
- Radius 12 (32 × 0.375, to the nearest 4). Pill: 1000.
- Effect: inner shadow x 0, y −4, blur 10, #000 50%.
- Ctrl + Alt + K. Name it Button.
- Boolean properties: Leading icon, Trailing icon, Label. Instance swap on both icons.
- Text property on the label.
- Properties: Size (2xs … 2xl), Shape (squircle, pill), State (rest, hover, pressed, disabled, loading).
- Per size: change height, padding x, gap, text style, icon frame and radius from the Size spec table.
- Hover / pressed: fill secondary-hover / secondary-active; stroke and icons border/brand. Disabled: the *-disabled set. Loading: disabled + trailing icon swapped for the spinner.
- Label property off, trailing icon off.
- Width fixed = height, padding 0. The icon stays centred.
- Pill shape gives a circle.
Size spec
Source: components/button.json. Every value you type in Figma is on the 4px grid. In Figma: auto layout, horizontal, height fixed, children centred; padding y is not set, it is what is left over.
2xsxssmmdlgxl2xlPill: same values, radius/full (1000). Inner shade on every filled state, inset 0 -4 10 rgba(0,0,0,.5). A Figma effect, not a token (D-021).
Colour
Every type, state by state. none = transparent. Every state with a fill gets the inner shade (0, −4, blur 10, #000 50%). Disabled colours; the trailing icon becomes a spinner. The spinner is an icon: same frame, the ring fills the key square, line weight = frame × size/icon/grid/stroke. Rest colours plus a ring outside the border: border/focus, stroke/2.
fill/brandcontent/on-brandcontent/on-brandfill/brand-hovercontent/on-brandcontent/on-brandfill/brand-activecontent/on-brandcontent/on-brandfill/brand-disabledcontent/on-brand-disabledcontent/on-brand-disabledfill/inversecontent/inverse-primarycontent/inverse-primaryfill/inverse-hovercontent/inverse-primarycontent/inverse-primaryfill/inverse-activecontent/inverse-primarycontent/inverse-primaryfill/secondary-disabledcontent/primary-disabledcontent/primary-disabledfill/secondaryborder/secondarycontent/primarycontent/primaryfill/secondary-hoverborder/brandcontent/primaryborder/brandfill/secondary-activeborder/brandcontent/primaryborder/brandfill/secondary-disabledborder/secondary-disabledcontent/primary-disabledcontent/primary-disabledfill/tertiaryborder/tertiarycontent/primarycontent/primaryfill/tertiary-hoverborder/brandcontent/primaryborder/brandfill/tertiary-activeborder/brandcontent/primaryborder/brandfill/tertiary-disabledborder/tertiary-disabledcontent/primary-disabledcontent/primary-disabledborder/secondarycontent/primarycontent/primaryfill/secondaryborder/brandcontent/primaryborder/brandfill/secondary-hoverborder/brandcontent/primaryborder/brandborder/secondary-disabledcontent/primary-disabledcontent/primary-disabledcontent/primarycontent/primaryfill/secondaryborder/brandcontent/primaryborder/brandfill/secondary-hoverborder/brandcontent/primaryborder/brandcontent/primary-disabledcontent/primary-disabledfill/brand-secondarycontent/brandcontent/brandfill/brand-secondary-hovercontent/brandcontent/brandfill/brand-secondary-activecontent/brandcontent/brandfill/brand-secondary-disabledcontent/brand-disabledcontent/brand-disabledcontent/brandcontent/brandfill/brand-secondarycontent/brandcontent/brandfill/brand-secondary-hovercontent/brandcontent/brandcontent/brand-disabledcontent/brand-disabled