# Conscia Design System: Agent Guide

This file is the compact, version-matched contract for coding agents working in
an application that has `@conscia-labs/design-system` installed. Read the
installed copy before changing application UI. The installed package version is
authoritative when this guide differs from documentation on `main`.

## Start here

1. Inspect the application's existing shell, global stylesheet, routing, and
   nearby design-system usage before editing.
2. Reuse a public Conscia pattern when one matches the workflow. Use primitives
   when the application genuinely needs a composition the package does not own.
3. Import only public exports. Do not reach into `dist` or package internals.
4. Preserve the application's routing, authentication, permissions, data,
   mutations, validation, and business behavior.
5. Verify the affected UI at desktop and mobile sizes, including keyboard and
   visible focus behavior for interactive changes.

Live examples and component guidance:
<https://conscia-labs.github.io/design-system/>

Machine-readable component inventory:
<https://conscia-labs.github.io/design-system/agent-manifest.json>

## Installation contract

Tailwind CSS v4 applications import these styles once from their global CSS:

```css
@import "tailwindcss";
@import "@conscia-labs/design-system/tailwind.css";
```

Do not add another `@source` for the package. The published stylesheet already
contains the package-relative source declaration.

Applications that do not run Tailwind use the complete precompiled bundle:

```css
@import "@conscia-labs/design-system/standalone.css";
```

Never import `standalone.css` in a Tailwind application. Its preflight and
utilities would compete with the application's generated CSS.

Set shared preferences on the document root:

```tsx
<html
  lang="en"
  data-appearance="system"
  data-density="comfortable"
  suppressHydrationWarning
>
```

Supported appearance values are `light`, `dark`, and `system`. Supported
density values are `comfortable`, `compact`, and `operational`.

Optional `data-mood` values are `balanced` and `airy` (no attribute is the
current bordered look). Set a mood on the root or on a subtree that also sets
`bg-canvas`; `moodProps(mood)` returns the attribute. On the root, a mood
also restyles the sidebar through three independent options, each following
the mood while left at `auto`: `AppShell sidebarLayout` (`auto`, `flush`,
`floating`), `AppSidebar variant` (`auto`, `light`, `dark`, `brand`), and
`AppSidebar activeStyle` (`auto`, `tint`, `solid`). Set them explicitly when
an application needs a fixed sidebar. `AppShell headerLayout="auto"` lets the
header follow the mood as well: integrated without one, split (full-height
sidebar with the brand in `AppSidebarHeader`, transparent `AppHeader` inside
`MainRegion`) under balanced or airy. For a split shell in every mood, use
`headerLayout="split"` (see `docs/shell-navigation.md`).

## Non-negotiable boundaries

- Do not copy design-system components into the application.
- Do not install `@base-ui/react`, `@radix-ui/*`, or copied shadcn components
  when the design system already provides the behavior.
- Do not use the removed `asChild` API. Use the documented `render` prop for
  custom-host composition.
- Do not use legacy utilities such as `bg-primary`, `bg-muted`, `border-input`,
  or their associated legacy variables. Use Conscia semantic roles.
- Do not reach into package implementation files or depend on Base UI details.
- Do not add application z-index workarounds for supported overlays before
  checking the documented `modal` behavior.
- Do not encode status using color alone. Retain meaningful text, labels, or
  icons.

## Choose the highest useful level

### Patterns

Prefer patterns for recurring product workflows:

- Application chrome: `AppShell`, `AppHeader`, `AppSidebar`, `MainRegion`,
  `PageFrame`, and `SidebarNavigation`.
- Page composition: `PageHeader`, `PageToolbar`, `ResourceSummary`, and
  `DetailSection`.
- Data-heavy collections: `DataTable`, `EntityTable`, `InventorySurface`, and
  `PaginationControls`.
- Operational dashboards: `MetricCard`, `MetricBand`, `DataPanel`,
  `AttentionList`, and `ActivityList`.
