# Build with Layout UI · Foundation 0.1

## Framework integration contracts

Read /docs/integrations and /integrations/support.json for tested versions, working examples, and verification scope. Standalone supports multipage HTML and single-page views with mount/dispose cleanup. React, Next.js, Vue, Nuxt, Angular, Svelte, and Astro have tested Button, Input, Dialog, and Theme integration paths. Keep framework state ownership separate from native controllers. Do not run legacy component scripts against adapter-owned DOM. Match the initial server/client theme mode. Other catalog enhancements follow their own installation contracts; do not invent wrappers.

Use plain HTML, CSS, and JavaScript. No package installation or required bundler.
Read components.json before generating interface code. Do not invent component APIs.

## Structure
- production/public contains directly deployable static pages and assets.
- Each route is a directory with index.html. Keep links root-relative and omit trailing slashes except for /.
- css/app.css is the reusable foundation; css/site.css is website-only presentation.
- js/theme.js applies saved/system theme before paint; js/app.js enhances interactions.
- Start local development with ./dev from the repository root.

## Composition
Use .container, .stack, .cluster, .grid, .panel, .btn, .badge, and .muted.
Use --bg, --surface, --soft, --ink, --muted, --line, --accent, --on-accent,
--tint, --radius, and --space. Do not hardcode surface or text colors in components.
Theme values: light, dark. Palette values: cobalt, iris, grove, rose, amber, teal, sky, slate, orange.
Set data-theme and data-palette on html. System preference is managed by theme.js.

## Behavior
Start with real links, semantic buttons, labeled forms, details, and dialog.
Use native event listeners. Render user text with textContent.
No client router, reactive state API, auth layer, database, or request abstraction exists yet.
Demo project changes are temporary. Do not present them as persisted data.
Preserve progressive enhancement: navigation and documents work without JavaScript.
Keep shared navigation consistent across static pages when editing it.

## Verify
Run python3 ops/web/check.py. Check modified routes, keyboard focus, native validation,
small screens, light/dark modes, and all palettes. Verify no console errors.
Shared website budgets: CSS <22 KB raw; JS <8 KB raw. No external runtime resources.
Optional library CSS modules total <30 KB raw (includes the standalone link style). Library navigation/Create CSS <6.6 KB; browsing JS <3.9 KB; Create tools <18 KB raw.
Never include catalog assets in kits or other pages. Each library HTML page including previews and roadmap navigation <38 KB raw.
Measure actual performance before claiming speed or AI success-rate improvements.

## Quality targets
Target 100 in Lighthouse Performance, Accessibility, Best Practices, and SEO, and passing
all available agentic browsing checks. Record the tool/version, URL, device profile, and run date;
do not claim a score without a fresh report. Scores can vary and are not full accessibility certification.
Production packaging inlines the bounded foundation/site CSS and the small pre-paint theme initializer.
Do not add render-blocking network requests, CSS imports, or CSS URL dependencies without revisiting
that strategy. Keep other scripts module/defer/async; preserve no-JavaScript styling and saved themes.
Every push runs the fast integrity, packaging, routing, icon, motion, and size-budget checks.
Lighthouse and PageSpeed audits are manual checks, available through Developer → Tools, and never
run as part of deployment. Keep 100 as the quality target; investigate measured issues without
claiming a score from an old report. The optional local audit command uses isolated development
tooling, never a shipped dependency. A local pass does not guarantee public PageSpeed results.
Maintain one h1, no skipped descending heading levels, unique titles/descriptions, canonical URLs,
and crawlable semantic links. Keep visible labels, keyboard focus, named dialogs, and announced feedback.
Before release, manually check keyboard navigation, dialog focus return, zoom/reflow, reduced motion,
and contrast across all palettes and modes. Preserve DOM content and accessible names for browser agents.
Do not add packages, hidden SEO text, or fabricated structured data to chase audit scores.

## Icons
Read production/public/icons/GUIDE.md and manifest.json before creating or importing icons.
Use semantic names and the .icon class. Run python3 ops/web/icons.py to validate changes,
then python3 ops/web/icon_gallery.py to refresh the comparison gallery.
Keep inline copies and selected sprites synchronized when changing source artwork.

