A small selectable token — a filter, a choice, a person — that can carry an avatar and be removed.
Options
| Choice | Values | Default | What it does |
|---|---|---|---|
tonesmbk-chip--tone-<value> | defaultbrandoutlineinverse | brand | Which action family the chip draws from; default is a see-through gray that shows on every surface. |
sizesmbk-chip--size-<value> | mdsm | md | The pill scale shared with the badge and the tag: 32 and 24 tall. |
selectedsmbk-chip--selected | On or off | Off | The chip is currently chosen: pressed in, with a tick before its label. Given as true or false, the chip is a toggle and announces pressed or not pressed; left out, it is a plain button. |
Examples
Usage
A chip on its own: the brand tone at the default size.
Tones
Four tones, drawn from the action color families.
Selected
A chosen chip is pressed in and shows a tick, so the choice shows by more than its color.
Toggles
A row of filters: every chip is a toggle, so the ones not chosen say "not pressed" as well.
Pressed by a script
A page built ahead of time can press a chip with its own script, setting aria-pressed alone: the chip shows its tick as when React turns it on.
Removable
The remove control appears when a handler is given; its cross sits in a box large enough to press.
Guidelines
Purpose
A small capsule that stands for one choice in a set — a filter, a category, a person — and can be pressed to select it or removed from the set. It is interactive, which is what separates it from a badge, and it is one of many, which is what separates it from a button.
When to use
Filters a person switches on and off.
People or items chosen into a field, each removable.
A row of categories where one or several may be selected.
A short row of related places, such as a site's services, drawn as chips: each chip is then a link.
Usage rules
Do
- Keep labels short; a chip that wraps is a card.
- Use the small size only where the row is dense and the chips are many.
- Give every chip in a filter row its selected state, on or off, so each one says it is a toggle.
Don't
- Color chips by category with the tones. Tones are emphasis, not meaning.
- Put an icon-only chip on a page. A chip is text.
Keep filter chips in one tone and let the chosen ones show it, so color means chosen.
A different tone per category suggests a meaning that is not there and hides which chips are chosen.
Keep the label to a word or two, so the chip stays on one line.
A sentence in a chip stretches it until it reads like a card.
Anatomy
- The block, a capsule of fixed height by size, on the same scale as the badge and the tag. The default tone is a see-through gray with no edge, so it shows on any surface; the others draw a hairline edge.
- The tick part, a check mark before the label of a selected toggle chip, in the avatar's place.
- The avatar part, an optional small avatar before the label, dimmed when the chip is disabled.
- The label part, the text, carrying its own horizontal inset.
- The remove part, an optional small control with a circled cross that removes the chip, inside a box large enough to press.
A toggle chip that is on is drawn from its pressed attribute, as the toggle button is; the selected modifier draws the same, for markup that sets it.
Behavior
A selectable chip toggles between unselected and selected when pressed. Hovered, the brand and inverse tones lift with the first elevation level and the default tone darkens; pressed and selected, every tone sinks in and the selected chip shows a tick before its label, so the choice shows by more than color. A removable chip does not toggle; its remove control calls back and the page takes it out of the set.
A chip given an address is a link: pressing it goes there. It is never selected and never removable.
Whether a chip is a toggle is decided by the selected prop being given at all. A filter or choice chip is always given it, true when chosen and false when not. A chip that only acts, such as a suggestion, leaves it out.
A toggle chip carries its tick whether it is on or off, and shows it while it is pressed, so a page built ahead of time can press a chip with a script of its own, setting aria-pressed alone, and get the same tick as when React turns it on.
In markup, a link chip is an a element carrying the same classes, so it looks the same and has the same hover and focus. The React wrapper's types refuse selected and onRemove on a chip given an address.
On a touch screen a chip that is pressed, and a remove cross, keep their size and take an invisible press area round them, out to the comfortable touch target; a removable chip takes the press on its cross alone. Where chips stand closer, as in a filter bar or a combobox, each area stops halfway to the next.
Accessibility
What it does for you
- A selectable chip is a real button with a pressed state exposed to assistive technology. Given
selectedas true or false, the wrapper marks it pressed or not pressed, so a screen reader says it is a toggle and whether it is on even while it is off. - The wrapper derives the pressed state from
selected; a page rendered by React does not set it itself. A page's own script that presses a chip setsaria-pressed, and the tick follows it. - A removable chip is plain text plus a real remove button, which the wrapper requires an accessible name for. The button is at least 24 by 24 pixels, the least a press needs, however small its cross.
- A chip given an address is a real link and is announced as one.
- The focus ring is the system rule on the chip. On the remove control it is drawn round the cross itself; on a brand or inverse chip it takes the chip's own text color, so it shows on the fill.
What you must do
- Leave
selectedout for a chip that only acts: it then says neither pressed nor not pressed. Give it for a filter, which must say whether it is on. - Give a removable chip's remove button an accessible name that says what is removed.
- Never make a chip both toggle and remove: that would need a button inside a button.
- Put a row of chips that are links in a navigation landmark or a named group, so the row says what its places are.
How to test
- Tab to a chip: the focus ring shows in both themes and in a forced-color mode, round the cross on a remove button. Press Enter, then Space: each toggles or acts once.
- With a screen reader, a selectable chip is read as a toggle button, on or off, a link chip as a link, and a remove button by what it removes.
- On a touch screen, the remove cross is easy to hit.
- With text at 200% and the page 320 pixels wide, the label stays whole.
Keyboard
| Key | What it does |
|---|---|
| Tab | Moves focus to the chip, then to its remove button when it has one. |
| Enter | Presses the chip or its remove button, or follows the chip when it is a link. |
| Space | Presses the chip or its remove button. A chip that is a link is not followed by Space. |
Source
The files the chip is made of. Its folder on GitHub (opens in a new tab) holds them with its wrapper and its document.
Stylesheet
/* smbk-chip — Chip. Figma "Chip" 4253:1311.
* A capsule with an optional avatar or tick, a label and an optional remove control, a gap between them.
* The label's own frame carries a further inset on both sides, as Figma's label frame does. Figma draws
* the edge inside the chip, so the padding gives the edge's width back; the default tone has no edge of
* its own, and draws a clear one so it keeps the same size.
* On is the toggle chip's aria-pressed, as the toggle button's is, so the look and what a screen reader hears
* agree; the selected class draws the same, for markup that sets it. */
.smbk-chip {
box-sizing: border-box;
display: inline-flex;
align-items: center;
gap: var(--smbk-space-inline-sm);
height: var(--smbk-size-32);
padding: 0 calc(var(--smbk-space-inset-xs) - var(--smbk-border-width-hairline));
border: var(--smbk-border-width-hairline) solid var(--smbk-color-action-brand-border);
border-radius: var(--smbk-radius-full);
background: var(--smbk-color-action-brand-background);
color: var(--smbk-color-action-brand-foreground);
font-family: var(--smbk-typography-label-md-font-family);
font-size: var(--smbk-typography-label-md-font-size);
font-weight: var(--smbk-typography-label-md-font-weight);
line-height: var(--smbk-typography-label-md-line-height);
letter-spacing: var(--smbk-typography-label-md-letter-spacing);
white-space: nowrap;
text-decoration: none;
cursor: pointer;
-webkit-user-select: none;
user-select: none;
appearance: none;
-webkit-tap-highlight-color: transparent;
touch-action: manipulation;
}
.smbk-chip:focus-visible {
outline: var(--smbk-focus-ring-width) solid var(--smbk-color-border-focus);
outline-offset: var(--smbk-focus-ring-offset);
}
.smbk-chip:disabled,
.smbk-chip[aria-disabled="true"] {
cursor: not-allowed;
pointer-events: none;
}
.smbk-chip__label {
display: block;
padding: 0 var(--smbk-space-inset-sm);
}
.smbk-chip__avatar {
flex-shrink: 0;
}
.smbk-chip:disabled .smbk-chip__avatar,
.smbk-chip[aria-disabled="true"] .smbk-chip__avatar {
opacity: var(--smbk-opacity-disabled);
}
/* A selected chip shows a tick where the avatar would be, so the choice shows by more than its fill, and
a little more room before it. */
.smbk-chip__tick {
font-size: var(--smbk-size-icon-sm);
}
/* A toggle carries its tick off as well as on, so a page's script that sets aria-pressed shows it; it shows only while
the chip is on (aria-pressed, or the selected class), and then hides the avatar. */
.smbk-chip:not([aria-pressed="true"], .smbk-chip--selected) > .smbk-chip__tick,
.smbk-chip:is([aria-pressed="true"], .smbk-chip--selected) > .smbk-chip__tick ~ .smbk-chip__avatar {
display: none;
}
.smbk-chip:is([aria-pressed="true"], .smbk-chip--selected):has(> .smbk-chip__tick) {
padding-inline-start: calc(var(--smbk-space-inset-md) - var(--smbk-border-width-hairline));
}
/* The cross is the chip's icon size, inside a 24 px box, the least a press needs; the box reaches past the
cross into the gap and the padding, so the cross sits where Figma draws it. The focus ring is drawn on
the cross's own edge. */
.smbk-chip__remove {
box-sizing: border-box;
display: inline-flex;
flex-shrink: 0;
align-items: center;
justify-content: center;
width: var(--smbk-size-target-min);
height: var(--smbk-size-target-min);
margin: calc((var(--smbk-size-icon-sm) - var(--smbk-size-target-min)) / 2);
padding: 0;
border: 0;
background: none;
color: inherit;
cursor: pointer;
appearance: none;
-webkit-tap-highlight-color: transparent;
touch-action: manipulation;
}
.smbk-chip__remove > .smbk-icon {
font-size: var(--smbk-size-icon-sm);
border-radius: var(--smbk-radius-full);
}
.smbk-chip__remove:focus-visible {
outline: none;
}
.smbk-chip__remove:focus-visible > .smbk-icon {
outline: var(--smbk-focus-ring-width) solid var(--smbk-color-border-focus);
outline-offset: 0;
}
/* On the brand and inverse fills the focus color would not show, so the ring takes the chip's own text color. */
.smbk-chip--tone-brand .smbk-chip__remove:focus-visible > .smbk-icon,
.smbk-chip--tone-inverse .smbk-chip__remove:focus-visible > .smbk-icon {
outline-color: currentColor;
}
/* ---- size sm ---- */
.smbk-chip--size-sm {
height: var(--smbk-size-24);
font-family: var(--smbk-typography-label-sm-font-family);
font-size: var(--smbk-typography-label-sm-font-size);
font-weight: var(--smbk-typography-label-sm-font-weight);
line-height: var(--smbk-typography-label-sm-line-height);
letter-spacing: var(--smbk-typography-label-sm-letter-spacing);
}
.smbk-chip--size-sm .smbk-chip__label {
padding: 0 var(--smbk-space-inset-xs);
}
.smbk-chip--size-sm .smbk-chip__tick,
.smbk-chip--size-sm .smbk-chip__remove > .smbk-icon {
font-size: var(--smbk-size-icon-xs);
}
.smbk-chip--size-sm:is([aria-pressed="true"], .smbk-chip--selected):has(> .smbk-chip__tick) {
padding-inline-start: calc(var(--smbk-space-inset-sm) - var(--smbk-border-width-hairline));
}
.smbk-chip--size-sm .smbk-chip__remove {
margin: calc((var(--smbk-size-icon-xs) - var(--smbk-size-target-min)) / 2);
}
/* ---- tone: brand (default) ---- */
@media (hover: hover) {
.smbk-chip--tone-brand:hover {
background: var(--smbk-color-action-brand-background-hover);
border-color: var(--smbk-color-action-brand-border-hover);
box-shadow: var(--smbk-elevation-raised);
}
}
.smbk-chip--tone-brand:active,
.smbk-chip--tone-brand[aria-pressed="true"],
.smbk-chip--tone-brand.smbk-chip--selected {
background: var(--smbk-color-action-brand-background-pressed);
border-color: var(--smbk-color-action-brand-border-hover);
box-shadow: var(--smbk-elevation-inset-strong);
}
.smbk-chip--tone-brand:disabled,
.smbk-chip--tone-brand[aria-disabled="true"] {
background: var(--smbk-color-action-brand-background-disabled);
border-color: var(--smbk-color-action-brand-border-disabled);
color: var(--smbk-color-action-brand-foreground-disabled);
box-shadow: none;
}
/* ---- tone: default, the see-through gray with no edge ---- */
.smbk-chip--tone-default {
background: var(--smbk-color-surface-neutral-subtle);
border-color: transparent;
color: var(--smbk-color-action-default-foreground);
}
@media (hover: hover) {
.smbk-chip--tone-default:hover {
background: var(--smbk-color-surface-hover);
}
}
.smbk-chip--tone-default:active,
.smbk-chip--tone-default[aria-pressed="true"],
.smbk-chip--tone-default.smbk-chip--selected {
background: var(--smbk-color-action-default-background-pressed);
box-shadow: var(--smbk-elevation-inset-strong);
}
.smbk-chip--tone-default:disabled,
.smbk-chip--tone-default[aria-disabled="true"] {
background: var(--smbk-color-action-disabled-background);
color: var(--smbk-color-action-disabled-foreground);
box-shadow: none;
}
/* ---- tone: outline ---- */
.smbk-chip--tone-outline {
background: transparent;
border-color: var(--smbk-color-action-outline-border);
color: var(--smbk-color-action-outline-foreground);
}
@media (hover: hover) {
.smbk-chip--tone-outline:hover {
background: var(--smbk-color-action-outline-background-hover);
border-color: var(--smbk-color-action-outline-border-hover);
}
}
.smbk-chip--tone-outline:active,
.smbk-chip--tone-outline[aria-pressed="true"],
.smbk-chip--tone-outline.smbk-chip--selected {
background: var(--smbk-color-action-outline-background-pressed);
border-color: var(--smbk-color-action-outline-border-hover);
box-shadow: var(--smbk-elevation-inset-strong);
}
.smbk-chip--tone-outline:disabled,
.smbk-chip--tone-outline[aria-disabled="true"] {
background: transparent;
border-color: var(--smbk-color-action-disabled-border);
color: var(--smbk-color-action-outline-foreground-disabled);
box-shadow: none;
}
/* ---- tone: inverse ---- */
.smbk-chip--tone-inverse {
background: var(--smbk-color-action-inverse-background);
border-color: var(--smbk-color-action-inverse-border);
color: var(--smbk-color-action-inverse-foreground);
}
@media (hover: hover) {
.smbk-chip--tone-inverse:hover {
background: var(--smbk-color-action-inverse-background-hover);
border-color: var(--smbk-color-action-inverse-border-hover);
box-shadow: var(--smbk-elevation-raised);
}
}
.smbk-chip--tone-inverse:active,
.smbk-chip--tone-inverse[aria-pressed="true"],
.smbk-chip--tone-inverse.smbk-chip--selected {
background: var(--smbk-color-action-inverse-background-pressed);
border-color: var(--smbk-color-action-inverse-border-hover);
box-shadow: var(--smbk-elevation-inset);
}
.smbk-chip--tone-inverse:disabled,
.smbk-chip--tone-inverse[aria-disabled="true"] {
background: var(--smbk-color-action-disabled-background);
border-color: var(--smbk-color-action-disabled-border);
color: var(--smbk-color-action-disabled-foreground);
box-shadow: none;
}
/* ---- touch: a chip and its remove cross take invisible press areas ----
On a touch screen an invisible area, centred on a chip that is pressed and on a remove cross, takes the press out to
the comfortable target in each direction it falls short of it; a chip that only carries a remove cross takes the
press on the cross alone. The chip, its focus ring and its place stay as drawn. A component that sets chips closer
together stops their areas halfway to the next one. */
@media (pointer: coarse) {
:where(button, a[href]).smbk-chip {
position: relative;
}
:where(button, a[href]).smbk-chip::after {
content: '';
position: absolute;
inset: min(0%, (100% - var(--smbk-size-target-comfortable)) / 2);
}
.smbk-chip__remove {
position: relative;
}
.smbk-chip__remove::after {
content: '';
position: absolute;
inset: min(0%, (100% - var(--smbk-size-target-comfortable)) / 2);
}
}
/* ---- high contrast (forced colors) ---- */
/* In Windows high-contrast mode the browser drops fills and shadows; this puts the meaning back with the
user's own colors. Nothing here applies outside that mode. */
@media (forced-colors: active) {
/* A toggle chip that is on (aria-pressed, or the selected class) is filled with Highlight. */
.smbk-chip[aria-pressed="true"],
.smbk-chip.smbk-chip--selected {
forced-color-adjust: none;
border-color: Highlight;
background: Highlight;
color: HighlightText;
box-shadow: none;
}
.smbk-chip[aria-pressed="true"]:focus-visible,
.smbk-chip.smbk-chip--selected:focus-visible {
outline-color: CanvasText;
}
}Tokens it reads
| Token and CSS | Value | Preview | Foundation |
|---|---|---|---|
| space.inline.sm--smbk-space-inline-sm | comfortable: 6pxcompact: 4pxchanges with density | Spacing | |
| size.32--smbk-size-32 | 32px | Size | |
| space.inset.xs--smbk-space-inset-xs | 4px | Spacing | |
| border-width.hairline--smbk-border-width-hairline | 1px | Border width | |
| color.action.brand.border--smbk-color-action-brand-border | #ffffff1a | Color | |
| radius.full--smbk-radius-full | 9999px | Radius | |
| color.action.brand.background--smbk-color-action-brand-background | #cc0d0d | Color | |
| color.action.brand.foreground--smbk-color-action-brand-foreground | #f9fafb | Color | |
| typography.label-md--smbk-typography-label-md-font-family--smbk-typography-label-md-font-size--smbk-typography-label-md-font-weight--smbk-typography-label-md-line-height--smbk-typography-label-md-letter-spacing--smbk-typography-label-md-text-transform | fontFamily: Geist, Geist Fallback, system-ui, sans-seriffontSize: 14pxfontWeight: 500lineHeight: 1.4letterSpacing: 0px | The quick brown fox | Typography |
| focus.ring.width--smbk-focus-ring-width | 2px | Focus | |
| color.border.focus--smbk-color-border-focus | light: #cc0d0ddark: #f34336changes with theme | Color | |
| focus.ring.offset--smbk-focus-ring-offset | 2px | Focus | |
| space.inset.sm--smbk-space-inset-sm | comfortable: 6pxcompact: 4pxchanges with density | Spacing | |
| opacity.disabled--smbk-opacity-disabled | 0.5 | Opacity | |
| size.icon.sm--smbk-size-icon-sm | 16px | Size | |
| space.inset.md--smbk-space-inset-md | comfortable: 8pxcompact: 6pxchanges with density | Spacing | |
| size.target.min--smbk-size-target-min | 24px | Size | |
| size.24--smbk-size-24 | 24px | Size | |
| typography.label-sm--smbk-typography-label-sm-font-family--smbk-typography-label-sm-font-size--smbk-typography-label-sm-font-weight--smbk-typography-label-sm-line-height--smbk-typography-label-sm-letter-spacing--smbk-typography-label-sm-text-transform | fontFamily: Geist, Geist Fallback, system-ui, sans-seriffontSize: 12pxfontWeight: 500lineHeight: 1.4letterSpacing: 0px | The quick brown fox | Typography |
| size.icon.xs--smbk-size-icon-xs | 12px | Size | |
| color.action.brand.background-hover--smbk-color-action-brand-background-hover | #a51d17 | Color | |
| color.action.brand.border-hover--smbk-color-action-brand-border-hover | #0000000d | Color | |
| elevation.raised--smbk-elevation-raised | light: 0px 2px 8px 1px #373d431a, 0px 1px 2px 0px #373d431adark: 0px 2px 8px 1px #00000026, 0px 1px 2px 0px #00000026 | Elevation | |
| color.action.brand.background-pressed--smbk-color-action-brand-background-pressed | #761812 | Color | |
| elevation.inset-strong--smbk-elevation-inset-strong | light: inset -1px 2px 2px 0px #373d4333dark: inset -1px 2px 2px 0px #0000004d | Elevation | |
| color.action.brand.background-disabled--smbk-color-action-brand-background-disabled | light: #cc0d0d66dark: #cc0d0d80changes with theme | Color | |
| color.action.brand.border-disabled--smbk-color-action-brand-border-disabled | #0000000d | Color | |
| color.action.brand.foreground-disabled--smbk-color-action-brand-foreground-disabled | #ffffff4d | Color | |
| color.surface.neutral-subtle--smbk-color-surface-neutral-subtle | light: #373d431adark: #606a7533changes with theme | Color | |
| color.action.default.foreground--smbk-color-action-default-foreground | light: #2d3237dark: #e7ebf0changes with theme | Color | |
| color.surface.hover--smbk-color-surface-hover | light: #373d4326dark: #606a754dchanges with theme | Color | |
| color.action.default.background-pressed--smbk-color-action-default-background-pressed | light: #c4cdd8dark: #4e555echanges with theme | Color | |
| color.action.disabled.background--smbk-color-action-disabled-background | light: #373d4314dark: #606a751achanges with theme | Color | |
| color.action.disabled.foreground--smbk-color-action-disabled-foreground | light: #7c8692dark: #606a75changes with theme | Color | |
| color.action.outline.border--smbk-color-action-outline-border | light: #c4cdd8dark: #373d43changes with theme | Color | |
| color.action.outline.foreground--smbk-color-action-outline-foreground | light: #2d3237dark: #c4cdd8changes with theme | Color | |
| color.action.outline.background-hover--smbk-color-action-outline-background-hover | light: #373d431adark: #606a751achanges with theme | Color | |
| color.action.outline.border-hover--smbk-color-action-outline-border-hover | light: #9ba5b1dark: #4e555echanges with theme | Color | |
| color.action.outline.background-pressed--smbk-color-action-outline-background-pressed | light: #c4cdd8dark: #2d3237changes with theme | Color | |
| color.action.disabled.border--smbk-color-action-disabled-border | light: #c4cdd8dark: #2d3237changes with theme | Color | |
| color.action.outline.foreground-disabled--smbk-color-action-outline-foreground-disabled | light: #9ba5b1dark: #606a75changes with theme | Color | |
| color.action.inverse.background--smbk-color-action-inverse-background | light: #2d3237dark: #f3f5f8changes with theme | Color | |
| color.action.inverse.border--smbk-color-action-inverse-border | light: #373d43dark: #f9fafbchanges with theme | Color | |
| color.action.inverse.foreground--smbk-color-action-inverse-foreground | light: #f9fafbdark: #232527changes with theme | Color | |
| color.action.inverse.background-hover--smbk-color-action-inverse-background-hover | light: #232527dark: #e7ebf0changes with theme | Color | |
| color.action.inverse.border-hover--smbk-color-action-inverse-border-hover | light: #2d3237dark: #9ba5b1changes with theme | Color | |
| color.action.inverse.background-pressed--smbk-color-action-inverse-background-pressed | light: #181a1cdark: #c4cdd8changes with theme | Color | |
| elevation.inset--smbk-elevation-inset | light: inset 0px 0px 1px 0px #373d434ddark: inset 0px 0px 1px 0px #00000066 | Elevation | |
| size.target.comfortable--smbk-size-target-comfortable | 44px | Size |
Manifest
{
"name": "chip",
"block": "smbk-chip",
"summary": "A small selectable token — a filter, a choice, a person — that can carry an avatar and be removed.",
"adapt": "css",
"axes": {
"tone": {
"values": [
"default",
"brand",
"outline",
"inverse"
],
"default": "brand",
"description": "Which action family the chip draws from; default is a see-through gray that shows on every surface."
},
"size": {
"values": [
"md",
"sm"
],
"default": "md",
"description": "The pill scale shared with the badge and the tag: 32 and 24 tall."
}
},
"flags": {
"selected": {
"description": "The chip is currently chosen: pressed in, with a tick before its label. Given as true or false, the chip is a toggle and announces pressed or not pressed; left out, it is a plain button."
}
},
"parts": [
"tick",
"avatar",
"label",
"remove"
],
"figmaNodeId": "4253:1311"
}