- States and feedback: `StateView`, `ErrorState`, `LoadingRows`,
  `ConfirmationDialog`, `CommandPalette`, and `FilterBar`.
- Multi-rail workspaces: the `Workbench*` family.

### Primitives

Use primitives for application-specific compositions: `Button`, `Card`,
`Field`, `Input`, `Select`, `SearchableSelect`, `Dialog`, `Sheet`, `Popover`,
`Tabs`, `Table`, `Badge`, `Alert`, `Toast`, and related anatomy exports.

Consult `agent-manifest.json` for the complete public runtime inventory and the
playground route for each family.

## Frequent decisions

- Use `Table` for semantic table anatomy. Use `DataTable` for sorting,
  selection, column definitions, responsive rows, or pagination.
- Underline tabs are quiet by default (no full-width rail). Use `variant="divider"`
  for structural separation, `variant="pills"` for small peer views, and
  `variant="segmented"` for mode switching. `NavigationTabsList` supports
  `variant="divider"` and `variant="pills"` as well. Root variants inherit
  through lists and triggers.
- Use `Tabs` for layered content or mode switching. Use `NavigationTabs` for
  route-backed destinations, including pill-styled ones: use
  `<NavigationTabsList variant="pills">` rather than `Tabs` or local restyling,
  so links keep `aria-current="page"`.
- Use `variant="nested"` for a tab group inside a section or another tab panel
  so it stays visibly subordinate to page-level tabs.
- `Table` scrolls wide content horizontally by default and fades the edge with
  more columns. Pass `scrollable={false}` only when you render a stacked layout
  at narrow widths. Use `paginationPageSizes.inventory` (25) for full-page
  inventories and `paginationPageSizes.detail` (10) for tables in detail pages.
- `StateView` and `ErrorState` have no border by default. Use `size="compact"`
  inside sections, panels, and tabs, and `divider` only when the state replaces
  table rows.
- Badge tones carry meaning: `neutral` for lifecycle states (Active, Enabled,
  Published), `success` only for positive evidence (Healthy, Ready, Verified),
  `information` for pending work, `warning` for problems that need action, and
  `danger` for failures. Badges size to their content; do not add `w-fit`.
- Use `Disclosure` instead of native `<details>`, `DateTimeField` instead of a
  native `datetime-local` input, `Stepper` for wizard progress, and
  `DailyVolumeChart` for per-day measures. Its successful series uses the
  `success` token by default so it matches outcome badges and trends nearby;
  pass `successfulTone="accent"` for a measure with no outcome, and
  `successfulTone` / `unsuccessfulTone` to choose other semantic tones.
- `PageFrame`, `PageHeader`, `PageToolbar`, and `DetailBackLink` share one left
  edge through `--ds-frame-padding-x`. Do not wrap them in extra padding.
- A `PageHeader` or `PageToolbar` inside a `Card` or `InventorySurface` takes
  the surface's own inset (`--ds-surface-padding`), a bottom border, and the
  card surface, while the table below keeps its tinted header band. Do not add
  padding, border, or background classes to them, and do not restyle them from
  global CSS.
- One header type per table: every `DataTable` header cell uses the
  table-head type whether or not it sorts. The sort control is the header text
  plus its icon, not a button-sized label. Do not pass `Button` type classes
  into column headers.
- For a missing `MetricBandItem` value, pass `value={null}` or an `emptyValue`
  such as "Never" rather than a display-size word.
- Use `Select` for a short, familiar list. Use `SearchableSelect` for longer
  lists that users need to filter.
- Use `LoadingButton` for asynchronous actions so layout, disabled state, and
  `aria-busy` remain consistent.
- Use `ConfirmationDialog` or `AlertDialog` only for consequential decisions.
- Use `operational` density for information-heavy administration, inventory,
  and workspace surfaces. Keep `comfortable` for general-purpose product UI.
