Back to Ghost

Primitives

apps/shade/src/docs/primitives-guide.mdx

6.38.01.9 KB
Original Source

import { Meta } from '@storybook/addon-docs/blocks';

<Meta title="Primitives / Primitives Guide" /> <div className="sb-doc">

Primitives

<p className="excerpt">Layout vocabulary. `Stack`, `Inline`, `Box`, `Grid`, `Container`, `Text`. Use these instead of writing `flex flex-col gap-4` so layouts read by intent.</p>

Use a primitive when

  • You need a column of children → Stack
  • You need a row → Inline
  • You need padding or a radius around content → Box
  • You need a width-constrained shell → Container
  • You need a two-dimensional layout → Grid
  • You're rendering text with a size/tone/weight → Text

Use semantic gaps (gap="md"), not raw numbers (gap-4). Map: see Tokens / Spacing.

Don't

  • Reach for a pattern first if one exists. Building a page header? Use PageHeader. Building a list page? Use ListPage.
  • Put product logic inside a primitive. If a wrapper is starting to know about Ghost data, it's a Pattern.
  • Wrap content in a bare <div> carrying only flex / grid / gap utilities — that's exactly what primitives replace.

Full rules and decision flow: Layers. Agent rules: apps/shade/AGENTS.md.

Example

A settings row — label and description on the left, control on the right:

tsx
import {Switch} from '@tryghost/shade/components';
import {Box, Inline, Stack, Text} from '@tryghost/shade/primitives';

<Box padding="lg" radius="md" className="border border-border-default">
    <Inline align="center" gap="md" justify="between">
        <Stack gap="xs">
            <Text weight="semibold">Email notifications</Text>
            <Text size="sm" tone="secondary">Get notified when your posts get engagement.</Text>
        </Stack>
        <Switch />
    </Inline>
</Box>

Browse the sidebar for each primitive's props and live stories.

</div>