Item
A row with media, a title, a description and actions.
In Accessible first
See it live
npm run storybookWhen to use it
In Accessible first: A list such as saved applications, each with worded secondary buttons. Not records compared cell by cell.
- Use it when: Lists of things such as people, files or settings, each with a few actions.
- Not when: Records people compare cell by cell (a table).
Instead:
Variants
What each variant is for in any design system, then what Accessible first decided and why. Each sample is the live story with that variant set.
defaultvariantNot used in Accessible firstWhat it is for
No border and no fill: the row sits on the surface around it.
In Accessible first
A row with no border is hard to see at high zoom; borders do the work.
outlinevariantUsed in Accessible firstWhat it is for
A border around each row.
In Accessible first
Each saved application, with a strong border that separates it at any zoom.
mutedvariantNot used in Accessible firstWhat it is for
A soft fill behind the row.
In Accessible first
A fill alone marks a row by color; status goes in a badge with an icon and a word.
defaultsizeUsed in Accessible firstWhat it is for
Comfortable padding.
In Accessible first
Every row. Large targets help people with motor needs.
smsizeNot used in Accessible firstWhat it is for
Tighter padding.
In Accessible first
Large targets help people with motor needs; nothing smaller.
Why it works this way
Item looks the way it does because of 5 answers about this product. Each card is one answer, in three steps: the answer, what it decided for Item, and what would move if it changed.
elevation.definition1The answer
Strong borders separate groups at any zoom.
2So Item
- –does not use default: A row with no border is hard to see at high zoom; borders do the work.
- ✓uses outline: Each saved application, with a strong border that separates it at any zoom.
Toggle: default Toggle: outline3If it changed
No other answer follows from it.
elevation.values1The answer
No shadows; borders do the work.
2So Item
- –does not use default: A row with no border is hard to see at high zoom; borders do the work.
- ✓uses outline: Each saved application, with a strong border that separates it at any zoom.
3If it changed
No other answer follows from it.
accessibility.count1The answer
Never color alone.
2So Item
- –does not use muted: A fill alone marks a row by color; status goes in a badge with an icon and a word.
Alert: destructive Badge: neutral Badge: success Badge: warning Badge: danger3If it changed
and 1 answer that follow from it: Status and messages.
accessibility.values1The answer
Large targets for tremor and touch.
2So Item
- ✓uses default: Every row. Large targets help people with motor needs.
- –does not use sm: Large targets help people with motor needs; nothing smaller.
Button: default Button: compact Button: large Toggle: default Toggle: sm Toggle: lg3If it changed
No other answer follows from it.
spacing.count1The answer
One density; no compact mode to fail people.
2So Item
- –does not use sm: Large targets help people with motor needs; nothing smaller.
3If it changed
No other answer follows from it.
Words
- Use common words. Explain any term you must use.
- One idea per sentence. Put the action first.
- Never blame the person.
- Use: Continue, Change, Start, You. Avoid: Invalid, Utilise, Submit, Error code.
The labels components use, from the voice tokens:
content.action.commit: Continuecontent.action.cancel: Go backcontent.action.destroy: Deletecontent.action.retry: Try againcontent.action.empty-action: Start a new application
See Voice and tone for the whole voice.
Use it in code
npx open-ds add item
The component reads only component keys and semantic tokens, so it follows the brand and mode on <html>.
Tokens it reads
Item reads 32 tokens of its own, each an alias of a semantic token. Each row is one part: its own token, the semantic it aliases, and the value it ends at. Change a semantic and every component that reads it follows; override one of Item's tokens to change Item alone.
Accessible first decides what changes when you switch theme. Here a switch changes colors only, so the marked rows differ between light and dark; everything else holds steady.
| Part | Its token and the semantic it aliases | Value |
|---|---|---|
| Item | ||
| Corner | component.item.radiusradius.controlvar(--item-radius) | radius.6:0.375rem |
| Text size | component.item.font-sizetype.body.font-sizevar(--item-font-size) | font.size.19:1.1875rem |
| Speed | component.item.durationmotion.duration.feedbackvar(--item-duration) | duration.moderate:200ms |
| Border focus Changes with theme on focus | component.item.border-focuscolor.border.focusvar(--item-border-focus) | Lightblue.light.600:#0B4FC0Darkblue.dark.600:#8AB4F8 |
| Background hover Changes with theme on hover | component.item.background-hovercolor.surface.sunkenvar(--item-background-hover) | Lightgray.light.100:#EDEDEDDarkgray.dark.100:#222222 |
| Focus on focus | component.item.focusborder-width.focusvar(--item-focus) | border-width.3:3px |
| Focus ring focus Changes with theme on focus | component.item.focus-ring-focuscolor.border.focusvar(--item-focus-ring-focus) | Lightblue.light.600:#0B4FC0Darkblue.dark.600:#8AB4F8 |
| Focus ring offset focus Changes with theme on focus | component.item.focus-ring-offset-focuscolor.surface.canvasvar(--item-focus-ring-offset-focus) | Lightgray.light.50:#F7F7F7Darkgray.dark.0:#0A0A0A |
| Gap | component.item.gapspace.gap.defaultvar(--item-gap) | space.16:1rem |
| Outline | ||
| Border Changes with theme | component.item.outline.bordercolor.border.defaultvar(--item-outline-border) | Lightgray.light.200:#D6D6D6Darkgray.dark.100:#222222 |
| Muted | ||
| Fill Changes with theme | component.item.muted.backgroundcolor.surface.sunkenvar(--item-muted-background) | Lightgray.light.100:#EDEDEDDarkgray.dark.100:#222222 |
| Default | ||
| Gap | component.item.default.gapspace.gap.groupvar(--item-default-gap) | space.32:2rem |
| Padding | component.item.default.paddingspace.inset.containervar(--item-default-padding) | space.24:1.5rem |
| Sm | ||
| Gap | component.item.sm.gapcomponent-spacing-map.x3var(--item-sm-gap) | space.20:1.25rem |
| Padding, left and right | component.item.sm.padding-xspace.inset.containervar(--item-sm-padding-x) | space.24:1.5rem |
| Padding, top and bottom | component.item.sm.padding-ycomponent-spacing-map.x3var(--item-sm-padding-y) | space.20:1.25rem |
| Icon | ||
| Size | component.item.icon.sizesize.control.defaultvar(--item-icon-size) | size.52:3.25rem |
| Corner | component.item.icon.radiusradius.badgevar(--item-icon-radius) | radius.4:0.25rem |
| Fill Changes with theme | component.item.icon.backgroundcolor.surface.sunkenvar(--item-icon-background) | Lightgray.light.100:#EDEDEDDarkgray.dark.100:#222222 |
| Icon size | component.item.icon.icon-sizesize.iconvar(--item-icon-icon-size) | size.20:1.25rem |
| Image | ||
| Corner | component.item.image.radiusradius.badgevar(--item-image-radius) | radius.4:0.25rem |
| Content | ||
| Gap | component.item.content.gapspace.gap.tightvar(--item-content-gap) | space.12:0.75rem |
| Title | ||
| Gap | component.item.title.gapspace.gap.defaultvar(--item-title-gap) | space.16:1rem |
| Text size | component.item.title.font-sizetype.body.font-sizevar(--item-title-font-size) | font.size.19:1.1875rem |
| Weight | component.item.title.font-weighttype.label.font-weightvar(--item-title-font-weight) | font.weight.700:700 |
| Description | ||
| Text size | component.item.description.font-sizetype.body.font-sizevar(--item-description-font-size) | font.size.19:1.1875rem |
| Weight | component.item.description.font-weighttype.body.font-weightvar(--item-description-font-weight) | font.weight.400:400 |
| Text Changes with theme | component.item.description.labelcolor.text.mutedvar(--item-description-label) | Lightgray.light.500:#595959Darkgray.dark.500:#949494 |
| Label 2 Changes with theme | component.item.description.label-2color.action.primaryvar(--item-description-label-2) | Lightblue.light.600:#0B4FC0Darkblue.dark.600:#8AB4F8 |
| Actions | ||
| Gap | component.item.actions.gapspace.gap.defaultvar(--item-actions-gap) | space.16:1rem |
| Header | ||
| Gap | component.item.header.gapspace.gap.defaultvar(--item-header-gap) | space.16:1rem |
| Footer | ||
| Gap | component.item.footer.gapspace.gap.defaultvar(--item-footer-gap) | space.16:1rem |
| From the system | ||
| Edge Changes with theme | color.border.default straight from the system, through --border | Lightgray.light.200 · #D6D6D6Darkgray.dark.100 · #222222 |