# Form & Phase — Shared components

The 16 studio pages and system lab import `src/system/components.jsx` and use `src/system/system.css`. `tokens.json` is the source for the generated `tokens.css`. Optical layers live in `src/system/optical.css`, with material theme tokens generated from `tokens.json`. Page composition lives in `src/studio.css` and `src/optical-pages.css`; the lab has its own documentation layout.

## Entry points

| Route | Purpose | Source |
|---|---|---|
| `/studio/` | Broad studio promise, capabilities, method, invitation | `src/studio.jsx` / Home |
| `/studio/expertise/` and its five service routes | Overview, service scope, decisions, deliverables, related expertise | `src/site-pages.jsx`, `src/service-content.js` |
| `/studio/design-systems/` | Foundations, components, design/code practice, adoption | `src/site-pages.jsx` / DesignSystems |
| `/studio/approach/` and `/studio/approach/quality/` | Full delivery process and quality practices | `src/site-pages.jsx` |
| `/studio/work/` and `/studio/work/form-phase-system/` | Approved studio-initiated project and inspectable artifacts | `src/site-pages.jsx` |
| `/studio/about/`, `/studio/privacy/`, `/studio/accessibility/` | Studio point of view and editorial privacy/accessibility pages | `src/site-pages.jsx` |
| `/studio/contact/` | Validated inquiry form with delivery-dependent feedback and copy fallback | `src/studio.jsx` / Contact |
| `/system-lab/` | State matrix, controls, motion, content stress specimens | `src/lab.jsx` |
| `/` | Written guide and original interactive specimens | `index.html`, `guide.md`, `src/controls.jsx` |

Studio pages are prerendered to HTML and hydrated with React. The content, navigation, fonts, and artwork are local. No API, telemetry, or form delivery is connected.

## Component contracts

| Component | Key props | Behavior / ownership |
|---|---|---|
| Button | `variant`, `size`, `href`, `loading`, `disabled`, `leading`, `icon`, `edgeColor` | Native button for action, anchor for navigation. Five variants only. Loading guards clicks and exposes busy state. |
| IconButton | `label`, Button props | Circular at every size and state: 40/48/56 px diameter. Loading replaces the icon with a centered spinner. Requires a descriptive accessible name. |
| GlassSurface | `as`, `className`, `children`, `edgeColor` | Tinted panel, proximity rim and halo; preserve a readable backdrop. |
| OpticalEdge | `color` | Decorative 1 px rim and soft halo; reused by all glass controls and panels. |
| OpticalStructure | `className` | Decorative CSS planes and rays, hidden from assistive technology. |
| OpticalLighting | none | One shared pointer-light controller mounted by SystemProvider; resting fallback and full observer cleanup. |
| Field | `id`, `label`, `hint`, `error`, `optional`, `edgeColor`, one child | Associates visible label, hint, and error; sets `aria-invalid`. Wraps the native/Radix control in a decorative glass layer. |
| Input / Textarea | Native element props; Input `size` = small/default/large, `htmlSize` for native character width | Input heights are 40/48/56 px, including validation states. Textareas grow with content. Browser autocomplete, selection, read-only and disabled behavior retained. |
| CheckControl | `label`, `checked`, `defaultChecked`, `onCheckedChange`, `disabled` | Radix Checkbox. Supports `indeterminate`. |
| SwitchControl | `label`, `checked`, `onCheckedChange`, `disabled` | Radix Switch; label describes the setting rather than its current state. |
| RadioControls | `label`, `options`, `value`, `onValueChange` | Radix Radio Group; arrow-key navigation and disabled options. |
| SelectControl | `options`, `value`, `onValueChange`, `placeholder`, `label` | Radix Select. Sizes match inputs at 40/48/56 px; compact selects use small. Portal inherits the document theme; popup constrained to available space. |
| Disclosure | `items`, `defaultValue` | Single expandable Radix Accordion. Content height drives animation. |
| TabGroup | `label`, `items`, `defaultValue` | Radix Tabs with automatic activation and keyboard navigation. |
| Modal | `trigger`, `title`, `description`, `children` | Radix Dialog with focus containment, Escape, close control, and focus return. |
| Alert | `tone`, `title`, `children`, `live` | Inline feedback; opt into announcements for actual dynamic changes. |
| SystemProvider | `children` | Shared theme, reduced-motion CSS, tooltip and notification environment. |
| useNotify | message string | Persistent Radix toast with close action; restores focus to originating control. |
| Container / Section | size / title / description / children | Shared measures and section rhythm. |
| StudioHeader / StudioFooter | current page | Navigation overlays each studio page; contracts after 48 px and stays fixed. Mobile menu uses Radix Dialog. Footer provides thematic closing and grouped destinations. |

## Usage

```jsx
<SystemProvider>
  <Field id="project-email" label="Email address" error={errors.email}>
    <Input type="email" autoComplete="email" required />
  </Field>
  <Button type="submit" loading={pending} icon={<ArrowUpRight aria-hidden />}>
    Send your inquiry
  </Button>
</SystemProvider>
```

Pass `loading={false}` in the resting state when an action can load; this reserves its leading indicator space. Keep the label stable. Use the form or adjacent status text to announce the outcome. Disabled buttons leave the tab order; loading buttons retain focus and ignore repeat activation. Navigational links should not be used for pending mutations.