- Use `data-mood="balanced"` as an app-wide default when a product should feel
  less boxed in. Use `data-mood="airy"` for overview, onboarding, empty-state,
  and short settings pages, never for the workbench, logs, or long tables.
- Use `MetricCard tone` (`highlight`, `supporting`, `accent`, `muted`) for at
  most one row of headline tiles. Status stays in badges and trends.

## Composition examples

Use fields as the unit of form layout and accessible help/error content:

```tsx
import {
  Button,
  Field,
  FieldDescription,
  FieldLabel,
  Input,
} from "@conscia-labs/design-system";

<form className="grid gap-5">
  <Field>
    <FieldLabel htmlFor="connection-name">Name</FieldLabel>
    <Input id="connection-name" name="name" />
    <FieldDescription>Use a name operators will recognize.</FieldDescription>
  </Field>
  <Button type="submit">Create connection</Button>
</form>;
```

Keep popup controls non-modal when nested inside a modal surface:

```tsx
<Dialog open={open} onOpenChange={setOpen}>
  <DialogContent>
    <DialogBody>
      <FormSelect modal={false} name="vendor" options={vendorOptions} />
      <SearchableSelect
        modal={false}
        name="model"
        options={modelOptions}
        onValueChange={setModel}
      />
    </DialogBody>
  </DialogContent>
</Dialog>
```

Prefer the integrated global header for new application shells:

```tsx
<AppShell headerLayout="integrated">
  <AppHeader>
    <AppHeaderStart>
      <SidebarTrigger />
      {productIdentity}
    </AppHeaderStart>
    <AppHeaderSearch>{globalSearch}</AppHeaderSearch>
    <AppHeaderActions>{accountActions}</AppHeaderActions>
  </AppHeader>
  <AppSidebar variant="auto">
    <AppSidebarContent>{navigation}</AppSidebarContent>
  </AppSidebar>
  <MainRegion>{children}</MainRegion>
</AppShell>
```

For products that adopt a mood, use `headerLayout="auto"` with the header
inside `MainRegion` and the brand in both places; each part shows only where
it belongs:

```tsx
<AppShell headerLayout="auto">
  <AppSidebar variant="auto">
    <AppSidebarHeader>
      <BrandLockup product="Beacon" render={<Link href="/" />} />
    </AppSidebarHeader>
    <AppSidebarContent>{navigation}</AppSidebarContent>
  </AppSidebar>
  <MainRegion>
    <AppHeader>
      <AppHeaderStart>
        <SidebarTrigger />
        <AppHeaderBrand>
          <BrandLockup product="Beacon" render={<Link href="/" />} />
        </AppHeaderBrand>
        <AppHeaderTitle title={pageTitle}>{breadcrumbs}</AppHeaderTitle>
      </AppHeaderStart>
      <AppHeaderSearch mobileTrigger={searchIconButton}>{globalSearch}</AppHeaderSearch>
      <AppHeaderActions>{accountActions}</AppHeaderActions>
    </AppHeader>
    {children}
  </MainRegion>
</AppShell>
```

Brand every Conscia app with `BrandLockup` rather than a hand-built icon and
label: the mark, then "Conscia" in semibold and the product name in regular
weight. Pass `product`, `href` or `render` for the home link, and optionally
`size` (`sm` 24px, `md` 32px by default, `lg` 36px). Its colors follow the
sidebar or header it sits in, a collapsed sidebar shows the mark alone and a
phone header the mark and product name, and its link is named
"Conscia <product>". It leaves the sidebar header glow alone; remove it in
the app only if the product calls for that.

Use the optional quiet sidebar treatment for lower-contrast product surfaces.
It works with `variant="auto" | "light" | "dark"` and keeps appearance and
density preferences in control:

```tsx
<AppSidebar variant="auto" treatment="quiet">
  <AppSidebarContent>{navigation}</AppSidebarContent>
</AppSidebar>
```

