# Haya Signal design system

Signal v0.1 is the implementation foundation for the approved Signal direction. It provides shared React components and a separate, interactive reference. Product routes, workflows, permissions, API clients and backend contracts remain unchanged.

## Open the reference

```sh
npm ci
npm run dev -- --host 127.0.0.1 --port 5175
```

Open http://127.0.0.1:5175/design-system.html. The Foundations, Components, Patterns and Usage tabs also support direct hash links, such as `design-system.html#patterns`. The reference needs no backend, authentication or real candidate records.

```sh
npm run build:design-system
npm run preview -- --mode design-system --port 5176
```

The standalone reference builds to `dist-design-system/`. The normal `npm run build` still builds only the Haya application into `dist/`. Do not deploy the reference output as the Haya application.

## Canonical sources

| Concern                                                        | Source                                 |
| -------------------------------------------------------------- | -------------------------------------- |
| Colours, typography, spacing, radius, motion, density          | `src/design-system/tokens.css`         |
| Shared component and composition styles                        | `src/design-system/signal.css`         |
| Theme boundary, browser preferences, portal container          | `src/design-system/SignalProvider.tsx` |
| shadcn Base UI components                                      | `src/components/ui/`                   |
| Brand, page heading, panels, record row, pipeline, empty state | `src/design-system/patterns.tsx`       |
| Interactive specimens and example records                      | `src/design-system/reference/`         |
| shadcn registry configuration                                  | `components.json`                      |
| Guidance for subsequent frontend tasks                         | `src/AGENTS.md`                        |

The reference imports the actual shared components. Its example records and layout CSS are confined to the reference entry. Features must not import from `design-system/reference`.

The visual baseline is the reviewed Signal prototype, revision `signal20260917a`, including the approved contrast adjustments for control borders and the nav indicator. The build brief is in `haya-prototype/SIGNAL-BUILD-BRIEF.md`. The separate production components, rather than the prototype CSS or JavaScript, are the implementation source of truth from this point onward.

## Component base and ownership

Components were generated with shadcn CLI 4.21.0 from the `base-nova` registry, using the existing `@base-ui/react` 1.6 foundation. Haya owns the generated code. The original shadcn structure and Base UI behaviour are retained; styling, variants and sizes are adapted to Signal.

See [shadcn Base UI components](https://ui.shadcn.com/docs/components/base/button), [Base UI composition](https://base-ui.com/react/handbook/composition) and [shadcn configuration](https://ui.shadcn.com/docs/components-json).

This is not the Radix version. Use Base UI's `render` prop, not `asChild`. For an anchor rendered by Button, specify `nativeButton={false}` and `role="link"`. Keep accessible labels on icon-only controls and connect each field's visible label, help and error text with `htmlFor` and `aria-describedby`.

The shipped primitives are Button, Badge, Input, Textarea, Label/Field, Checkbox, RadioGroup, Select, Tabs, Avatar, Separator, Skeleton, Dialog, Tooltip and Toast. Only Signal-supported sizes and variants are exposed. Button sizes are `default` and `icon`, both 42px minimum; no tiny shadcn button variants. Badge variants are neutral/default, ready, overdue, success and outline. Use OverdueBadge to include the clock consistently.

To add a missing primitive, use the configured registry:

```sh
npx shadcn@4.21.0 add <component> --dry-run
npx shadcn@4.21.0 add <component>
```

Inspect the generated changes. Do not overwrite locally adapted components. Adapt new primitive defaults to Signal tokens, supported sizes, motion and the scoped portal container, then add an interactive specimen. A new dependency or new primitive does not require a new abstraction layer.

## Integrating a screen

Wrap a migrated application area once. Keep the provider above route changes when integrating multiple screens, so overlays and preferences share one boundary.

```tsx
import { SignalProvider } from '@/design-system/SignalProvider'
import { TooltipProvider } from '@/components/ui/tooltip'
import { Toaster } from '@/components/ui/toast'

;<SignalProvider>
  <TooltipProvider>
    <Toaster>
      <ExistingScreen />
    </Toaster>
  </TooltipProvider>
</SignalProvider>
```

Import components directly from `@/components/ui/...` and compositions from `@/design-system/patterns`. Features continue to supply real records, stage labels, status, permissions and callbacks from their existing domain models. PipelinePath is presentational: it never determines a hiring stage or advances a candidate.

`useSignal()` exposes `theme`, `setTheme`, `density` and `setDensity`. Preferences are stored only in the current browser under `haya.signal.preferences.v1`; they are not account-level settings or backend data. Valid updates synchronize across tabs. Invalid or blocked storage falls back safely. Use `persist={false}` for isolated tests or embedded specimens.

Dialog, Select, Tooltip and Toast portals render into the provider's container. This keeps dark colours and density attached to overlays, even while a legacy screen is still present elsewhere. Mount one Toaster for the area; call `toast.add({ title, description, actionProps })` for feedback. Default timeout is 4 seconds, pausing while the user interacts with the notification.

The provider imports the local Commissioner font and Signal CSS. Styling is deliberately scoped and unlayered: Haya's legacy global CSS is unlayered, so a lower-priority utility layer would silently lose control of typography and controls. Do not introduce a global preflight or change legacy tokens to enable an individual Signal screen. Feature layout classes may reference `--s-*` tokens; shared component visual changes belong in the design system.

## Visual and content contract

- Use the eight type roles and seven unique sizes: 42, 24, 20, 16, 14, 12 and 11px. Commissioner only; no synthesized weights.
- Use spacing 4, 8, 12, 16, 24, 32 and 48px. Controls have 3px radius, avatars 4px, panels 0. Avatar sizes are 26, 36, 48 and 56px.
- Ember marks one next action per region, current pipeline nodes and the decision corner. Danger is a separate cool red. Preserve the approved Ember node treatment until recruiter testing decides otherwise.
- Use explicit semantic colour tokens, not opacity-derived surfaces or new hex values in features. Control outlines use the stronger control-line token; line is for nonessential dividers.
- Use Lucide icons at 16 or 20px with stroke 1.5. Keep meaning in text and shape as well as colour.
- Use 160ms ease-out transitions and the shared duration token. Reduced motion sets it to zero. Skeletons have no decorative animation; lists have no entrance animation.
- Default rows have a 93px minimum, compact rows 56px. A row may grow for long content or mobile wrapping. Do not clip content to meet an exact height.
- Workspace preview width is 330px, 280px below 1250px, 240px below 960px, and full width above the list below 700px.
- Use noun headings, numerical status and direct action labels. Wordmark is lowercase `haya`, without a trailing period. No decorative motifs beyond the approved offset square.

## Review and adoption

Run:

```sh
npm test
npm run build
npm run build:design-system
npm run verify:production-authority
npm run verify:production-bundle
```

Review changed components in light and dark, both densities, keyboard focus, mobile/tablet/desktop, and relevant loading, empty, error, disabled and long-content states. Check portalled overlays as well as inline components. Preference and interaction tests live beside the provider and reference.

Adopt one real screen next, preferably My work, using its existing APIs and permission checks. Expand the system only for a repeated need discovered in that screen, then migrate the next screen against the same components. The reference is a component review surface, not evidence that production workflows have been migrated or fully tested.

Open research item: test Paths accent density with two or three recruiters. Non-selected current nodes remain Ember until that feedback supports a change. A neutral-node alternative has not been adopted.
