OpenParable
Skip to content
← Back to skills

Shade use primitives

Development

Shade use primitives is an Agent Skill for development published in the TryGhost/Ghost repository on GitHub: a SKILL.md instruction file an AI agent can load to do this task.

Replace bare divs that only carry flex/grid/gap utilities with Shade primitives (Stack, Inline, Box, Grid, Container, Text). Use semantic gap="md" instead of gap-4. Trigger when editing TSX in Shade-consuming apps.

View on GitHub ↗
QUICK START

How to use this skill

Bring this guide into your coding agent with a prompt tailored to the tool you use.

  1. Open your project in Codex.
  2. Copy and paste the prompt below.
  3. Review files and risks before approving any action.
Prompt to paste
Inspect the Agent Skill at https://github.com/TryGhost/Ghost/blob/HEAD/.agents/skills/shade-use-primitives/SKILL.md and all supporting files. Report provenance, permissions, scripts, dependencies, and security risks. Propose an exact installation into .agents/skills/shade-use-primitives, but wait for my approval before writing files or running scripts.

Copying this prompt does not install or run the skill. Review third-party files before use.

Shade — use primitives, not flex divs

A <div> whose className is only flex / grid / gap / items-* / justify-* utilities is a primitive call site. Use a Shade primitive instead so layouts read by intent, not by class string.

import {Stack, Inline, Box, Grid, Container, Text} from '@tryghost/shade/primitives';

Picking the primitive

You needUseElement
Column of childrenStackflex-col
Row of childrenInlineflex-row (with optional wrap, as)
Padding / radius around contentBoxdiv with padding, radius props
Two-dimensional layoutGriddisplay: grid
Width-constrained shellContainermax-width wrapper
Text with size/tone/weightTextas, size, tone, weight

Use the semantic gap scale, not raw numbers

gap="md" reads as intent and resolves to the shared spacing scale. Mapping:

StepTailwindUse for
nonegap-0flush
xsgap-1inline icons-to-text
smgap-2dense lists, badges
mdgap-3default row/column spacing
lggap-5section spacing
xlgap-6between major blocks
2xlgap-8page-level rhythm

The same scale applies to Box padding (padding="md" → p-3).

Correct

<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 about engagement.</Text>
        </Stack>
        <Switch />
    </Inline>
</Box>

Incorrect

// BAD — bare div doing flex layout
<div className="flex flex-col gap-2">
    <div className="font-semibold">Title</div>
    <div className="text-sm text-gray-600">Hint</div>
</div>

// BAD — raw gap-4 instead of gap="md"
<Stack className="gap-4">

When NOT to reach for a primitive

  • A pattern already exists for the shape (page header → PageHeader, list page → ListPage).
  • The wrapper is starting to know about Ghost data — that's a Pattern, not a primitive composition.
  • You're inside a Shade primitive's own implementation.

className still works

Primitives forward className and merge with cn(). Use it for one-offs the prop API doesn't cover (e.g. className="border border-border-default"). Don't reach for className to set flex, gap, align, or justify — that's what the props are for.