Cognition

Foundations

Guidelines

The rules every Distyl frontend follows. Use the semantic token for intent, the correct component for the job, and let the token layer handle dark mode.

Component semantics

Every interactive element must be the semantically correct component. Using the wrong element is a bug, not a style choice.

ComponentInteractiveTriggers actionUse for
ButtonYesYesSubmit, save, open dialog, trigger mutation
TagNoNoNon-interactive label, category, keyword
BadgeNoNoStatus indicator, count, state
ChipYesNoSelectable filter or toggle option
LinkYesNavigatesNavigation to a route or URL
Don't

Don't bypass the system with hardcoded colors, raw Tailwind palette utilities, off-scale spacing, dark-mode classes, or a button used as a label.

Do
className="bg-background-subtle text-text-subtle"
<Tag>Engineering</Tag>
className="p-3"
className="bg-background-default text-text-default"

Full specification

Cognition Design System

Distyl AI · v1.2.1 · June 2026

This file defines the design and component conventions for all Distyl frontend work. It applies to new implementations, new features, and any surface where AI tooling (Claude Code, Cursor, Copilot) is generating or modifying UI code. Follow these rules exactly. No exceptions without a proposal.

1. Token Rules

Colors

Never use hardcoded hex, rgb(), hsl(), or Tailwind color utilities directly in components. Always use semantic tokens via CSS custom properties.

Backgrounds

color.background.default      /* --color-background-default    Main page/app background */
color.background.subtle       /* --color-background-subtle     Sidebars, off-white surfaces */
color.background.secondary    /* --color-background-secondary  Secondary surfaces, hover states */
color.background.accent       /* --color-background-accent     Brand-tinted surfaces */
color.background.primary      /* --color-background-primary    Primary brand fills, buttons */
color.background.inverse      /* --color-background-inverse    Dark surfaces */
color.background.danger       /* --color-background-danger     Error background tints */
color.background.success      /* --color-background-success    Success background tints */
color.background.warning      /* --color-background-warning    Warning background tints */

Text

color.text.default            /* --color-text-default          Body text, primary content */
color.text.subtle             /* --color-text-subtle           Secondary text, placeholders */
color.text.disabled           /* --color-text-disabled         Disabled text only */
color.text.inverse            /* --color-text-inverse          Text on dark/filled backgrounds */
color.text.primary            /* --color-text-primary          Brand text, links, active labels */
color.text.danger             /* --color-text-danger           Error messages */
color.text.success            /* --color-text-success          Success messages */
color.text.warning            /* --color-text-warning          Warning messages */

Borders

color.border.default          /* --color-border-default        Standard borders, input outlines */
color.border.subtle           /* --color-border-subtle         Light separators */
color.border.strong           /* --color-border-strong         Emphasized borders */
color.border.primary          /* --color-border-primary        Focused/active inputs */
color.border.danger           /* --color-border-danger         Error state borders */
color.border.success          /* --color-border-success        Valid/success borders */

Feedback (canonical — use these for all status states)

color.feedback.danger         /* --color-feedback-danger       All error/destructive states */
color.feedback.success        /* --color-feedback-success      All success states */
color.feedback.warning        /* --color-feedback-warning      All warning states */
color.feedback.info           /* --color-feedback-info         All info states */

Spacing

Use the 4px base unit scale. Never use arbitrary px values in inline styles.

space.1 = 4px    space.2 = 8px    space.3 = 12px   space.4 = 16px
space.6 = 24px   space.8 = 32px   space.12 = 48px  space.16 = 64px

Radius

radius.none = 0px    radius.sm = 4px     radius.md = 8px
radius.lg = 12px     radius.xl = 16px    radius.full = 9999px

Typography

font-family: Geist (all body copy, UI labels, inputs)
font-family: Geist Mono (code blocks, technical values only)

sizes: 12px / 14px / 16px / 18px / 20px / 24px / 30px / 36px
weights: 400 / 500 / 600 / 700

Type scale. The heading scale starts at H1 — there is no Display level.

LevelSizeWeight
H136pxBold (700)
H230pxSemibold (600)
H324pxSemibold (600)
H420pxSemibold (600)
Lead18pxSemibold (600)
Body16pxRegular (400)
Small14pxMedium (500)
Caption12pxRegular (400)

Nav section labels and page eyebrows use Caption (12px regular). Headings (H1–H4) use a tightened line-height of 1.2; body and supporting text use 1.5.


2. Component Rules

The Golden Rule

Every interactive element must be the semantically correct component. Using the wrong element is a bug, not a style choice.

Component Definitions

ComponentInteractiveTriggers ActionUse ForNever Use For
ButtonSubmit, save, open dialog, trigger mutationLabels, tags, status, categories
TagNon-interactive label, category, keywordActions, navigation, status
BadgeStatus indicator, count, stateActions, categories
ChipSelectable filter or toggle optionSubmission actions
Link❌ (navigates)Navigation to a route or URLActions that mutate state

Button Variants

// Primary — default filled action
<Button variant="default">Save Changes</Button>

