Person chip
An interactive member chip combining an avatar with a name. Used in the WITH row of the expense form to select who's in a split. Chips toggle selection on tap, show a dismiss button when selected, and go into a settled state when a member has fully paid up.
Variants
Default — unselected, tappable. Paper surface with hairline border. Shows avatar and name. No remove button.
Selected — teal surface with teal border. The avatar turns teal. A remove button (×) appears to the right of the name, letting the user deselect without tapping the whole chip.
Settled — muted, non-interactive. Strikethrough on the name, desaturated avatar. Communicates that this person's balance is zero and they can't be added to a new split.
Sizes
| Size | Avatar | Font | Padding | Use case |
|---|---|---|---|---|
| Small | 22px | 12px | 4px 10px 4px 4px | Dense lists, compact split rows |
| Default | 28px | 14px | 6px 12px 6px 6px | Expense form WITH row — standard |
| Large | 34px | 15px | 8px 16px 8px 8px | Full-screen member picker |
States
| State | Visual change | Transition |
|---|---|---|
| Default | Paper surface, hairline border | — |
| Hover | Background shifts to --s-warm, text to --ink | 140ms ease-out |
| Pressed | scale(0.97) | 140ms ease-out |
| Focused | 2px outline in --teal, 2px offset | Instant |
| Selected | Teal surface + border, × button appears | 140ms ease-out |
| Settled | Muted, strikethrough name, non-interactive | — |
On dark surfaces
On dark surfaces (moments section, dark cards), the chip uses the elevated dark surface with a dark border. The selected teal state stays consistent — low-opacity teal wash with a teal border ring. The settled chip becomes even more recessed at reduced opacity.
Group layout
Use .person-chip-group as a flex-wrap container with 8px gap. Selected chips bubble to the front via JS — the DOM order should reflect selection order. Settled chips always go last.
Avatar colors
Avatar colors cycle through the avatar palette. The logged-in user always gets --av-you (ink). Colors are assigned deterministically by user ID so a person always has the same color across sessions and devices.
Anatomy
| Part | Element | Notes |
|---|---|---|
| Container | div[role="button"] or button | Pill shape. Handles the toggle click. Entire chip is the tap target. |
| Avatar | .person-chip__avatar | 28px circle. Initials, 2 chars max. Color from palette. |
| Name | .person-chip__name | First name only. Strikethrough in settled state. |
| Remove button | .person-chip__remove | Only visible in selected state. Separate tap target from chip toggle. Requires aria-label="Remove [name]". |
Usage
Accessibility
Tab. Space or Enter toggles selection. When selected, the remove button (×) is a separate focusable element. Tab from the chip body reaches the remove button.
<button> with aria-pressed="true|false" to announce selection state. The remove button needs aria-label="Remove [name]". Settled chips use aria-disabled="true" and should be skipped from the tab order.
Code
HTML
<!-- Default (unselected) --> <button class="person-chip" aria-pressed="false"> <div class="person-chip__avatar" aria-hidden="true">AA</div> <span class="person-chip__name">Aarav</span> </button> <!-- Selected (with remove button) --> <button class="person-chip person-chip--selected" aria-pressed="true"> <div class="person-chip__avatar" aria-hidden="true">PR</div> <span class="person-chip__name">Priya</span> <span class="person-chip__remove" role="button" aria-label="Remove Priya" tabindex="0">×</span> </button> <!-- Settled --> <div class="person-chip person-chip--settled" aria-disabled="true"> <div class="person-chip__avatar person-chip__avatar--mute" aria-hidden="true">RK</div> <span class="person-chip__name">Rohan</span> </div> <!-- Group layout --> <div class="person-chip-group" role="group" aria-label="Split with"> <!-- chips here --> </div>
CSS classes
| Class | Purpose |
|---|---|
.person-chip | Base — pill shape, avatar + name layout, press state |
.person-chip--selected | Teal surface, teal border, shows remove button |
.person-chip--settled | Muted, strikethrough name, non-interactive |
.person-chip__avatar | 28px circle, initials |
.person-chip__avatar--coral | Coral avatar color |
.person-chip__avatar--teal | Teal avatar color |
.person-chip__avatar--butter | Butter avatar color |
.person-chip__avatar--mute | Muted avatar color |
.person-chip__name | Name text — gets strikethrough in settled state |
.person-chip__remove | × dismiss button — shown only in selected state |
.person-chip-group | Flex-wrap container with 8px gap |
Design tokens used
| Token | Value | Role |
|---|---|---|
--teal-pale | #D6EAE2 | Selected chip background |
--teal | #0E7C66 | Selected chip border, selected avatar |
--teal-deep | #0A5F4F | Selected chip text |
--s-warm | #F1E9D5 | Settled chip background, hover |
--ink-line | #DDD2B8 | Default chip border |
--ink-mute | #8A8276 | Settled text + avatar |
--r-pill | 999px | Chip border radius |