## Themes and surfaces

Use `data-theme="light"`, `"dark"`, or `"accent"` on a section. Semantic roles inherit from the nearest theme. Dedicated image-display tokens ensure an inner dark section remains dark inside a light page. The accent surface has its own ink action, pale secondary fill, ink outline/ghost/link treatments, and contrast-checked boundaries.

Portaled controls inherit the root theme. The local-theme matrix contains buttons only. If future work places a select, dialog, or tooltip in a differently themed surface, provide a themed portal container or explicit theme on its content.

## Demo and production boundaries

The contact form validates required fields, focuses an error summary, and guards duplicate submissions. `src/site-config.js` holds a same-origin endpoint, currently unset. A successful HTTP response containing `{accepted:true}` is required before showing confirmation. Unconfigured delivery, network failures, and rejected responses preserve entries and show an error with a copyable brief. Requests abort on unmount. Connect server-side validation, abuse controls, and actual delivery before launch; see `PRODUCTION-NOTES.md`.

The written guide's earlier Radix islands are still an independent documentation layer. New studio pages and the state lab consume the shared components. Migrating every historical guide specimen to the new exports can follow without blocking page review.

## Build

`npm run build` regenerates tokens, compiles all browser bundles, prerenders the 16 studio routes and system lab, copies source CSS, syncs the guide's token inspector, and attaches content hashes to CSS/JS links. It does not render guide Markdown: update the corresponding HTML prose whenever the written specification changes. `npm run bundle` checks local links and creates the downloadable ZIP.

## Optical material ownership

`OpticalEdge`, `GlassSurface`, and `OpticalStructure` live in `src/system/optical.jsx`. Material colors inherit from `tokens.json.material`. Primary fill/text/edge colors have dedicated semantic roles, keeping link colors independent. `Button`, `Field`, and `GlassSurface` accept an optional CSS color through `edgeColor`; the lab demonstrates `edgeColor="#8AA8FF"`.

Only visible controls, fields, and panels within `component.light-proximity-radius` (96 px) react. Distance is measured to the element bounds with a smooth fade across that radius. One requestAnimationFrame batches angle/strength writes; resize, scroll, and DOM observers refresh cached bounds. There are no pointer-driven React state updates or continuous animation loops. Disabled/loading controls do not illuminate; invalid fields preserve their error boundary.

All optical controls share a crisp 1 px masked rim plus a 3 px masked ring blurred by 4 px at 0.9 opacity. The latter supplies the halo without a face overlay. Entry takes 160 ms; leaving proximity fades both layers over 650 ms while preserving direction. A stationary nearby pointer holds its light. Coarse input, reduced motion, and fixed-light selection show the resting surface; forced colors removes optical decoration and reduced transparency suppresses bloom. Observers, event listeners, and the queued frame clean up on unmount.

Dark neutral edges face the pointer; light/accent neutral edges are darker and face away (180° offset). The light neutral rim peaks at 28% alpha, shared by its halo, for a softer contour. An explicit color makes a near-side highlight in every theme. Primary buttons use a 1 px inner line tinted 82% toward white, with the original supplied color retained for the outward halo; other controls use their supplied color for both layers. Primary uses deep vermilion with white text and a coral edge; accent sections retain ink fills. Glass faces, header, planes, and hero artwork have no pointer-driven illumination overlay. Buttons have no inset shadows.

The expanded fixed header has a static ivory/charcoal clothoid gradient extending 64 px below it. This fades out as the shorter compact header's glass veil appears. Neither layer intercepts pointer events or changes layout.

## Clothoid gradient generation

The corrected PopHops helper is preserved verbatim in `src/system/vendor/clothoid-gradient.ts`; see its neighboring README for source provenance and the bidirectional fix. `scripts/gradients.mjs` samples each curved segment 33 times. Forward/reverse easing gives transparent tails and mirrored circular peaks a smooth join.

`gradients.css` is generated and content-hashed. A color-independent rim alpha profile resolves each element's custom color and dynamic angle locally; static fades inherit from the theme. Fresnel/OKLab calculations run at build time. Sharp mask geometry and proof rays retain their structural edges.

## Content and route ownership

`src/site-map.js` is the route and navigation source used by both rendering and the build. Top-level navigation always uses complete page URLs. `src/service-content.js` separates service copy from the shared ServicePage layout. `src/site-pages.jsx` supplies PageIntro, contextual links, editorial sections, technical decision rows, and the contained WorkTeaser. Dedicated design-system, approach, quality, work, studio, and utility pages reuse those foundations without adding pointer-driven React state. `src/content-pages.css` controls their responsive layouts.

The shared footer now appears on every studio page; Contact omits its repeated contact invitation. `CONTENT-MAP.md` records each destination and the full application-delivery coverage.

## Long-form utility pages

Privacy and Accessibility use a shared editorial pattern: restrained hero, full-width divider, sticky section index and date, and a reading column capped at 44 rem. Section numbers and fine rules provide orientation without card chrome. At 700 px the index moves above the article; below 380 px its links become one column. Section anchors account for the fixed header. Main navigation continues to target complete pages.
