The name of a data field or a source file, wherever one is shown: a small pill that reads as a label, never a value, so a reader never mistakes the two. The third of the kit's three small-tag species: StatusPill/StatusDot communicate STATE, Chip communicates SELECTION, FieldChip communicates NAMING (docs/design-language/03-status-language.md §A1, the same line quoted in chip.md and status.md).
The canon behind it. docs/design-language/03-status-language.md §A1 (03-status-language.md:13, the three-species line quoted above) and docs/design-language/01-foundations.md §A1 (01-foundations.md:13, only the semantic layer knows about themes, the exact rule this component's own dark-mode bug taught).
When to reach for it#
Naming a schema field key (partType, brand, upc) or a source file, anywhere that name appears as a label rather than as content. Do not reach for it to show a record's status, that is StatusPill/StatusDot, and not for a selectable or removable value, that is Chip.
Contract#
FieldChip's own contract is deliberately narrow, much narrower than Chip's: there is no size, variant, Icon, selected, or removable mode.
children: the field key text, or any node.tone:FieldChipTone, a two-way union."field"(default) is a quiet neutral pill (bg-base-200 text-base-content/70);"file"is a warm pill (bg-warning/10 text-warning), because "a file is a thing you can open, so it reads as its own kind" (field-chip.tsx:22-23).onClick: when given, the chip renders as a real<button>; when omitted, a plain non-interactive<span>.className.
Both tones use theme tokens rather than raw palette literals on purpose: an earlier version used raw slate/amber colors that never flipped with the theme and drew white pills in dark mode (field-chip.tsx:19-21).
Types are exported from field-chip.tsx itself rather than a sibling .types.ts (FieldChipProps, FieldChipTone, index.ts); this is the one component in this batch that does not follow the kit's usual file split.
App-local wrappers#
Because the contract is this narrow, speedway ships two app-local components that wrap FieldChip rather than extending it:
FileChip(speedway/app/components/file-chip.tsx), built ontone="file": adds a leading file icon, a trailing external-link icon, click-to-preview wiring through a modal hook, and a custommidTruncatethat keeps a filename's start and extension and ellipsizes the middle, because CSS end-truncation eats exactly the part that tells two vendor exports apart (file-chip.tsx:74-80). Without a resolvable id it degrades to a plain non-clickable chip that "still reads correctly; it simply cannot be opened" (file-chip.tsx:29-30).WrappingFieldChip(speedway/app/components/wrapping-field-chip.tsx): wraps a plaintone="field"chip in an inner<span className="min-w-0 break-words">so long field names wrap inside atable-fixedcolumn instead of overflowing into the next one. The inner span is load-bearing:FieldChipisinline-flex, so a bare string child becomes an anonymous flex item whose defaultmin-width: autorefuses to shrink below its longest unbreakable word; only a real element you can putmin-w-0on lets the wrap actually happen (wrapping-field-chip.tsx:11-17).
Both are the shipped pattern for extending FieldChip's behavior; reach for one of them, or build a sibling the same way, before adding props to FieldChip itself.
Plain, unwrapped <FieldChip>{value}</FieldChip> with the default field tone is also common and legitimate: speedway/app/components/content-fields.tsx:121, speedway/app/components/ProductItem.tsx:336, speedway/app/routes/workspaces/jobs/run.tsx:319, speedway/app/routes/workspaces/review/log.tsx:517, walmart-mvp/frontend/src/features/parts/PartPreviewModal.tsx:221,266,623.
Sanctioned combinations#
| Combination | Produces | Where used | Why |
|---|---|---|---|
Plain <FieldChip>{key}</FieldChip>, default field tone, no onClick | A read-only field-name label in a key/value row or table cell | content-fields.tsx:121, ProductItem.tsx:336, PartPreviewModal.tsx:221 | The field name is informational only; nothing to click |
tone="file" plus an onClick that opens a preview modal, via the app-local FileChip | A filename that opens in place instead of navigating away | file-chip.tsx:33-72 | Per the source comment, leaving the part you are reading to go look at a column header is a bad trade (file-chip.tsx:28-29) |
className="min-w-0" plus an inner min-w-0 break-words span, via the app-local WrappingFieldChip | A field-name chip that wraps instead of overflowing a fixed-width table column | wrapping-field-chip.tsx | PIM field names run long and unbroken; a table-fixed grid cannot let one chip paint over the next column |
Banned combinations#
Do not use FieldChip to show a record's state (that is StatusPill / StatusDot) or a selectable, removable value (that is Chip). The three small-tag species are never interchangeable (docs/design-language/03-status-language.md:13); a field name in a status tone tells the reader the field is a state, which is the confusion the species exist to prevent.
Do not add size, variant, Icon, or a removable mode to FieldChip to get a richer chip. The narrow contract is deliberate; the shipped way to extend behaviour is an app-local wrapper (FileChip, WrappingFieldChip, above), or Chip when the thing is a value after all.
Do not put a raw palette colour on a chip tone. Both tones read theme tokens because an earlier version used raw slate/amber that never flipped with the theme and drew white pills in dark mode (field-chip.tsx:19-21).
Do not drop a bare <FieldChip> with a long field name into a table-fixed column and expect it to wrap. FieldChip is inline-flex, so a bare string child cannot shrink below its longest word and paints over the next column; wrap it (WrappingFieldChip, wrapping-field-chip.tsx:11-17).
Before you adopt this#
Five questions to answer before reaching for FieldChip.
- Does the shell, a parent layout, or a global provider already render this? Not applicable, FieldChip is inline content; no shell already renders it.
- Does this app already ship a local implementation of the same thing?
FileChip/WrappingFieldChipare the shipped app-local wrappers; extend one before adding props. - Does this app's kit pin reach the version this component or prop landed in? Not applicable, no version-landed FieldChip prop is noted in this doc.
- Does the component derive its own accessible name and keyboard path, or must the call site supply them? Not applicable, FieldChip has no keyboard path;
onClickalone makes it a button. - Which canon §A rules bind this surface, and which does the composition break? §A1 says the three small-tag species are never interchangeable.
Travels with#
Table, as the field-name cell in a schema/attribute row.IconFor, for the leading/trailing glyphs the app-localFileChipadds.
Snippet#
// speedway/app/components/wrapping-field-chip.tsxexport function WrappingFieldChip({ children }: { children: ReactNode }) { return ( <FieldChip className="min-w-0"> <span className="min-w-0 break-words">{children}</span> </FieldChip> );}