Labels and icons that change
Controls whose text or icon changes in place.
When a control's result shows on the control itself — Copy becoming Copied, Sync becoming Synced — it changes in place, keeps its size, and is announced to screen readers.
The problem
Some actions finish at once and have nothing else to show: Copy, Copy invite link, Sync now. If the control doesn't change, people click again and still aren't sure it worked. If only an icon changes, screen-reader users miss it. And a "Copied" that shows when the copy failed is worse than nothing.
What to do
- Show the result on the control. Copy becomes "Copied" with a green check for about 1.5 seconds, then goes back. Sync goes Sync now → Syncing… → Synced.
- Keep it in view. A menu row that shows its own result keeps the menu open. If the control disappears after the click (a menu that closes), confirm with a toast instead.
- Say it out loud, and only if it's true. The change is announced through a polite live region. A copy that failed shows as an error, never as "Copied".
Which one do I use?
| Situation | Do this | Bridge example |
|---|---|---|
| Copy an email or phone number | CopyField: the row's Copy hint becomes "Copied" | Profile contact info |
| Copy a link from a button | The button's label and icon change in place | Copy invite link → Copied |
| Copy from a ⋮ menu | closeOnClick={false} on the row, so the menu stays open and the row shows the result | Copy profile link → Copied |
| The menu closes when you choose (a split button's caret) | A Toast confirms instead, since the row is gone | Intro link copied to clipboard |
| The copy failed | An error toast, never "Copied" | Couldn't copy the intro link. Try again. |
| Sync with a visible label | A ghost Button: refresh → spinner → check | Sync now → Syncing… → Synced |
| Sync as an icon only | The refresh IconButton; the state is in its label (Live and running states) | Rail calendar line |
| The result lasts (sharing) | Not a flash: the control shows the new state from now on | Share network → Invited |
See it work
Live examples built from the package on the sample network. Click through them.
Copy a contact detail
Click a row: "Copy" becomes "Copied" with a check, then goes back. Screen readers hear "Copied".
Sync now, Syncing…, Synced
The label follows the work, then settles back.
What to write
| Part | Rule | Example |
|---|---|---|
| Before | Verb and object. | Copy invite link · Copy profile link · Sync now |
| Copied | One word, past tense, no exclamation mark. | Copied |
| While running | The verb in -ing with an ellipsis. | Syncing… |
| Done | Past tense, one word. | Synced |
| Toast instead | Past tense and object, under 10 words. | Intro link copied to clipboard |
| Failed | "Couldn't" and what to do. | Couldn't copy the intro link. Try again. |
| Lasting state | The state, not the action you just took. | Invited |
Approved, not built yet
- A control that changes its label keeps a fixed width, so the row doesn't shift.
Accessibility
- The new label is announced through a polite live region (CopyField does this for you; do the same for your own buttons).
- The control keeps its own name: CopyField is "Copy email: leah.okonkwo@saltmarsh.example" whatever it shows.
- The check is decorative; the word carries the meaning, so color is never the only signal.
- Keeping the menu open leaves focus on the row that changed, so keyboard users see the result too.
| Keys | What happens |
|---|---|
| Enter / Space | Copies, or starts the sync. |
| ↑ / ↓ | Moves between menu rows. |
| Esc | Closes the menu. |
Components this uses
Each opens that component’s own page, with its options and code.
| Component | Its job here |
|---|---|
| CopyField | An email or phone number whose action is Copy, with "Copied" built in and announced. |
| ActionMenu | closeOnClick={false} on a row that shows its own result. |
| Button | Copy invite link (secondary) and a labeled Sync (ghost, xs, loading). |
| IconButton | Icon-only Sync: refresh → spinner → check. |
| Toast | Confirms a copy when the control that did it has gone. |
Where Bridge OS differs today
Follow this page, not the gap.
- Bridge OS writes "Copied!" with an exclamation mark in two places and "Copied" in another; it should be one (exclamation marks are still undecided).
- How long "Copied" stays varies: 1.5 seconds in CopyField, 2 seconds for Copy profile link and Copy invite link.
- The profile menu's "Copied" check hard-codes its green (
#0EA02E) instead of the success token.
Sources
- Bridge OS
copy-field.tsx - components.md → ActionMenuItem
- Bridge OS
calendar-sync-status.tsx - Package CopyField
- Bridge OS
connectors-tab.tsx(invites tab) - Bridge OS
profile-tab.tsx - Bridge OS
profile-intro-actions.tsx; components.md → ButtonGroup / Copy intro link - Bridge OS
hooks/use-copy-intro-link.ts - Bridge OS
calendar-sync-status.tsx(CalendarSyncButton) - Bridge OS
recently-met-rail.tsx(sharing states comment) - Handbook Voice and tone page
- Proposals approved by Aman 2026-11-02.