import * as React from 'react'; import { FC, ReactNode, InputHTMLAttributes, TextareaHTMLAttributes, SelectHTMLAttributes } from 'react'; import { P as TimbalRuntimeProviderProps, y as ThreadProps, C as ChartArtifact } from './chat-rMlckAQG.cjs'; export { g as ChartSeriesConfig, E as ThreadVariant, M as TimbalChat, N as TimbalChatProps } from './chat-rMlckAQG.cjs'; import * as react_jsx_runtime from 'react/jsx-runtime'; export { B as Button, a as ButtonColor } from './button-C9cq2nm3.cjs'; export { a as Avatar, b as AvatarFallback, c as AvatarImage, d as AvatarVariant, B as Banner, e as BannerProps, K as Kanban, i as KanbanCardData, j as KanbanCardVariant, k as KanbanColumnData, l as KanbanDensity, m as KanbanDragHandleProps, n as KanbanLocation, o as KanbanMoveEvent, p as KanbanProps, q as KanbanRenderCardContext, r as KanbanTone, T as Timeline, s as TimelineItem, t as TimelineProps, U as UntitledButton, v as UntitledButtonColor, w as UntitledButtonProps, x as UntitledButtonSize } from './kanban-BdbkopVJ.cjs'; import '@assistant-ui/react'; import 'class-variance-authority/types'; import 'class-variance-authority'; import 'radix-ui'; import '@dnd-kit/core'; /** * Copy-paste into a workforce agent system prompt (or codegen tool context) so the * model knows which app-kit components exist and how to compose them — without * copying a single reference layout. * * @example * ```ts * import { APP_KIT_AGENT_INSTRUCTIONS } from "@timbal-ai/timbal-react/app"; * * const systemPrompt = `${basePrompt}\n\n${APP_KIT_AGENT_INSTRUCTIONS}`; * ``` */ declare const APP_KIT_AGENT_INSTRUCTIONS: string; type CatalogKind = "primitive" | "block"; type CatalogImportPath = "@timbal-ai/timbal-react/app" | "@timbal-ai/timbal-react/ui" | "@timbal-ai/timbal-react/studio"; interface CatalogEntry { /** Stable id (kebab-case). */ id: string; /** Display name (usually the primary export). */ name: string; kind: CatalogKind; /** Group label for docs/gallery. */ category: string; description: string; /** When an agent or author should reach for this. */ whenToUse: string; importFrom: CatalogImportPath; /** Exported symbols (root names) — verified against the barrel by a test. */ exports: string[]; /** Blocks only: the primitives/surfaces this composes. */ composedOf?: string[]; /** Blocks only: repo-relative source path to read/fork. */ source?: string; } /** The full catalog — blocks first, then primitives/surfaces. */ declare const APP_KIT_CATALOG: CatalogEntry[]; /** Look up a catalog entry by id. */ declare function getCatalogEntry(id: string): CatalogEntry | undefined; /** * The single source of truth for what generated Timbal UIs are *allowed* to * look like — the anti-slop constraint, encoded as data. * * Both the deterministic linter (`ui-lint.ts`) and the agent prompt * (`APP_KIT_AGENT_INSTRUCTIONS`) read from this module, so the rules a model is * told and the rules it is checked against can never drift apart. * * "Slop" = the generic AI-dashboard look: a decorative icon on every tile, * loud green/red trend pills, arbitrary palette colors, gratuitous dividers, * card-in-card nesting, bold giant numbers. Taste lives here, not in prose. * * This is a **public, documented** API (exported from the package root and * `/app`), in the same tier as the theme generator — not an internal class * composite. */ /** * Semantic color token roots the design system defines (see `styles.css`). * Generated code may only reach for colors through these — `bg-background`, * `text-muted-foreground`, `border-border`, `text-primary`, `bg-destructive`, * the timbal chrome extensions, etc. Anything else is slop. */ declare const SEMANTIC_COLOR_TOKENS: readonly ["background", "foreground", "card", "card-foreground", "popover", "popover-foreground", "primary", "primary-foreground", "secondary", "secondary-foreground", "muted", "muted-foreground", "accent", "accent-foreground", "destructive", "destructive-foreground", "border", "input", "ring", "sidebar", "sidebar-foreground", "sidebar-primary", "sidebar-primary-foreground", "sidebar-accent", "sidebar-accent-foreground", "sidebar-border", "sidebar-ring", "elevated-from", "elevated-to", "modal-from", "modal-to", "playground-from", "playground-via", "playground-to", "composer-bg", "composer-border", "composer-border-focus", "bubble-user", "bubble-user-foreground", "code-block-bg", "code-header-bg"]; type SemanticColorToken = (typeof SEMANTIC_COLOR_TOKENS)[number]; /** * Gradient / fill tokens reserved for **chrome only** (buttons, the elevated * surface, the modal shell, the playground backdrop). They must never decorate * a data card, a stat tile, or a list — that is the canonical "slop gradient". */ declare const RESERVED_GRADIENT_TOKENS: readonly ["primary-fill-from", "primary-fill-to", "primary-fill-hover-from", "primary-fill-hover-to", "primary-fill-active-from", "primary-fill-active-to", "secondary-fill-hover-from", "secondary-fill-hover-to", "secondary-fill-active-from", "secondary-fill-active-to", "destructive-fill-hover-from", "destructive-fill-hover-to", "destructive-fill-active-from", "destructive-fill-active-to", "ghost-fill-hover", "ghost-fill-active", "elevated-from", "elevated-to", "modal-from", "modal-to", "playground-from", "playground-via", "playground-to"]; /** * The Tailwind named palette. Any of these followed by a numeric shade * (`-50`..`-950`) in a color utility is a hardcoded color — not a token — * and is rejected by the linter. */ declare const TAILWIND_PALETTE_COLORS: readonly ["slate", "gray", "zinc", "neutral", "stone", "red", "orange", "amber", "yellow", "lime", "green", "emerald", "teal", "cyan", "sky", "blue", "indigo", "violet", "purple", "fuchsia", "pink", "rose"]; /** Tailwind color-bearing utility prefixes the linter inspects. */ declare const COLOR_UTILITY_PREFIXES: readonly ["bg", "text", "border", "ring", "from", "via", "to", "fill", "stroke", "decoration", "outline", "shadow", "divide", "accent", "caret"]; /** * Numeric budgets the linter enforces. Tuned for "tasteful dashboard", not * "icon confetti". A view that needs more icons than this is almost always * decorating instead of communicating. */ declare const SLOP_BUDGETS: { /** Max decorative/standalone icons rendered in a single generated file. */ readonly maxIconsPerView: 6; /** Max consecutive list rows separated by an explicit border/divider before * it reads as a "ruled table" — prefer spacing or zebra instead. */ readonly maxRowDividers: 2; }; /** * House-style rules in plain language. The prompt renders these verbatim and * the linter maps each `id` to a check, so the model is told and tested on the * exact same list. * * ## Severity policy (keep new rules honest) * * - **Correctness + theming integrity ⇒ lint `error`.** Silent runtime breaks * (invalid CSS, black charts, broken chat layout) and anything that punches * through the theme system (raw palette colors, hand-authored tokens). * - **Taste ⇒ `enforcement: "prompt-only"` (v2).** Style opinions (icons, * bold metrics, glow, uppercase, card nesting, hand-rolled controls) are no * longer linted at all — they moved to the screenshot critique rubric in * the `timbal-ui` skill, judged on rendered output where taste is actually * visible. On fork-first projects the component source is project-owned, * so control/chrome checks would flag the design system itself. The rules * stay in this list because legacy prompts still render them. * - **Prefer blocklists over allowlists.** "Don't do X" scales with better * models; "must use component Y" caps every generated app at that * component's quality. When a kit component keeps losing to hand-rolled * output, fix the component rather than tightening the rule. */ interface HouseRule { id: string; /** One-line imperative the agent reads. */ rule: string; /** Why it matters (kept short — models follow rules with rationale better). */ why: string; /** A wrong example (kept to a fragment). */ slop?: string; /** The tasteful equivalent. */ good?: string; /** * How the rule is enforced. `"lint"` (the default) means `ui-lint.ts` has a * deterministic check for it; `"prompt-only"` means the pattern can't be * detected with high enough precision to gate on, so it's taught in the * prompt but deliberately not linted (a low-precision check would false-flag * legitimate UIs under the strict gate). Keeping this explicit lets a test * assert every rule made a coverage decision — see `ui-lint.test.ts`. */ enforcement?: "lint" | "prompt-only"; } declare const HOUSE_RULES: readonly HouseRule[]; /** * Deterministic correctness linter for generated Timbal UI code (v2). * * Runs a dependency-free line scan over a `.tsx` string and flags patterns * that **silently break at runtime or punch through the theme system**: * hardcoded colors, `hsl(var(--token))` wrapping (invalid CSS → blank * charts), unsafe chart dataKeys, inline style colors, hand-authored theme * tokens / `forcedTheme`, and chat surfaces wrapped in bordered containers. * * ## v2 severity policy — correctness only * * Every check in this file is an **`error`**: it blocks regardless of model * tier because the output is objectively broken (invalid CSS, dead dark * mode, un-rebrandable app), not merely ugly. * * **Taste rules are no longer linted.** Icon budgets, bold metrics, glow, * uppercase headings, card nesting, hand-rolled rails and controls moved to * the screenshot critique rubric (the `timbal-ui` skill's `critique.md`) — * judged on rendered output, where taste is actually visible. On fork-first * projects the component source is project-owned, so "hand-rolled control" * checks would flag the design system itself. The corresponding * `HOUSE_RULES` entries are annotated `enforcement: "prompt-only"`. * * The linter intentionally avoids an AST dependency — the checks are * line/regex heuristics tuned for high precision. Feed * `formatLintReport(findings)` back to the generating agent (see * `reviewGeneratedUi`) so it can self-correct. * * Public, documented API — exported from the package root and `/app`. */ type LintSeverity = "error" | "warn"; interface LintFinding { /** Stable rule id (maps to a HouseRule where applicable). */ rule: string; severity: LintSeverity; /** 1-based line number in the supplied source. */ line: number; /** Human-readable explanation + fix. */ message: string; /** The offending source fragment (trimmed). */ snippet: string; } interface LintResult { findings: LintFinding[]; errorCount: number; warnCount: number; /** True when there are no `error`-severity findings. */ ok: boolean; } interface LintOptions { /** * Legacy knob (pre-4.0): treated warnings as errors. v2 emits no * warn-tier findings, so this flag has no effect — accepted so existing * callers and the `--strict` CLI flag keep working. */ strict?: boolean; } /** * Lint a single generated `.tsx` (or fragment) string for correctness. * * @example * ```ts * const { ok, findings } = lintGeneratedUi(generatedTsx); * if (!ok) sendBackToAgent(formatLintReport(findings)); * ``` */ declare function lintGeneratedUi(source: string, options?: LintOptions): LintResult; /** * Render lint findings as a compact, agent- and human-readable report. * Empty string when there are no findings. */ declare function formatLintReport(findings: LintFinding[]): string; /** * Layer 4 of the anti-slop system: the critique loop. * * `reviewGeneratedUi` runs the deterministic linter (Layer 2) and packages the * result into something a generating agent can act on in one round-trip — a * pass/fail verdict plus a ready-to-send revision prompt that names the exact * lines and fixes. The system prompt string (`UI_REVIEW_AGENT_INSTRUCTIONS`) * teaches the model to self-review *before* it returns code, so the loop is * usually closed without a second model call. * * Public, documented API — exported from the package root and `/app`. */ interface ReviewResult { /** Raw linter output. */ lint: LintResult; /** True when no blocking (error-severity, or any warning in strict mode) findings remain. */ passed: boolean; /** Compact human/agent-readable report ("" when clean). */ report: string; /** * A ready-to-send follow-up prompt instructing the agent to fix the findings, * or `null` when the UI passed. Send this back to the model and re-run * `reviewGeneratedUi` on its next output until `passed` is true. */ revisionPrompt: string | null; } /** * Review a generated `.tsx` string for slop and produce an actionable verdict. * * @example * ```ts * let code = await generate(userPrompt); * for (let i = 0; i < 2; i++) { * const review = reviewGeneratedUi(code, { strict: true }); * if (review.passed) break; * code = await generate(review.revisionPrompt!); // agent fixes named issues * } * ``` */ declare function reviewGeneratedUi(source: string, options?: LintOptions): ReviewResult; /** * Append to a UI-generation agent's system prompt so it self-reviews before * returning code. Pairs with `APP_KIT_AGENT_INSTRUCTIONS` and * `THEME_AGENT_INSTRUCTIONS`. */ declare const UI_REVIEW_AGENT_INSTRUCTIONS: string; /** * Design DNA — the per-project design specification. * * A `dna.json` file is the single durable artifact that records every visual * decision for a generated app: brand color, neutral temperature, surface * strategy, type system, shape, elevation, density, motion, and layout intent. * It is authored once (by an agent or a human), versioned with the project, * and compiled deterministically to `tokens.css` by `compileDna`. * * Design goals of the schema: * * - **Bounded freedom.** Every field is either an enum, a number in a * validated range, or a color that the compiler re-derives into a coherent * system. There is no way to express "hot pink text on lime" — bad raw * material can't enter. * - **Curated registries.** Typography comes from a curated pairing registry * (or explicit stacks), statuses and chart palettes from named recipes — * see `registries.ts`. * - **Durable consistency.** Because every later edit reads the same file, * page 12 looks like page 1, and a re-theme is a one-field diff. * * Validation is hand-rolled (no runtime deps — this ships inside a * self-contained CLI) and collects *all* problems before throwing, so an * agent can fix a bad file in one pass. */ type DnaSurfaceStrategy = "flat" | "panel" | "console"; type DnaMode = "light" | "dark"; type DnaStatusSetId = "signal" | "muted" | "vivid"; type DnaChartRecipeId = "categorical" | "brand" | "monochrome"; type DnaControlShape = "rounded" | "sharp" | "pill"; type DnaElevationLevel = "none" | "hairline" | "soft" | "medium" | "strong"; type DnaElevationStrategy = "border" | "shadow" | "both"; type DnaDensity = "compact" | "comfortable" | "spacious"; type DnaMotionPreset = "instant" | "snappy" | "smooth" | "expressive"; /** * Surface finish — the house rendering style for controls and canvases. * - `timbal` (default) — the signature Timbal chrome: soft canvas gradient * behind content, gradient-filled primary controls with an inset top * highlight, gently graded elevated/modal surfaces. All stops are derived * from the brand + neutrals, so it survives any rebrand. * - `flat` — degenerate stops (from == to, hairline control shadows): the * same component source renders plain flat shadcn-style surfaces. Use for * reference-matching flat designs. */ type DnaFinish = "timbal" | "flat"; /** * Personality axes, each 0–1. They bias derivation defaults when the * corresponding explicit field is omitted, and they are read by review * tooling as the project's intent record. 0.5 is always neutral. */ interface DnaPersonality { /** 0 = stripped/minimal, 1 = expressive/decorated. */ minimal_expressive?: number; /** 0 = flat (borders separate), 1 = dimensional (shadows separate). */ flat_dimensional?: number; /** 0 = cool neutrals, 1 = warm neutrals. */ cool_warm?: number; /** 0 = dense/data-first, 1 = airy/marketing. */ dense_airy?: number; /** 0 = sober/enterprise, 1 = playful/consumer. */ serious_playful?: number; } interface DnaReference { /** Where the reference came from. */ source: "mobbin" | "user" | "url" | "other"; /** Screen id, file path, or URL. */ ref: string; /** What was borrowed — e.g. "layout: split list+detail; warm canvas". */ borrowed?: string; } interface DnaMeta { /** Product/app name the DNA was authored for. */ name?: string; /** One-sentence design intent, e.g. "calm, data-dense logistics console". */ summary?: string; /** Provenance: which references informed this DNA and what was taken. */ references?: DnaReference[]; } interface DnaColor { /** Primary brand color — hex / rgb() / oklch(). The only required color. */ brand: string; /** * Optional secondary accent color. NOTE: this never tints functional * surfaces — dropdown/menu hovers, selected rows, canvases, and sheets * (`--accent`, `--playground-*`, `--sidebar-accent`) are always neutral * gray. Reserved for decorative use. */ accent?: string; /** * Neutral temperature. Defaults to PURE NEUTRAL (`chroma: 0`): white/gray/ * dark surfaces with gray hovers, regardless of how chromatic the brand * is — brand-washed canvases and tinted dropdown hovers are a named * mistake. Set `chroma` (0–0.03) explicitly to opt in to tinted neutrals; * `hue` defaults to the brand hue and only matters when chroma > 0. */ neutrals?: { hue?: number; chroma?: number; }; /** * Surface strategy: * - `flat` — canvas and cards share a background; borders separate. * - `panel` — gray canvas, lighter elevated cards (classic SaaS). * - `console` — dark-first, dense, near-black canvas with flat cards. */ surfaces?: DnaSurfaceStrategy; /** Which mode the app should boot into. Both are always compiled. */ defaultMode?: DnaMode; /** Status color set (success / warning / info / destructive). */ status?: DnaStatusSetId; /** * Selection-control accent — the checked/active fill for checkboxes, * radios, and similar binary controls (`--selection` / * `--selection-foreground`). Defaults to the status set's info blue. * Foreground is gated at 3:1 (graphical-object threshold) so vivid * accents survive verbatim. */ selection?: string; /** * Chart palette: a named recipe (`categorical` | `brand` | `monochrome`) * or an explicit array of 3–8 colors. */ charts?: DnaChartRecipeId | string[]; } interface DnaTypography { /** Curated pairing id from the font registry (see `registries.ts`). */ pairing?: string; /** Explicit stacks — override the pairing (or stand alone). */ sans?: string; display?: string; mono?: string; /** Stylesheet URL that loads the fonts (Google Fonts css2 etc.). */ importUrl?: string; /** Modular type-scale ratio, 1.1–1.4. Default 1.2. */ scale?: number; /** Body size in px, 13–17. Default 15. */ baseSize?: number; /** Heading weight. Default derives from personality (500/600). */ headingWeight?: 400 | 500 | 600 | 700; /** Heading letter-spacing personality. Default "normal". */ tracking?: "tight" | "normal"; } interface DnaShape { /** Base corner radius in rem, 0–1.5. Default 0.625. */ radius?: number; /** Control corner language: rounded (default) / sharp / pill. */ controls?: DnaControlShape; /** Border width in px for separating borders. Default 1. */ borderWidth?: 1 | 2; } interface DnaElevation { /** Shadow weight for cards/overlays. Default derives from personality. */ level?: DnaElevationLevel; /** What carries separation: borders, shadows, or both. Default "both". */ strategy?: DnaElevationStrategy; } interface DnaSpacing { /** Global density. Scales the Tailwind spacing unit + control heights. */ density?: DnaDensity; } interface DnaMotion { /** Motion personality: durations + easing set. Default "snappy". */ preset?: DnaMotionPreset; } /** * Layout intent — NOT compiled to tokens. This is the durable record of * structural decisions that every later edit must respect: the shell * archetype, nav model, page width. Review tooling reads it; the compiler * carries it through untouched. */ interface DnaLayout { /** Shell archetype, e.g. "sidebar", "topbar", "sidebar+topbar", "minimal", "split". */ shell?: string; /** Page content width, e.g. "boxed" | "full" | "narrow". */ pageWidth?: string; /** Free-form structural notes ("nav groups: Ops, Billing; detail = drawer"). */ notes?: string; } interface DnaVoice { /** Casing rule for headings/labels. Timbal house style is "sentence". */ case?: "sentence"; /** Copy tone notes, e.g. "direct, no exclamation marks". */ tone?: string; } /** * Escape hatch for one-off token tweaks. Values must be token-referential — * `var(...)`, `color-mix(...)`, `calc(...)`, `transparent`, or a plain * dimension. Raw color literals (`#hex`, `oklch(...)`, `rgb(...)`) are * rejected: the compiled system stays the single color source. * * Either a flat map (applies to both modes) or `{ light, dark }`. */ type DnaOverrides = Record | { light?: Record; dark?: Record; }; interface DesignDna { version: 1; meta?: DnaMeta; personality?: DnaPersonality; /** * House finish for controls + canvases. Defaults to `"timbal"` — new * projects ship the classic Timbal look unless the DNA opts out. */ finish?: DnaFinish; color: DnaColor; typography?: DnaTypography; shape?: DnaShape; elevation?: DnaElevation; spacing?: DnaSpacing; motion?: DnaMotion; layout?: DnaLayout; voice?: DnaVoice; overrides?: DnaOverrides; } declare class DnaValidationError extends Error { readonly problems: string[]; constructor(problems: string[]); } /** * Validate a parsed JSON value as a `DesignDna`. Collects every problem and * throws a single `DnaValidationError`, so one fix pass suffices. */ declare function parseDna(input: unknown): DesignDna; /** * Design DNA compiler — `dna.json` → `tokens.css`. * * Deterministic: the same DNA + compiler version always emits byte-identical * CSS, so drift (hand-edited tokens) is detectable by recompiling and * comparing (`timbal-dna check`). The compiler: * * 1. Resolves defaults (personality axes bias omitted fields). * 2. Derives complete paired light + dark neutral ladders from the surface * strategy + neutral temperature. * 3. Derives primary / accent / ring / sidebar / status / chart colors. * 4. **Contrast-fixes every foreground/background pair** (WCAG), recording * each adjustment in the report — unreadable text cannot be compiled. * 5. Emits `:root` / `.dark` custom properties plus Tailwind v4 `@theme` * blocks (colors, radius ladder, fonts, type scale, spacing/density, * shadows, easings) so utilities like `bg-card`, `rounded-control`, * `h-control`, `shadow-md`, `ease-standard` all carry the DNA. * * The emitted vocabulary is a superset of the shadcn/ui token contract, so * battle-tested shadcn-shaped components restyle without touching their * internals. */ declare const DNA_COMPILER_VERSION = "1.3.0"; interface DnaCompileReport { /** Contrast fixes the compiler applied (informational). */ adjustments: string[]; /** Non-blocking concerns worth a look (e.g. chart series similarity). */ warnings: string[]; } interface DnaCompileResult { /** The complete tokens.css contents. */ css: string; report: DnaCompileReport; /** Content fingerprint embedded in the header (FNV-1a over DNA + version). */ fingerprint: string; /** The mode the app should boot into (`dna.color.defaultMode`, default light). */ defaultMode: DnaMode; } declare function compileDna(dna: DesignDna): DnaCompileResult; /** * Curated registries the DNA compiler draws from. * * This is where taste is encoded as *data*: every font pairing, status set, * motion preset, and elevation ladder here has been chosen to read as a * designed product. The DNA schema lets a generator pick from these menus * (or supply explicit values within validated ranges) — it cannot invent * raw material below this quality floor. * * All Google Fonts URLs use the css2 API with explicit weight ranges so the * loaded payload stays small. */ interface FontPairing { id: string; label: string; /** Personality tags for agent selection ("what vibe does this carry?"). */ vibe: string[]; /** Body / UI stack. */ sans: string; /** Display/heading stack. Omitted = headings use `sans`. */ display?: string; /** Monospace stack. */ mono: string; /** Google Fonts css2 stylesheet loading every family above. */ importUrl: string; } declare const FONT_PAIRINGS: readonly FontPairing[]; declare function getFontPairing(id: string): FontPairing | undefined; /** * Solid status colors per mode, in OKLCH channel form. The compiler derives * foregrounds, subtle backgrounds, and subtle foregrounds from these anchors * and then contrast-fixes every pair. */ interface StatusAnchor { l: number; c: number; h: number; } interface StatusSet { id: DnaStatusSetId; label: string; light: { success: StatusAnchor; warning: StatusAnchor; destructive: StatusAnchor; info: StatusAnchor; }; dark: { success: StatusAnchor; warning: StatusAnchor; destructive: StatusAnchor; info: StatusAnchor; }; } declare const STATUS_SETS: readonly StatusSet[]; declare function getStatusSet(id: DnaStatusSetId): StatusSet; interface MotionSpec { id: DnaMotionPreset; label: string; /** Micro-interactions: hover tints, toggles. */ fast: string; /** Standard transitions: menus, popovers, accordions. */ base: string; /** Large surfaces: dialogs, drawers, page-level. */ slow: string; /** Default easing. */ ease: string; /** Entrance/emphasis easing. */ easeEmphasized: string; } declare const MOTION_PRESETS: readonly MotionSpec[]; declare function getMotionPreset(id: DnaMotionPreset): MotionSpec; interface ElevationLadder { id: DnaElevationLevel; /** Tailwind shadow scale, ascending. Paired light/dark values. */ light: { xs: string; sm: string; md: string; lg: string; xl: string; }; dark: { xs: string; sm: string; md: string; lg: string; xl: string; }; } declare const ELEVATION_LADDERS: readonly ElevationLadder[]; declare function getElevationLadder(id: DnaElevationLevel): ElevationLadder; interface DensitySpec { /** Tailwind v4 base spacing unit (all padding/gap utilities scale from this). */ spacingUnit: string; /** Control heights (md / sm / lg) in rem. */ control: string; controlSm: string; controlLg: string; } declare const DENSITY_SPECS: Record<"compact" | "comfortable" | "spacious", DensitySpec>; /** * Theme generator — turn brand *intent* into a complete, paired light + dark * token set, so agents and apps never hand-author `oklch(...)` or risk a * light-only override (the failure mode `theme-sanity.ts` warns about). * * The package owns the OKLCH math for every Timbal extension token (button * gradients, playground tint, ring, elevated surfaces) because deriving them * correctly requires knowing the full token contract in `styles.css`. Callers * only supply a brand color (and optional accent / radius / neutral tint). * * const theme = createTimbalTheme({ brand: "#4f46e5" }); * document head ← themeToCss(theme) // build-time / SSR * applyTimbalTheme(theme) // runtime, swappable * * Tokens are returned as `{ light, dark }` maps of CSS variable name → value. * Only the keys that change from the package defaults are included — overrides * cascade over `styles.css`, so unset tokens keep their shipped values. */ /** CSS variable name → value. */ type ThemeTokenMap = Record; interface TimbalThemeTokens { /** Variables applied in `:root` (light mode). */ light: ThemeTokenMap; /** Variables applied in `.dark`. */ dark: ThemeTokenMap; /** Mode-independent variables (e.g. `--radius`, `--font-sans`) applied once in `:root`. */ root?: ThemeTokenMap; /** * Font stack applied as `font-family` on the theme scope (and `body`), so * every component re-skins. Set independently of the `--font-*` vars so the * serializer can emit the actual `font-family` declaration. */ fontFamily?: string; /** * Optional stylesheet URL (e.g. a Google Fonts link) that loads the font in * `fontFamily`. Injected as a `` by `applyTimbalTheme` / `TimbalThemeStyle`; * for build-time `themeToCss` the host should add the link itself. */ fontImportUrl?: string; } /** Drop-shadow weight for cards / elevated surfaces. */ type ThemeShadow = "none" | "hairline" | "soft" | "medium" | "strong"; interface TimbalThemeTypography { /** Body / UI font stack. Applied as `font-family` and `--font-sans`. */ sans: string; /** Optional heading/display stack (`--font-display`; falls back to sans). */ display?: string; /** Optional monospace stack (`--font-mono`). */ mono?: string; /** Optional stylesheet URL that loads the fonts (Google Fonts CSS, etc.). */ importUrl?: string; } interface TimbalThemeIntent { /** Primary brand color — any CSS color: `#4f46e5`, `rgb(...)`, `oklch(...)`. */ brand: string; /** Optional secondary accent. Defaults to a desaturated brand. */ accent?: string; /** Corner radius in rem (maps to `--radius` + `--radius-2xl`). Default 0.75. */ radius?: number; /** * Tint neutral surfaces (background / muted / border) toward the brand hue * with very low chroma, for a more cohesive branded feel. Default `false` * keeps the shipped neutral grays. */ tintNeutrals?: boolean; /** * Full typography personality (font stacks + optional web-font URL). When * set, the generated theme re-skins every component's font. */ typography?: TimbalThemeTypography; /** Shadow weight for cards / elevated surfaces. Default keeps the shipped `medium`. */ shadow?: ThemeShadow; } /** * Derive a complete paired token set from brand intent. Pure — safe in SSR, * tests, and build scripts. */ declare function createTimbalTheme(intent: TimbalThemeIntent): TimbalThemeTokens; interface ThemeToCssOptions { /** * Scope the theme to elements matching `[data-timbal-theme=""]` * instead of `:root`. Useful for previewing several themes on one page * without touching the live document. */ scope?: string; /** Indentation for emitted declarations. Default two spaces. */ indent?: string; /** * Prepend an `@import url("…")` for the theme's `fontImportUrl`. Off by * default — `@import` must precede all other rules, so this is only safe when * the returned CSS is the entire stylesheet. Prefer loading the font with a * `` (which `applyTimbalTheme` / `TimbalThemeStyle` do automatically). */ includeFontImport?: boolean; } /** * Serialize a theme to a CSS string with paired `:root` (light) and `.dark` * blocks — the exact shape apps would otherwise hand-author. Writing this in a * single block guarantees light + dark stay in sync. When the theme carries a * `fontFamily`, a matching `font-family` rule is emitted so every component * re-skins (scoped to the subtree for previews, or `:root` + `body` globally). */ declare function themeToCss(theme: TimbalThemeTokens, options?: ThemeToCssOptions): string; /** * Ensure a stylesheet `` for a web-font URL exists in ``. Idempotent * per URL; replaces any previously injected theme font link. No-op in SSR. */ declare function ensureThemeFontLink(url: string | undefined): void; /** * Apply a theme at runtime by injecting (or replacing) a single managed * `