// Secondary — outlined/ghost
<Button variant="outline">Cancel</Button>

// Destructive — irreversible actions only
<Button variant="destructive">Delete System</Button>

Component Tokens (use these, not semantic tokens directly in components)

/* Button */
button.background.primary       /* Filled button background */
button.background.primary.hover /* Filled button hover */
button.text.primary             /* Filled button label */
button.background.secondary     /* Outlined button background */
button.border.secondary         /* Outlined button border */
button.text.secondary           /* Outlined button label */
button.background.danger        /* Destructive button fill */
button.text.danger              /* Destructive button label */

/* Input */
input.background.default        /* Input field background */
input.border.default            /* Input border at rest */
input.border.focus              /* Input border focused */
input.border.error              /* Input border invalid */
input.text.default              /* Input typed text */
input.text.placeholder          /* Input placeholder */
input.text.disabled             /* Input text disabled */

/* Tag / Badge */
tag.background.default          /* Tag background */
tag.text.default                /* Tag label */
tag.border.default              /* Tag border */
badge.background.default        /* Neutral badge */
badge.background.success        /* Success badge */
badge.background.danger         /* Error/danger badge */
badge.background.warning        /* Warning badge */

3. Higher-Order Components

Before building any page-level UI from scratch, check the Cognition Component Library for existing patterns. The library includes production-ready blocks for:

  • App shells — sidebar + topnav + content area layouts
  • Headers — page headers with actions, breadcrumbs, titles
  • Navigation — sidebar nav, top nav, secondary nav patterns
  • Data displays — tables, stat cards, metric layouts
  • Forms — complete form layouts with validation states

If a pattern exists in the library, use it. If it needs modification, extend it. Do not replace it.


4. Radix UI / shadcn Rules

All Radix UI usage must go through shadcn wrappers in components/ui/. Never import directly from @radix-ui/* in feature files.

// ✅ Correct
import { Collapsible } from "@/components/ui/collapsible";

// ❌ Wrong
import * as Collapsible from "@radix-ui/react-collapsible";

5. Anti-Patterns (pulled from codebase audit)

These patterns exist in the current codebase. Do not replicate them in new work.

// ❌ Hardcoded colors
className = "bg-[#f5f5f5] text-gray-500 border-gray-300";

// ✅ Correct
className = "bg-background-subtle text-text-subtle border-border-default";
// ❌ Button used as a tag/label
<button className="rounded-full px-2 py-1">Engineering</button>

// ✅ Correct
<Tag>Engineering</Tag>
// ❌ Direct Tailwind gray scale
className = "text-gray-500 border-gray-200 bg-gray-100";

// ✅ Correct
className = "text-text-subtle border-border-default bg-background-secondary";
// ❌ Hardcoded spacing
style={{ padding: '13px', margin: '7px' }}

// ✅ Correct — 4px scale
className="p-3 m-2"  // 12px, 8px
// ❌ --secondary, --muted, --accent all resolved to the same value (legacy bug)
// These are now properly differentiated — use the correct semantic token
color.background.subtle; // was --muted
color.background.secondary; // was --secondary
color.background.accent; // was --accent (now purple.50, not gray.100)

6. CSS Variable Migration Map

When updating existing components, use this map to migrate legacy variables.

Legacy VariableNew Canonical Token
--primarycolor.background.primary
--backgroundcolor.background.default
--foregroundcolor.text.default
--secondarycolor.background.secondary
--mutedcolor.background.subtle
--accentcolor.background.accent
--destructivecolor.feedback.danger
--bordercolor.border.default
--inputcolor.border.default
--radiusradius.md
--successcolor.feedback.success
--warningcolor.feedback.warning

7. Dark Mode

Dark mode is implemented entirely at the semantic token layer. Never add dark: classes to individual components. Semantic tokens remap automatically via [data-theme="dark"] on the <html> element.

// ❌ Never do this
className = "bg-white dark:bg-gray-950 text-gray-900 dark:text-gray-50";

// ✅ Correct — tokens handle dark mode automatically
className = "bg-background-default text-text-default";

8. Rollout

Segment 1 — AI Tools

Drop this file as CLAUDE.md in the root of fe-distillery. Also add as .cursorrules for Cursor users. AI coding tools will generate Cognition-compliant code on new work without additional prompting.

Segment 2 — Figma to Repo

Set up Code Connect to map Figma components to their React equivalents. Engineers inspecting a component in Figma will see the exact import and props without interpretation.

Segment 3 — External Sessions

The portable system prompt version of this doc (cognition-skill.md) applies to any Claude session, Lovable build, v0 export, or Bolt project outside the repo.


9. Governance

  • Adding a token — MINOR version bump
  • Renaming or deleting a token — MAJOR version bump + 2 week deprecation period
  • Changing a token value — PATCH (small visual delta) or MINOR (significant change)
  • New component — MINOR version bump

All token changes proposed via Figma branch first. No direct edits to the main library.

Questions: #engineering-fe or #research-and-design.