## Motion
Use shared --motion-* tokens and CSS presets first. Load js/motion.js only where needed.
Keep effects short, finite, and limited to opacity/transforms. Honor reduced-motion preferences.
Do not delay essential content, focus, validation, or navigation for an animation.
Custom effects must define their purpose, final state, and no-motion behavior.
Test rapid replay, cancellation, keyboard operation, and motion-disabled rendering.

## Consistency and drift prevention
Treat consistency as a high priority. Reuse existing components and shared tokens before adding variants.
Change shared defaults centrally and update documentation, examples, and checks together.
Use --motion-normal (300ms) and --motion-ease for default transitions and animations.
Pressed buttons change background without scaling. Do not add component-specific timing tokens.
Document purposeful exceptions; avoid one-off colors, spacing, icon styles, or interaction behavior.

## Interface language
Follow contentStyle in production/public/components.json (components.json in the published kit).
Use title case for navigation links and buttons, including button-style links and their accessible names.
Capitalize the first and last words and major words; keep short articles, conjunctions, and prepositions
(such as a, the, and, or, for, in, of, to, with) lowercase internally: Build with AI, Get Started, Dev Tools.
Keep headings, page titles, form labels, placeholders, statuses, and messages in sentence case.
Preserve official names and acronyms; use lowercase for literal technical identifiers. Short .eyebrow
labels may render uppercase through CSS while their source text stays sentence case.
Preserve proper names and user-entered text. Reuse wording and casing for identical actions.
Label links to /docs/ai as AI Guide. Documentation pagination follows sidebar order, with no
previous link on Introduction and no next link on AI Guide.

## Spacing consistency
Card padding owns outer spacing. Do not add outward margins to first or last children.
Stack gaps own internal rhythm; do not layer documentation prose margins onto stack children.
Keep prose-to-example spacing outside components, using shared spacing tokens.

## Grid and visual rhythm
Use the shared container: 1200px maximum width with 32px desktop, 24px tablet, and
16px mobile gutters. Align major section edges and reuse two- or three-column grids.
Use a 4px spacing scale: 4, 8, 12, 16, 20, 24, 32, 40, 48, 56, 64, 80.
Prefer 24px component gaps, 48px major column gaps, and 80px desktop section padding.
Keep related content closer than separate groups, prose near 65ch, and heading hierarchy clear.
Collapse columns when content needs space; never shrink controls or text to preserve a grid.
Borders, type sizes, icon strokes, and documented optical adjustments may fall off the spacing scale.

Use compact spacing for supporting strips: 48px desktop, 32px mobile. Major sections
use 80px desktop, 56px mobile. Keep each inter-section gap owned by one element.

