Empty
What a region shows when it loaded and has nothing: why, and what to do next.
In Brand expressive
See it live
npm run storybookWhen to use it
In Brand expressive: A region that loaded with nothing, in playful words with a clear next step. Not while images load; skeleton does that.
- Use it when: An empty list, a search with no results, or a first visit with no items yet.
- Not when: Still loading (skeleton or spinner), or a load that failed (alert).
Instead:
Variants
What each variant is for in any design system, then what Brand expressive decided and why. Each sample is the live story with that variant set.
defaultvariantUsed in Brand expressiveWhat it is for
The picture shown as it is: an image or illustration.
In Brand expressive
A campaign image or illustration beside the playful words and a clear next step.
iconvariantUsed in Brand expressiveWhat it is for
The picture as an icon in a small tile.
In Brand expressive
A two-tone icon in a tile, for a small region where a picture would push the words away.
How Brand expressive uses it
Why it works this way
Empty looks the way it does because of 4 answers about this product. Each card is one answer, in three steps: the answer, what it decided for Empty, and what would move if it changed.
content.q11The answer
Images, campaigns and short punchy copy.
2So Empty
- ✓uses default: A campaign image or illustration beside the playful words and a clear next step.
Badge: info Input group: block-start Input group: block-end3If it changed
and 10 answers that follow from it: One idea at a time, Browse and explore, Regular, and 7 more.
human.q11The answer
Delight is the point.
2So Empty
- ✓uses default: A campaign image or illustration beside the playful words and a clear next step.
3If it changed
and 1 answer that follow from it: Fast.
iconography.definition1The answer
Two-tone icons carry the brand color.
2So Empty
- ✓uses icon: A two-tone icon in a tile, for a small region where a picture would push the words away.
3If it changed
No other answer follows from it.
iconography.roles1The answer
Icons also decorate campaign pages.
2So Empty
- ✓uses icon: A two-tone icon in a tile, for a small region where a picture would push the words away.
Button: ghost Button: icon Input group: inline-start3If it changed
and 1 answer that follow from it: Two.
Words
- Headlines are short and bold. Three to six words.
- Verbs that move: Join, Try, Go.
- Playful in headlines, plain in errors.
- Use: Join, Drop, Go, Love. Avoid: Synergy, Leverage, Kindly.
The labels components use, from the voice tokens:
content.action.commit: Gocontent.action.cancel: Not nowcontent.action.destroy: Removecontent.action.retry: Try againcontent.action.empty-action: Explore the drop
See Voice and tone for the whole voice.
Use it in code
npx open-ds add empty
The component reads only component keys and semantic tokens, so it follows the brand and mode on <html>.
Tokens it reads
Empty reads 15 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 Empty's tokens to change Empty alone.
Brand expressive 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 |
|---|---|---|
| Empty | ||
| Gap | component.empty.gapspace.gap.sectionvar(--empty-gap) | space.64:4rem |
| Corner | component.empty.radiusradius.containervar(--empty-radius) | radius.24:1.5rem |
| Padding | component.empty.paddingcomponent-spacing-map.x6var(--empty-padding) | space.48:3rem |
| Padding 2 | component.empty.padding-2component-spacing-map.x12var(--empty-padding-2) | space.64:4rem |
| Header | ||
| Gap | component.empty.header.gapspace.gap.defaultvar(--empty-header-gap) | space.16:1rem |
| Icon | ||
| Corner | component.empty.icon.radiusradius.containervar(--empty-icon-radius) | radius.24:1.5rem |
| Fill Changes with theme | component.empty.icon.backgroundcolor.surface.sunkenvar(--empty-icon-background) | Lighttaupe.light.100:#FCEFE6Darktaupe.dark.100:#33241F |
| Text Changes with theme | component.empty.icon.labelcolor.text.primaryvar(--empty-icon-label) | Lighttaupe.light.900:#1F1411Darktaupe.dark.900:#FFF8F3 |
| Title | ||
| Text size | component.empty.title.font-sizetype.heading.font-sizevar(--empty-title-font-size) | font.size.28:1.75rem |
| Weight | component.empty.title.font-weighttype.label.font-weightvar(--empty-title-font-weight) | font.weight.700:700 |
| Description | ||
| Text size | component.empty.description.font-sizetype.body.font-sizevar(--empty-description-font-size) | font.size.17:1.0625rem |
| Text Changes with theme | component.empty.description.labelcolor.text.mutedvar(--empty-description-label) | Lighttaupe.light.600:#6B544ADarktaupe.dark.400:#B5988A |
| Label 2 Changes with theme | component.empty.description.label-2color.action.primaryvar(--empty-description-label-2) | Lightpink.light.600:#D61F7ADarkpink.dark.600:#F59ACA |
| Content | ||
| Gap | component.empty.content.gapspace.gap.groupvar(--empty-content-gap) | space.40:2.5rem |
| Text size | component.empty.content.font-sizetype.body.font-sizevar(--empty-content-font-size) | font.size.17:1.0625rem |