`MetricBand` also supports `presentation="compact"`. It keeps each metric in
its own row on narrow screens and uses the configured number of columns on
desktop. Metric values accept React nodes, including links; in compact
presentation, an omitted value is shown as unavailable rather than zero.
`ActivityItem` already supports recent
request history: put the direct diagnostics anchor in `title`, use `metadata`
for a timestamp, and place a readable outcome `Badge` in `status`. The anchor
can open in a new tab through normal browser controls; do not wrap the full row
around other interactive content.

## Styling

Use semantic tokens and utilities rather than palette colors:

- Surfaces: `canvas`, `surface`, `surface-raised`, `surface-muted`, and
  `surface-floating`.
- Text: `text-primary`, `text-secondary`, `text-supporting`, and `text-muted`.
- Brand: `brand` with `brand-foreground` for burgundy identity surfaces,
  `brand-accent` for blue emphasis, and `brand-supporting-*` for restrained
  green expression. Use the matching foreground token on filled surfaces.
- Actions and selection: `action-*` and `selection-*`.
- Status: `information-*`, `success-*`, `warning-*`, and `danger-*`.
- Neutral structure: `border-subtle`, `control-border`, and shared radius,
  spacing, type, elevation, and focus tokens.

Use `className` for layout and local composition. Override semantic variables
only when the product intentionally changes a system-level decision. Avoid
component descendant selectors.

The low-level `palette-burgundy`, `palette-electric-blue`, and `palette-green`
variables preserve official brand values, but applications should compose with
the semantic brand roles above. Do not use `text-brand` as ordinary text on a
dark canvas; burgundy is intentionally stable across appearances and is
designed as an identity surface with `text-brand-foreground`.

### Brand usage

Follow the conscia.com colour-usage guide: burgundy covers the most area,
then white and green, with electric blue as a small accent and the pale
tints smaller still.

- **Burgundy** is for large identity surfaces: `AppSidebar variant="brand"`
  with `AppHeader tone="brand"`, heroes, and `Card tone="brand"` service
  tiles. Never use it as a thin accent line.
- **Electric blue** is for actions, focus, selection, and accents. Buttons
  already use it.
- **Green** is for supporting identity (`brand-supporting`, `Card
  tone="supporting"`) and positive trends.
- **Peach** is the `highlight` role (`Card tone="highlight"`) for case studies
  and callouts. It is never a status; danger is an orange-red.
- `ds-supergraphic` draws the dotted rings on heroes, sign-in, empty
  workspaces, and brand cards. Never put it, or a toned card, behind data.
- `brand-emphasis` is deprecated. Use `brand-accent`, `brand-supporting`, or
  `highlight` instead.

### Typeface

The package bundles Inter (`@fontsource-variable/inter`, SIL OFL) with the
weight and optical-size axes, and `--font-sans` starts with `"Inter Variable"`.
The files are self-hosted from the package, so offline Electron apps render it
too. Do not add a font CDN or set a literal `font-family`; use
`var(--font-sans)`.

Use only the three brand weights: regular 380, medium 570 and semibold 670.
Tailwind's `font-normal`, `font-medium`, `font-semibold` and `font-bold` map to
them. Tables, timestamps, metric values, meters and pagination use tabular
figures; add `tabular-nums` to any other column of numbers that should line up.

## Accessibility ownership

The package provides component-level keyboard behavior, focus management, and
semantic structure. The application remains responsible for:

- Meaningful labels and accessible names.
- Heading order and page landmarks.
- Form validation messages and their relationships.
- Alternative text.
- Route state such as `aria-current`.
- Complete keyboard-accessible business workflows.

## Verification

Use the consuming application's own commands. At minimum, run its typecheck and
tests for behavior changes, then inspect the affected route in light and dark
appearance at desktop and mobile sizes. For interactive changes, exercise the
keyboard path, focus return, disabled or pending state, and any nested overlay.

For migration work, use the versioned migration guide linked from the package
README. Do not infer compatibility from old shadcn or Radix usage.