## Selective library
The first collection lives at /library. Read library/manifest.json for exact markup, dependencies,
contracts and byte sizes. Include base and only the selected items’ transitive style dependencies,
once each; do not also include app.css. Static components use zero runtime JS; tooltips and dialogs include only their optional component script.
Browsing pages use catalog.js for search and theme previews, with no manifest fetch.
Only /create loads builder.js, kit.js, design.js, and archive.js for design and ZIP exports. Selection persists locally
when storage is available. No catalog or builder JavaScript is shipped inside a kit. No LayoutDB APIs exist yet.
Generate library/index.html, manifest.json and assets/*.css with python3 ops/web/library.py;
edit the generator and shared app.css sources, not generated copies. Run library.py --check and
node ops/web/test_library.mjs when changing library sources. Keep budgets measured and explicit.

## Navigation and extensible kits
Primary navigation: Docs, Components, Blocks, Charts, Templates, Create. Marketing and
comparison links live in the footer. ops/web/chrome.py owns shared chrome; run it after
changing navigation/footer, then regenerate the library. Library entries use top-level
category routes. Older /library/... URLs have noindex compatibility pages.
Create exports plain HTML/CSS, examples, and a validated layout-kit.json. Reopening that
file keeps the design and installed module hashes; add-ons contain only missing CSS, optional component scripts, and
new examples, never an index.html replacement. Unknown or changed module versions require
a deliberate upgrade. No AI-specific runtime or backend is required.

## Component examples
Present one focused behavior per example with its own preview, formatted HTML, native View Code disclosure, and Copy HTML action. Keep installation separate. Generate formatting and syntax colors with ops/web/examples.py; do not ship a browser syntax highlighter or add packages. Keep preview-only scaffolding out of exported component code.

Use logical alignment and spacing for RTL. Set dir="rtl" on the document or component and lang to the content language. Direction does not translate content. Library previews offer LTR/RTL controls; include an actual RTL-language example for directional components. Create exports the selected direction.

Disclosures use native details/summary. Labels are nonselectable; body content remains selectable. Default motion rotates the chevron and progressively animates intrinsic block size, an intentional layout-animation exception for expanding content. Use shared motion tokens, honor reduced motion and data-motion=none, and keep unsupported browsers functional without animation. Code disclosure bodies own a 16px internal gap and 24px bottom padding.

All interactive component motion uses the shared duration and easing by default, including selective kits. Base CSS owns reduced-motion and data-motion=none safeguards for elements, pseudo-elements, backdrops, and disclosure content. Keep focus and dismissal immediate; static cards, badges, tables, charts, and sections do not animate on load. Component scripts must not bypass these preferences.

Promote reusable site UI into framework components, blocks, or tokens, and use those same implementations on the site. Keep brand copy and page-specific layout in site styles; avoid adding site-only behavior or dependencies to exported kits. Animated standalone links use .link-underline.link-animated; prose links retain visible underlines.

Components, Blocks, Charts, and Templates have independent section sidebars. Desktop and mobile navigation list only entries in the current section; use the primary navigation to switch sections. The Library overview links to sections without duplicating every item in its sidebar.

ops/web/roadmap.json owns aspirational sidebar entries. Render them as dimmed noninteractive text under Planned, separately from available entries. Do not add planned items to selectable kits, component manifests, or placeholder routes. Remove or replace roadmap entries when their components ship; no release dates are implied.

## Component quality standard
Make APIs small and predictable, defaults polished, and examples focused and copyable.
Measure exported bytes and dependencies; static components require no runtime JavaScript.
Verify the exported component, not only its catalog preview: both themes, every palette,
narrow widths, long/localized text, RTL, zoom/reflow, and no-JavaScript behavior.
Check native semantics, names, text contrast (at least 4.5:1 for normal text), and keyboard
operation for interactive components. Static labels do not belong in the tab order.
Keep documentation crawlable as semantic HTML with meaningful headings and metadata.
SEO is a page concern; no component guarantees rankings or audit scores.
Motion must explain a change, use shared tokens, and honor reduced motion and data-motion=none.
Static badges do not animate on load. Announce only meaningful dynamic status changes.
Record checks performed and remaining limitations; superiority and perfect-score claims need evidence.
Badges default to pills; badge-rounded is the explicit rectangular option. Keep labels short,
allow essential text to wrap, and include visible status text or context for numeric counts.
Run python3 ops/web/test_badge.py when changing badge styles or theme colors.

Buttons retain native semantics: button for actions, anchor with href for navigation.
Set button type explicitly. Keep targets at least 44px, preserve visible keyboard focus,
and label icon-only controls. Use disabled only on buttons; aria-disabled alone does not
block events. Busy flows need application-owned request guards, recovery, and status
announcements. Use shared color transitions without lift or scale. Verify exported
button states match the site; do not fork hover or press rules in the kit generator.

Cards compose panel and stack with native header/footer; --space owns padding and gaps.
Use article for standalone content and context-appropriate headings. Static cards have
no tabindex, click handler, entrance motion, or hover lift. Keep actions native and avoid
nested interactive controls. Allow content-driven height, wrapping, and visible focus.
For media, reserve dimensions, supply appropriate alt text, and lazy-load only below the fold.
Run python3 ops/web/test_card.py when changing panel styles or theme colors.

Dialogs use native showModal for inertness and modal semantics. Supply a visible title,
a clear cancel/close control, and deliberate initial focus. Avoid backdrop dismissal for
forms and consequential decisions. Verify Escape, forward/reverse Tab containment, focus
return, invalid forms, rapid reopening, short viewports, RTL, and motion preferences.
Never equate closing a dialog with persistence success. Keep essential content available
outside JavaScript-only dialogs; application code owns requests and result announcements.
Run node ops/web/test_dialog.mjs for dialog runtime regressions, plus native browser focus and validation checks.

Disclosure quality: use native details with summary first, no nested interactive summary controls or redundant ARIA state. Use disclosure-content stack for rich bodies. Omit name for independent items; use a unique shared name for exclusive groups, with multiple-open fallback in older browsers. Native disclosures use Tab and Enter/Space, not accordion arrow keys. Keep essential instructions visible. Verify closed content leaves the tab order, long labels wrap, RTL spacing, rapid toggles, and motion opt-outs. No JavaScript is required.

Input quality: use /components/input, native input.input, and a field wrapper with a visible associated label. Provide appropriate type, name, autocomplete, and linked help/error text. Do not use placeholders as labels or block paste. Mark required visibly and natively; set aria-invalid only after validation and clear it with the error. Distinguish disabled from readonly; validate on the server. Keep 44px targets and 16px text. Test keyboard focus, autofill semantics, narrow/RTL layouts, contrast, and motion opt-outs. No runtime is required. Keep field as the stable kit/module identifier for compatibility.

Link quality: use real descriptive anchors with href; actions use buttons. link-underline has a persistent background underline for standalone links, with shared color feedback and immediate focus. Keep inline prose labels natural and standalone navigation title-cased. Disclose new tabs visibly with rel=noopener. Use plain explanatory text for unavailable destinations; anchors have no disabled attribute. Preserve native modifier-click, context menus, keyboard activation, downloads, and crawlable destinations. Verify wrapping, contrast, keyboard focus and motion opt-outs. No runtime is required. Run python3 ops/web/test_link.py.

Add link-animated to opt into a 300ms underline reveal on hover or keyboard focus for short standalone links. Native underlines remain the default for prose and long labels; omit link-underline for those. RTL reverses the reveal direction; motion opt-outs make it immediate.

Table quality: Use native table, caption, thead, tbody, and scoped column/row headers. Keep row labels readable and numbers aligned with table-number; format currency and dates in the application. Wrap wide tables in table-scroll with tabindex=0, role=region, and an accessible name tied to the caption; provide a visible scrolling hint when needed. Inside table-scroll, --table-min sets a readable minimum width (28rem default); use 100% for short tables that should fit. Text wraps while table-number preserves numeric values. Preserve column relationships on small screens instead of hiding important columns or converting cells to cards. Table-striped and table-compact are optional; retain usable targets when cells contain controls. Use descriptive links or buttons inside cells, never clickable or focusable rows. Use colspan matching the column count for empty-state messages. Keep source data in HTML for no-JavaScript access and crawlers; escape untrusted text or use textContent. This static table has no sorting, selection, filtering, pagination, virtualization, or fetching. Implement those separately only when needed; sorting requires native buttons, accurate aria-sort, and announcements of changes. Do not use role=grid unless implementing its complete keyboard model. Static rows have no entrance motion or misleading hover affordance; interactive children use shared motion and focus standards. Validate keyboard scrolling, zoom/reflow, RTL, theme contrast, and screen-reader header associations. Run python3 ops/web/test_table.py.

Tooltip quality: preserve an independent accessible trigger name; aria-describedby adds supplemental context and may contain other help IDs. No title attribute, nested controls, live region, or focus transfer is needed. Hints ignore pointer events and remain dismissible with Escape; dismissal persists until the pointer or focus leaves, or a new touch interaction requests the hint. Never hide required instructions, validation errors, or disabled-control explanations in a tooltip. Touch does not cancel the trigger action, so essential help must remain visible. Top/bottom/left/right are physical placement preferences. Keep text short enough for the viewport. Use shared 300ms opacity motion; a 120ms pointer-leave grace period avoids flicker and is not an animation duration. Pointer movement over the hint must not keep it open; keyboard focus remains persistent. Put data-motion=none on both elements or their common ancestor. Test keyboard, pointer travel, Escape, touch ordering, edge collisions, RTL, repeated initialization, and no-motion behavior. No packages or network requests are required; only selected tooltip kits include the script. Run node ops/web/test_tooltip.mjs.

Tooltip documentation has a 54 KB HTML budget for its separate nine-position fade and slide fixtures and six copyable examples; other library pages remain below 38 KB.

Tooltips default to fade. Opt into a 4px directional fade-and-slide with data-effect=slide on the tooltip. The resolved collision side determines movement; reduced motion and data-motion=none disable both.

Accordion quality: compose native details/summary in .accordion; accordion-bordered is optional. Include base, disclosure, and accordion styles once. Use a unique name per exclusive group or omit name for multiple expansion. Keep only one initial open item in an exclusive group. Native Tab and Enter/Space work without JavaScript; arrow/Home/End navigation and disabled items are not implemented. Older browsers may allow multiple items in named groups. Never add redundant ARIA states or nested summary controls. Introduce groups with a contextual heading and keep essential help visible. Reuse disclosure motion, spacing, and no-motion safeguards. Test keyboard, hidden body focus, single/multiple behavior, wrapping, RTL, and exported kits.

Use accordion-plus for the optional plus-to-cross indicator. Rotate 45 degrees using the shared disclosure transition; keep the indicator decorative and honor motion opt-outs. Chevron remains the default.

Library usage notes: ops/web/usage.py owns concise, task-focused guidance rendered under Usage and exported as structured usage entries in the AI manifest. Keep headings descriptive, paragraphs short, and code names in backticks. Preserve the detailed contract for machine reference; update both when component behavior changes. Do not render the full contract as a wall of prose.

Tabs: use .tabs[data-tabs], a labeled .tab-list, disabled source buttons with unique IDs and aria-controls, and source-visible section panels. tabs.js progressively supplies roles, selection, and roving focus. Support automatic/manual activation, RTL, vertical arrows, Home/End, disabled tabs, and motion opt-outs. Keep documentation section links as navigation. Never ship catalog navigation scripts in kits. Verify inactive panels leave the tab order and preserve source content without JavaScript.

Tabs documentation allows 49 KB HTML for seven complete copyable examples. Catalog section navigation retains real anchors and all visible sections, with aria-current=location and a shared-timing active marker. Sidebar heading aligns with the breadcrumb.

## Section navigation
Use section-nav with data-section-nav for sticky fragment links to visible section-target sections. Use Tabs only for mutually exclusive panels. Preserve native anchors, history and focus; aria-current=location follows scrolling. Set --section-nav-offset on a common ancestor for fixed headers. Keep navigation and sections in one containing block. Respect reduced motion and test horizontal overflow, RTL, deep links and browser Back.

Section navigation supports optional data-mobile="select": a labeled native dropdown at 760px and below, synchronized to scroll position. Selection uses native fragment navigation; without JavaScript preserve the links. Documentation pages enable this mode.

## Select
Use a labeled native select inside .select-control for single choices. Preserve native keyboard, form, validation, reset and mobile behavior. Place associated help/error text outside the wrapper. No custom popup, filtering or multiple selection is provided. Section navigation exports Select styles as a dependency for its optional mobile dropdown.

## Checkbox
Use label.checkbox with a native checkbox and span label; keep embedded links outside. Group with fieldset.checkbox-group and legend. Preserve native checked, disabled, required, form submission and reset. Mixed state uses the indeterminate DOM property and requires application-owned group/reset logic. Associate errors with aria-describedby and aria-invalid only after validation. Respect reduced motion, forced colors, RTL and 44px label targets.

## Radio
Use fieldset.radio-group with a visible legend and native inputs inside label.radio. Share a name within each group and use distinct values; independent groups need distinct names. Preserve native arrows, Space, Tab, form submission and reset. No mixed or readonly state. Mark required visibly, link errors with aria-describedby, and clear aria-invalid after correction. Never navigate or submit on selection. Keep 44px label targets, wrapping, RTL, forced colors and motion opt-outs. No runtime is required.

Radio documentation allows 43 KB HTML for eight complete, copyable grouped examples; exported Radio CSS stays below 1.5 KB with no runtime JS.

## Textarea
Use native textarea.input.textarea in field with a visible associated label, unique ID, name, and linked help/errors. Preserve vertical resizing, newlines, paste, undo, composition, and native form/reset behavior. Initial content belongs between tags. Explain required and length constraints; native lengths count UTF-16 code units. Distinguish readonly from disabled; validate on the server. No auto-grow, counter, persistence or runtime is included. Keep 16px text, fluid width, immediate focus, shared motion opt-outs, and RTL support.

## Password
Compose native input.input type=password and the progressive password visibility control. Use current-password/new-password autocomplete, preserve paste and password managers, and never log, store, send or prefill real secrets in examples. Hidden toggle is enabled by deferred password.js; keep type=button, matching aria-controls, and localized Show/Hide labels. Reset/pagehide remask. Disabled controls cannot reveal; readonly can. No strength meter or authentication is included. Verify keyboard, selection, repeat initialization, disabled fieldsets, RTL, narrow widths, reset, and no-JavaScript fallback.

Password documentation allows 65 KB HTML for seven complete input-and-toggle examples with both inline visibility icons. Keep eye/eye-off copies synchronized with the icon sources; aria-label describes the next action.

## Switch
Use native input type=checkbox role=switch inside label.switch, with a stable setting name. Checked owns accessible state; do not duplicate aria-checked or add custom keyboard behavior. No mixed or readonly state. Use for on/off settings, not consent. Keep 44px label targets, logical RTL thumb travel, shared 300ms motion and opt-outs, and forced-color fallback. Native submission/reset remain intact. Application code owns saving and errors; demos are temporary. No runtime is required.

Kit checks cover every singleton, pair, full-kit omission and 8,192 seeded samples. Use LAYOUT_EXHAUSTIVE=1 node ops/web/test_library.mjs for all combinations; keep routine deployment checks bounded as the catalog grows.

## Date picker
Use a native date input and optional progressive datepicker calendar. Preserve no-JavaScript entry, ISO date-only values, min/max/step, form submission/reset, and disabled/readonly semantics. Keep the keyboard reference in documentation; the calendar has no help tooltip or tooltip dependency. Hide the browser calendar indicator only after enhancement. Backdrop clicks dismiss without changing the date; no Cancel button is shown. Use an anchored nonmodal dialog on desktop and a native modal below 480px. Reposition on scroll, close on resize, and do not steal focus after outside interaction. Separate month and year buttons reveal keyboard-accessible selection grids. Native modal dialog owns Escape and focus containment; calendar uses one roving day, arrows, Home/End, Page Up/Down and Shift for years. Never convert date-only values through local time zones. Localize control labels/help separately from Intl date names, and explicitly choose week start. Gregorian dates with optional paired ranges; no natural-language parsing or persistence. Honor shared motion and opt-outs. Date picker has an explicit 32 KB raw runtime and 90 KB documentation budget for ten complete examples; run node ops/web/test_datepicker.mjs plus native browser interaction checks.

Time entry: pair Date picker with a labeled native input type=time, or use time alone with base and field styles and no JavaScript. Use step=60 for minutes, step=1 for seconds, and native min/max/required constraints. Enhanced fields match the picker’s 12/24-hour format; native fallback follows browser preferences; values are HH:mm or HH:mm:ss. Applications must explain and resolve scheduling time zones and daylight-saving ambiguity. Preserve native submission, reset, readonly and disabled behavior.

Styled time picker: .datepicker[data-timepicker] progressively enhances a native time field and hidden labeled button using Date picker assets. data-hour-format=12 enables AM/PM; default is 24-hour. Valid edits commit immediately; invalid edits retain the last valid value. Escape/outside close. Translate data-title/hour/minute/second/increase/decrease/period/invalid. Preserve native fallback and time zone guidance.

Date ranges: use data-date-range around two date pickers whose inputs have data-range-start and data-range-end. Preserve min/max/step; one shared calendar selects a draft start then an end, committing both values after the second selection; dismissal discards incomplete drafts. Preserve step bases. Same-day ranges are allowed. Typed reversed dates set native validity on the end and an optional data-range-error live message; translate data-range-message. Clearing removes the ordering constraint and reset revalidates. Validate ordering on the server; no-JavaScript inputs still work independently.

Time-picker chevrons and AM/PM remain visible but disabled when their next candidate violates native min/max/step constraints. Hide AM/PM only in 24-hour mode. Recheck after typing and constraint changes; invalid typed edits retain the last valid submitted value.

Enhanced date ranges use one visible date-range button with a localized value and calendar icon. Keep separate named native start/end fields in source as the no-JavaScript fallback and form values. The shared calendar applies both endpoints together. Route native validation feedback to the visible control.

Theme startup: apply the resolved browser color scheme before paint. Catalog previews load the shared theme initializer before their styles and inherit the parent theme/palette; keep preview bootstrap out of exported component HTML. Live theme changes also update color-scheme. Run node ops/web/test_theme.mjs. Tabs and Accordion documentation allow 512 extra bytes for preview startup tags (52,512 and 38,512 bytes respectively).

Documentation copy actions use the shared copy icon and Tooltip, with a stable accessible name and live copy feedback. Radio documentation allows 43,064 bytes for the shared tooltip stylesheet reference.

Color documentation: /docs/colors shows the actual semantic tokens for every shipped palette and theme. Generate it from app.css with python3 ops/web/colors.py after changing colors; --check detects stale swatches. Use semantic tokens in components rather than fixed swatch values.

Full color scales: ops/web/palette.py defines 19 families with 11 shades (50–950). /docs/colors/palette.css is optional, never bundled into the foundation. Raw --color-* values are fixed; semantic tokens adapt to nine palettes and two themes. Run colors.py --check and test_colors.py after edits. Six additional theme definitions increase the shared CSS budget to 24 KB, combined component CSS to 32 KB, and shared JS to 8,128 bytes.

Documentation separates /docs/colors (raw scales and optional palette CSS) from /docs/themes (theme selection, semantic tokens, and customization). Both are generated by ops/web/colors.py and linked in the docs sidebar.

Custom themes: Themes and Create share themeTokens/themeCSS in library/design.js. Preserve chosen --brand with --on-brand for primary buttons; derive --accent and --on-accent separately for semantic controls and readable links. Test all three neutral tones in both modes, bright/dark brands, and invalid hex input. The shared theme generator raises the combined Create JS budget to 21 KB.

Site controls should use the shipped components: wrap single-choice selects in .select-control and load select.css. The component retains native interaction; its unstyled browser arrow is only an intentional forced-colors fallback, not the default site presentation.

Site component adoption: use Input for text/search fields, Textarea for multiline fields, Select for single-choice dropdowns, Checkbox/Radio for their native control groups, and Dialog structure for modal surfaces. Load only the needed assets. Keep purpose-built navigation, swatches, palette dots, file/color/range inputs distinct when no equivalent component exists. Run python3 ops/web/test_site_components.py; source-only examples and browser fallbacks must be intentional.

Custom theme options: secondary is an optional six-digit hex string (empty by default for old kits). Export secondary brand/hover/on-brand and readable accent tokens only when configured; never reinterpret .btn.secondary as a secondary brand fill. Supported neutral tones: paper, neutral, gray, sand, sage, slate, rose. Validate imported settings, handoff parameters, and all text pairs in both modes.

## Product messaging and roadmap upkeep
Use “Design with AI. Build like a pro.” as the product promise. Explain the concrete foundation: thoughtful components, balanced spacing, consistent states, flexible themes, and readable code. Avoid unsupported quality or AI-performance claims.

## Icon reference
Before designing or revising icons, read /icons/GUIDE.md and always check the corresponding artwork at https://heroicons.com/. Use it to assess silhouette, optical balance and small-size clarity while preserving Layout UI’s soft outline language. Record licensing and attribution if adopting artwork.

Standalone site text links use `link-underline link-animated`, adopted by `chrome.text_links`. Keep native underlines for links within prose and preserve button/navigation styles. The Link documentation deliberately demonstrates both variants. Use shared motion tokens and immediate keyboard focus; honor reduced motion and data-motion=none.

Learn collection introductions are generated by ops/web/collection_guides.py at /docs/icons, /docs/components, /docs/blocks, and /docs/templates. The searchable icon browser is /icons, generated by icon_gallery.py. Main navigation and Build footer links open collections; documentation sidebar links open guides. chrome.DOCS owns Learn ordering and pagination.
