{
  "name": "Layout UI",
  "version": "0.1",
  "status": "experimental foundation",
  "stylesheet": "/css/app.css",
  "dependencies": [],
  "themes": [
    "light",
    "dark"
  ],
  "palettes": [
    "cobalt",
    "iris",
    "grove",
    "rose",
    "amber",
    "teal",
    "sky",
    "slate",
    "orange",
    "lime"
  ],
  "tokens": [
    "--bg",
    "--surface",
    "--soft",
    "--ink",
    "--muted",
    "--line",
    "--accent",
    "--accent-hover",
    "--on-accent",
    "--tint",
    "--space",
    "--radius",
    "--error"
  ],
  "primitives": {
    "stack": {
      "class": "stack",
      "purpose": "Vertical flow",
      "gapToken": "--space"
    },
    "cluster": {
      "class": "cluster",
      "purpose": "Wrapping horizontal row"
    },
    "grid": {
      "class": "grid",
      "purpose": "Responsive auto-fit columns",
      "gapToken": "--space"
    },
    "panel": {
      "class": "panel",
      "purpose": "Themed bordered surface",
      "rule": "Use panel and stack; --space controls card padding and stack gaps. Card children own no outward edge margins. Use semantic header/footer and context-appropriate headings. Static containers are not focusable or clickable; put native links or buttons inside. Avoid fixed heights and clipped focus rings. See /components/card. Use panel-link for a whole-card anchor with shared link and hover styling."
    },
    "button": {
      "class": "btn",
      "variants": [
        "secondary",
        "ghost"
      ],
      "elements": [
        "button",
        "a"
      ],
      "rule": "Use native buttons for actions and anchors with href for navigation. Set button type explicitly. Maintain 44px minimum targets; label icon-only controls. Native disabled blocks activation; aria-disabled alone does not. Busy state requires application handling, visible feedback, and completion/error announcements. Shared color motion respects reduced motion and data-motion=none. See /components/button. Use btn-icon for square 44px icon-only controls. Page CSS must not override shared component paint, typography, padding, or minimum target sizes.",
      "modifiers": [
        "btn-large",
        "btn-icon",
        "btn-pill"
      ]
    },
    "badge": {
      "class": "badge",
      "purpose": "Short status label; include text, not color alone",
      "variants": [
        "badge-neutral",
        "badge-outline",
        "badge-solid"
      ],
      "shapes": [
        "badge-rounded",
        "badge-count",
        "badge-pill (compatibility alias)"
      ],
      "rule": "Use a span for static labels. Include visible text, hide decorative icons, and give counts context. Do not add tabindex to static badges. See /components/badge for examples and the library manifest for the full contract.",
      "defaultShape": "pill"
    },
    "table": {
      "element": "table",
      "wrapper": "table-scroll",
      "modifiers": [
        "table-striped",
        "table-compact",
        "table-number"
      ],
      "rule": "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."
    },
    "icon": {
      "class": "icon",
      "manifest": "/icons/manifest.json",
      "guide": "/icons/GUIDE.md",
      "sizeToken": "--icon-size",
      "rule": "Use inline SVG or selected sprite; label the parent control and hide decorative SVGs."
    },
    "tooltip": {
      "class": "tooltip",
      "page": "/components/tooltip",
      "stylesheet": "/library/assets/tooltip.css",
      "script": "/library/assets/tooltip.js",
      "rule": "Use a focusable data-tooltip trigger with aria-describedby referencing a unique role=tooltip element with popover=manual. Plain text only; no interactive content or essential help. Escape dismisses. Optional script handles focus, pointer, touch and viewport positioning."
    },
    "dialog": {
      "class": "dialog",
      "page": "/components/dialog",
      "stylesheet": "/library/assets/dialog.css",
      "script": "/library/assets/dialog.js",
      "rule": "Use native dialog and showModal, with a visible title referenced by aria-labelledby. data-dialog-open and aria-controls reference its ID; data-dialog-close cancels. Initial focus favors a safe action or reading heading. Escape cancels and focus returns to the opener. Backdrop dismissal is opt-in via data-dialog-dismiss; omit it for forms and consequential decisions. Opening resets returnValue to cancel and scroll to the top. method=dialog returns explicit button values but never persists data. Use shared motion tokens and test reduced motion, focus containment, native validation, short screens, and RTL."
    },
    "link": {
      "class": "link-underline",
      "purpose": "Persistent underlined navigation with native browser behavior",
      "stylesheet": "/library/assets/link.css",
      "rule": "Use a native anchor with a meaningful href and descriptive visible text. link-underline keeps a persistent background underline for standalone links, sharing position and thickness with link-animated; hover and press use shared color transitions, with immediate keyboard focus. Base CSS handles reduced motion and data-motion=none. Keep prose links underlined and sentence-cased in context; standalone navigation uses title case. Use a button for actions, never href=# or javascript: as a substitute. Do not add role=button, redundant tabindex, or disabled to an anchor. For unavailable destinations render plain explanatory text instead of a fake disabled link. Prefer the same tab; if a new tab is needed, disclose it in visible text and use target=_blank with rel=noopener. Set aria-current=page only for the current navigation destination. Keep real hrefs for crawling, browser menus, and modifier-click; no click interception or runtime is required. Use spacious cluster or navigation layouts for standalone targets; inline prose links follow text flow. Do not truncate meaningful link labels. Downloads must identify their content and use a real file URL; download is a browser hint, not a guarantee. Do not claim ranking improvements from this component. 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.",
      "modifiers": [
        "link-animated"
      ]
    },
    "input": {
      "class": "input",
      "wrapper": "field",
      "modifiers": [
        "input-large"
      ],
      "rule": "Use a native input with class=input and a visible associated label. Compose label, control, and help in field. Use unique ids, name for submitted values, aria-describedby for help/errors, and appropriate type, autocomplete and inputmode. Placeholder is an example, never a label. Mark required visibly and with required. Set aria-invalid=true only after validation, describe how to fix the error, and clear the state when corrected. aria-invalid does not enforce validity; validate on the server too. Disabled controls cannot be edited, focused, or submitted; readonly values remain focusable and submitted. Do not block paste or password managers. Default targets are at least 44px with 16px input text; input-large is 52px. Shared color transitions respect reduced motion and data-motion=none; focus is immediate. No automatic animation, masking, floating labels, requests, storage, or runtime JavaScript is included. Keep instructions in semantic HTML; input styling does not improve search ranking. The field kit id and field.css filename remain stable for existing kits.",
      "url": "/components/input"
    },
    "colorPicker": {
      "class": "color-picker",
      "attribute": "data-color-picker",
      "page": "/components/color-picker",
      "variants": [
        "data-basic",
        "data-alpha",
        "data-hue-slider",
        "data-slider-only",
        "data-slider-swatch",
        "data-swatch-only"
      ],
      "rule": "Use the shared named hex field and swatch. data-hue-slider adds an inline hue rail; data-slider-only hides swatch and hex only after enhancement. data-hue labels the rail. Hue preserves saturation and brightness. Include selective Color Picker CSS and JS; retain no-JavaScript hex entry."
    }
  },
  "notImplemented": [
    "client routing",
    "reactive state",
    "API client",
    "authentication",
    "persistence"
  ],
  "motion": {
    "tokens": [
      "--motion-normal",
      "--motion-ease",
      "--motion-distance"
    ],
    "class": "motion-enter",
    "presets": [
      "fade",
      "slide",
      "scale"
    ],
    "optionalModule": "/js/motion.js",
    "exports": [
      "motion",
      "stagger",
      "revealOnScroll"
    ],
    "disable": "data-motion=\"none\"",
    "rule": "Reduced motion wins, including in selective kits. Use shared 300ms timing and easing for interactive states; static content stays still. Base CSS disables animations, transitions, and smooth scrolling for reduced motion or data-motion=none, including pseudo-elements and backdrops. Animation decorates state; never gate content, dismissal, or focus on completion.",
    "scrollReveal": {
      "attribute": "data-reveal=\"slide\" or \"fade\"",
      "behavior": "Optional once-only section entrances. Initially visible and focused sections stay still; content remains visible without JavaScript. Honors reduced motion and data-motion=none."
    }
  },
  "contentStyle": {
    "default": "Sentence case",
    "sentenceCase": "Use for headings, page titles, form labels, placeholders, statuses, tooltips, and messages. Capitalize the first word and proper nouns.",
    "titleCase": "Use for navigation links and buttons, including button-style links. Capitalize the first and last words and major words; keep short articles, conjunctions, and prepositions lowercase internally (a, the, and, or, for, in, of, to, with). Preserve official names and acronyms: Layout UI, GitHub, JavaScript, AI.",
    "lowercase": "Use for literal code, paths, filenames, identifiers, and user handles. Preserve user-entered text; do not lowercase ordinary interface copy for decoration.",
    "uppercase": "Preserve acronyms such as UI, AI, CSS, and API. Only short eyebrow labels may be visually uppercase through .eyebrow; author their source text in sentence case. Never uppercase sentences or action labels.",
    "consistency": "Use identical wording and casing for the same action everywhere. Navigation and button accessible names use title case and include the visible label; other accessible names use sentence case. Fix source text, never use CSS capitalization.",
    "examples": {
      "heading": "Create your first project",
      "button": "New Project",
      "navigation": "Getting Started",
      "field": "Email address",
      "status": "In progress",
      "eyebrowSource": "The foundation"
    }
  },
  "layout": {
    "container": ".container",
    "maxWidth": "1200px",
    "gutters": {
      "desktop": "32px",
      "tablet": "24px",
      "mobile": "16px"
    },
    "spacingScale": [
      4,
      8,
      12,
      16,
      20,
      24,
      32,
      40,
      48,
      56,
      64,
      80
    ],
    "rule": "Align section edges; use shared grids and gaps. Keep related items closer, prose readable, and collapse columns before content becomes cramped. Optical adjustments, type, and borders are exceptions.",
    "direction": {
      "supported": [
        "ltr",
        "rtl"
      ],
      "usage": "Set dir=\"rtl\" on html or a component and lang to its content language. Create preserves direction in kit settings. Direction does not translate text. Use logical spacing and text-align:start."
    }
  },
  "library": {
    "page": "/library",
    "manifest": "/library/manifest.json",
    "selection": "Include base plus the transitive dependencies of selected items, once each. Do not also include app.css. Catalog scripts are not component dependencies.",
    "generation": "python3 ops/web/library.py",
    "scope": "Native components, blocks and templates. Interactive components declare optional JavaScript dependencies. LayoutDB integration is planned, not implemented.",
    "kitBuilder": "/create",
    "categories": [
      "/components",
      "/blocks",
      "/charts",
      "/templates"
    ],
    "extend": "Save layout-kit.json from each export. Open it at /create to preserve design and installed module hashes. Add-ons contain only missing CSS and new examples. They never replace your index.html."
  },
  "qualityStandards": {
    "usability": "Use small predictable APIs, sensible defaults, focused copyable examples, and explicit limitations.",
    "performance": "Measure exported bytes and dependencies. Include only selected styles and scripts; static components need no runtime JavaScript.",
    "accessibility": "Verify semantics, accessible names, contrast, keyboard behavior where applicable, RTL, wrapping, and zoom/reflow in exported kits.",
    "discovery": "Keep documentation and meaningful content available as semantic HTML without JavaScript. SEO belongs to the containing page; components do not guarantee rankings or scores.",
    "motion": "Use shared motion tokens only for purposeful feedback. Respect reduced motion and data-motion=none. Static badges and content do not animate on load.",
    "verification": "Test exported components across themes, palettes, narrow widths, long content, and no-JavaScript operation. Report actual checks and limitations; do not claim superiority or perfect scores without evidence."
  },
  "canvas": {
    "attributes": {
      "data-canvas": [
        "dots",
        "grid"
      ],
      "data-ruler": "Optional decorative tick marks; use with data-canvas or a positioned container.",
      "data-guides": "Subtle inline alignment borders for grouped sections; content owns its padding."
    },
    "tokens": {
      "--canvas-step": "24px default; use 48px for a wider grid"
    },
    "usage": "Subtle CSS-only decorative surfaces. Uses theme line color, no interaction or animation. Pseudo-elements are reserved. Add content padding to clear the ruler. Not an interactive editor or measurement tool. Included in the foundation and kit base."
  },
  "surfaceTreatment": {
    "attribute": "data-finish",
    "values": [
      "flat",
      "gradient",
      "glass"
    ],
    "default": "flat",
    "scope": "Set on html, a section, or a component. Inherited by panel, surface, and filled buttons. Fields, ghost buttons, text and semantic status colors remain solid. Nested flat overrides inherited effects.",
    "classes": {
      "surface": "Apply the neutral surface treatment to a custom container.",
      "text-gradient": "Explicit accent gradient for short display text; do not apply to body copy."
    },
    "accessibility": "Glass keeps a 92% opaque themed surface, falls back to solid without backdrop-filter, and honors reduced transparency. Check contrast against actual backgrounds. Forced-color mode keeps gradient text readable. No animation or dependencies."
  },
  "validation": {
    "messageClass": "field-error",
    "token": "--error",
    "usage": "Set aria-invalid=true after validation and connect the corrective message with aria-describedby. Use field-error on the message. Clear both error state and message when corrected. Do not rely on color alone."
  },
  "integrations": {
    "status": "tested framework foundation; local prerelease",
    "docs": "/docs/integrations",
    "standalone": "/integrations/theme.js",
    "react": "packages/react",
    "components": [
      "button",
      "field",
      "dialog"
    ],
    "themeSchemaVersion": 1,
    "paintAttributes": [
      "data-paint-surface",
      "data-paint-border",
      "data-paint-text"
    ],
    "preset": "Prism",
    "scope": "Button, Input, Dialog and Theme integration contracts share native semantics and CSS. Other catalog components use their documented markup and optional assets. Framework runtimes are never loaded by standalone pages.",
    "customFonts": {
      "default": "local",
      "sources": [
        "local",
        "file",
        "google"
      ],
      "contract": "Explicit custom font configuration; no default network requests. WOFF2 files remain at the configured project URL."
    },
    "paints": [
      "surface",
      "border",
      "text",
      "button"
    ],
    "environments": [
      "standalone",
      "react",
      "next",
      "vue",
      "nuxt",
      "angular",
      "svelte",
      "astro"
    ],
    "supportMatrix": "/integrations/support.json",
    "examples": "examples/README.md"
  }
}
