<!-- Generated from src/docs/introduction.mdx by scripts/build-docs.ts. Do not edit. -->

# Shade

Shade is the design system that powers Ghost Admin. Tokens, layout primitives, generic UI controls, and product-shaped patterns — all in one place so designers, engineers, and AI agents stop reinventing them.

## In one screen

- **[Tokens](./tokens.md)** — colour, type, spacing, radius, motion. Visual galleries in the sidebar.
- **[Primitives](./primitives-guide.md)** — `Stack`, `Inline`, `Box`, `Grid`, `Container`, `Text`. Layout, not chrome.
- **Components** — `Button`, `Input`, `Dialog`, `Tabs`… Generic and accessible. Built on Radix + ShadCN.
- **Patterns** — `PageHeader`, `KpiCard`, `Filters`. Product-shaped compositions.
- **Page templates** — `ListPage`, `DetailPage`. Top-level page wrappers that compose patterns.

The boundaries between those layers are the whole point. See **[Layers](https://github.com/TryGhost/Ghost/blob/main/apps/shade/CONTRIBUTING.md#layers)**, in Contributing to Shade, for the decision flow.

## Working in Shade

Shade is the **default source for Ghost Admin UI** and for apps built for Ghost. If a control exists, use it. If it doesn't and you'll reuse it inside Ghost Admin, [Layers](https://github.com/TryGhost/Ghost/blob/main/apps/shade/CONTRIBUTING.md#layers) tells you which folder to add it to.

See **[Contributing to Shade](https://github.com/TryGhost/Ghost/blob/main/apps/shade/CONTRIBUTING.md)** for the implementation and review checklist.

## Imports

Import from the subpath for the layer you need:

| Subpath                          | Contains                                                               |
| -------------------------------- | ---------------------------------------------------------------------- |
| `@tryghost/shade/primitives`     | `Stack`, `Inline`, `Box`, `Grid`, `Container`, `Text`                  |
| `@tryghost/shade/components`     | Generic controls: `Button`, `Input`, `Dialog`, `Table`…                |
| `@tryghost/shade/patterns`       | Product-shaped compositions: `PageHeader`, `KpiCard`, `Filters`…       |
| `@tryghost/shade/page-templates` | Page wrappers: `ListPage`, `DetailPage`                                |
| `@tryghost/shade/utils`          | `cn`, `formatNumber` and other helpers, and the `LucideIcon` namespace |
| `@tryghost/shade/app`            | `ShadeApp` and `useShade`                                              |
| `@tryghost/shade/tokens`         | Token names and the `shadeToken()` helper                              |

```tsx
import {Stack} from '@tryghost/shade/primitives';
import {Button} from '@tryghost/shade/components';
import {PageHeader} from '@tryghost/shade/patterns';
import {ListPage} from '@tryghost/shade/page-templates';
import {cn, formatNumber} from '@tryghost/shade/utils';
```

Page templates have their own subpath: `ListPage` comes from `@tryghost/shade/page-templates`, not `/patterns`. Page templates are top-level page wrappers; patterns are the building blocks they compose.

Never import from the root `@tryghost/shade` barrel or from a deep path into the package, such as `@tryghost/shade/src/components/ui/button`. The subpath makes the layer visible at the call site, which is the first thing a reader needs; the root barrel hides it and encourages cross-layer drift. Inside Shade itself, imports follow [Contributing to Shade](https://github.com/TryGhost/Ghost/blob/main/apps/shade/CONTRIBUTING.md#imports).

## Embedded and standalone setup

Where Shade's stylesheet and `ShadeApp` come from depends on who owns the document:

- **Embedded Admin apps** are bundled into Ghost Admin and render in Admin's own document. Admin owns the single stylesheet and application wrapper, so these apps import neither `@tryghost/shade/styles.css` nor `ShadeApp`; they only import components.
- **Standalone surfaces** own their document. This includes a Ghost app running in an iframe inside Admin, because an iframe doesn't inherit Admin's styles. They import `@tryghost/shade/styles.css` once, in the app stylesheet, and wrap the app in `ShadeApp`.

To set up a standalone surface, follow [Using Shade in a Ghost app](./ghost-apps.md): install, Tailwind, fonts, `ShadeApp`, theme and page frame.

## Admin 7 milestones

Shade previews use the new controls by default. New screens use ordinary
`Button`, `SelectTrigger`, `InputGroup`, `Badge`, `FilterBar`, and `PageHeader`
components; there is no need to add `shape="pill"` or copy milestone classes into
pages. Form selects and input groups retain standard rounded corners; their
embedded action buttons inherit the button shape. PageHeader.SelectTrigger supplies
the pill shape for header dropdowns. Explicit shapes remain available for a specific visual need.

Admin enables the current design on every route without a Labs flag. The generic
`isAdmin7` provider switch remains available:

```tsx
<ShadeApp darkMode={darkMode} isAdmin7={isAdmin7}>
    <App />
</ShadeApp>
```

Components can read the same value for structural or interaction differences
required by legacy surfaces:

```tsx
import {useShade} from '@tryghost/shade/app';

const {isAdmin7} = useShade();
```

Pure appearance differences belong in shared recipes and scoped tokens.
`Button variant="subtle"` expresses a low-emphasis action: ghost chrome with
`isAdmin7` enabled, outline chrome otherwise. Avoid using control shape as a
proxy for behavior.

Portals carry `data-admin7` through Shade's internal scope wrapper. Keep
scope attributes off `document.body`: independently mounted surfaces must not
change one another's menus or dialogs. Shade previews and standalone ActivityPub
default to the new appearance. Embedded ActivityPub inherits Admin's computed
boolean from the shared application wrapper.

The [feature flag guide](https://github.com/TryGhost/Ghost/blob/main/docs/practices/feature-flags.md#admin-7-milestones)
covers naming, shared presentation, testing and removal. Retain compatibility
branches only while a surface still needs them; ordinary component calls
already use the intended defaults.
