diff --git a/.changeset/add-bucket-schema-and-merge.md b/.changeset/add-bucket-schema-and-merge.md new file mode 100644 index 0000000..4f3991c --- /dev/null +++ b/.changeset/add-bucket-schema-and-merge.md @@ -0,0 +1,27 @@ +--- +"ghost-expression": minor +--- + +Land the three-stage scan pipeline: topology (`map.md`) → objective (`bucket.json`) → subjective (`expression.md`). All three stages are now owned by `ghost-expression`; the previously separate `ghost-map` package is folded in. + +**New artifact: `ghost.bucket/v1`** — catalogues every concrete design value with structured specs, occurrence counts, and deterministic content-hashed IDs. + +**New verbs:** +- `ghost-expression inventory [path]` — emit deterministic raw repo signals as JSON (manifests, language histogram, registry, top-level tree, git remote). Feeds the topology recipe. (Migrated from `ghost-map inventory`.) +- `ghost-expression bucket ` — `merge` (concat with id-based dedup, idempotent — useful for modular rollups and fleet cohort views), `fix-ids` (recompute every row's `id` from content, so surveyor agents can author rows with empty `id` fields and finalize in one pass). +- `ghost-expression scan-status [dir]` — report which scan stages have produced artifacts (`map.md`, `bucket.json`, `expression.md`) and which stage to run next. The build-system glue orchestrators call between stages. + +**Updated verbs:** +- `ghost-expression lint` now auto-detects file kind by extension/content and dispatches to the right validator (`expression.md`, `map.md`, or `bucket.json`). + +**New skill recipes:** +- `map.md` — author `map.md` from a target (the topology stage). Migrated from the standalone `ghost-map` package. +- `survey.md` — author `bucket.json` from a target (the objective stage). Walks the agent through LLM-driven extraction with dialect-specific grep strategies, exhaustiveness discipline, and saturation predicate. +- `scan.md` — meta-recipe that orchestrates topology → survey → profile end-to-end via `scan-status` checkpoints. Use when the user wants a full scan rather than a specific stage. + +**Refactored skill recipe:** +- `profile.md` — now strictly the subjective interpretation stage. Reads `bucket.json` as ground truth; cannot fabricate values not in the bucket; cites bucket rows as evidence. Pre-requires `map.md` + `bucket.json`. Hard split from the previous one-pass extract+interpret recipe. + +**Removed:** the `ghost-map` package is deleted. `ghost.map/v1` schema and types now live in `@ghost/core`; `inventory` and `lint` (for `map.md`) move to `ghost-expression`. Consumers that imported from `ghost-map` should switch to `@ghost/core` (schemas/types) or `ghost-expression` (CLI verbs / library functions). + +Bucket schema, deterministic-id generation, lint, merge, and fix-ids primitives live in `@ghost/core`. diff --git a/.changeset/canonical-decision-vocabulary.md b/.changeset/canonical-decision-vocabulary.md new file mode 100644 index 0000000..190520b --- /dev/null +++ b/.changeset/canonical-decision-vocabulary.md @@ -0,0 +1,5 @@ +--- +"ghost-expression": minor +--- + +Add a controlled vocabulary of 12 canonical decision dimensions (`color-strategy`, `surface-hierarchy`, `shape-language`, `typography-voice`, `spatial-system`, `density`, `motion`, `elevation`, `theming-architecture`, `interactive-patterns`, `token-architecture`, `font-sourcing`) so fleet-aggregation primitives can group decisions across members. Profile recipe nudges authors toward canonical slugs; novel project-flavored slugs may pair with an optional `dimension_kind` that maps to a canonical bucket. New soft `non-canonical-dimension` lint warning suggests the closest canonical match. The schema accepts the optional `dimension_kind` field on `decisions[]`; existing expressions remain valid. diff --git a/.changeset/config.json b/.changeset/config.json index 3aa985a..346c54b 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -7,11 +7,5 @@ "access": "public", "baseBranch": "main", "updateInternalDependencies": "patch", - "ignore": [ - "ghost-ui", - "ghost-docs", - "@ghost/core", - "ghost-fleet", - "ghost-map" - ] + "ignore": ["ghost-ui", "ghost-docs", "@ghost/core", "ghost-fleet"] } diff --git a/.changeset/drop-roles.md b/.changeset/drop-roles.md new file mode 100644 index 0000000..e7a4cbc --- /dev/null +++ b/.changeset/drop-roles.md @@ -0,0 +1,6 @@ +--- +"ghost-expression": major +"ghost-drift": patch +--- + +Drop `roles[]` from the `expression.md` schema. Slot → token bindings either fall out of decisions[] (pattern consequences) or live in bucket.json components[] (exhaustive catalog). The hybrid `roles[]` slot was filling neither role cleanly and didn't scale to systems with many components. Existing files that carry `roles:` will fail strict lint — drop the section to migrate. Drift skill recipes that referenced `roles[]` as part of the expression frontmatter have been updated. diff --git a/.changeset/drop-signature-section.md b/.changeset/drop-signature-section.md new file mode 100644 index 0000000..b327d31 --- /dev/null +++ b/.changeset/drop-signature-section.md @@ -0,0 +1,9 @@ +--- +"ghost-expression": major +--- + +Drop the `# Signature` body section and `observation.distinctiveTraits` field from the `expression.md` schema. Every claim Signature carried — distinctive traits, load-bearing absences — was already covered better elsewhere: `rules[]` with `presence_floor` codifies absences as enforceable patterns, decisions cite evidence directly, and `observation.personality` carries the lightweight summarizing role. Signature had collapsed into restating frontmatter as bullets. + +The parser silently ignores legacy `# Signature` blocks in older `expression.md` files, so existing artifacts continue to load without erroring. The writer no longer emits the section, and lint no longer expects it. `INVARIANTS.md` §3 has been amended to remove "Signature" from the body partition list. + +Removed: `DesignObservation.distinctiveTraits` (TypeScript), `# Signature` body section, `observation.distinctiveTraits` field. Context-bundle emit now uses `observation.personality` for the trait-phrase hooks in README and skill description. diff --git a/.changeset/fix-oklch-backfill-and-tighten-survey.md b/.changeset/fix-oklch-backfill-and-tighten-survey.md new file mode 100644 index 0000000..374b9f8 --- /dev/null +++ b/.changeset/fix-oklch-backfill-and-tighten-survey.md @@ -0,0 +1,14 @@ +--- +"ghost-expression": patch +"ghost-drift": patch +--- + +Fix self-distance bug + tighten the survey recipe's exhaustiveness rule. + +**Bug fix.** `loadExpression` now backfills `oklch` on palette colors that arrive hex-only (frontmatter without an explicit `oklch` tuple). Without this, `comparePalette` treated hex-only colors as fully unmatched and contributed distance `1.0` per color — even when comparing an expression to itself. Self-distance was reported as 17.5% on a freshly authored expression. Backfill is deterministic (same hex → same oklch), so re-parsing the same file always yields the same in-memory shape. + +**Defensive fallback.** `comparePalette` also now resolves oklch on-the-fly when missing, and falls back to hex-string equality when even on-the-fly compute can't parse the color (CSS variables, opaque external refs). This covers third-party producers that don't backfill on write. + +**Recipe tightening.** `survey.md` now states the exhaustiveness rule up front and applies it per section. The rule is repo-agnostic — the recipe doesn't name specific signal sources (no "use registry.json"); the agent identifies the canonical signal in *this* repo, enumerates exhaustively, and cross-checks counts. New `Never sample` rule and explicit guidance against placeholder/glob names. Triggered by a dogfood scan that produced ~10% recall on `components[]` (6 rows for a 97-component package). + +**Schema cut: `bucket.libraries[]` removed.** External libraries (icon sets, primitive collections, motion libs, charting) no longer have a top-level bucket section. Whether a system uses Radix or hand-rolls primitives doesn't change what its design language *is*. When a library is load-bearing for the design language (icon family, font sourcing), it surfaces as prose evidence in the interpreter stage under the relevant decision dimension. Bucket sections are now `values`, `tokens`, `components`. `LibraryRow` / `LibraryRowSchema` / `libraryRowId` removed from `@ghost/core` exports. Existing `bucket.json` files with a `libraries` field will fail lint (use a no-op migration: drop the field). diff --git a/.changeset/v0-rules-and-perceptual-prior.md b/.changeset/v0-rules-and-perceptual-prior.md new file mode 100644 index 0000000..1eabb73 --- /dev/null +++ b/.changeset/v0-rules-and-perceptual-prior.md @@ -0,0 +1,5 @@ +--- +"ghost-expression": minor +--- + +Add `rules[]` as the v0 authoring surface for drift review and a perceptual-weight prior that calibrates emitted severity. An `expression.md` can now declare grep-friendly rules in frontmatter (id, canonical, kind, pattern, optional severity / match / tolerance / presence_floor / support); the emitter renders them as a Rams-shaped slash command grouped by Critical / Serious / Nit, with rationale + match shape + support cited per rule and a calibration footer that explains why severities landed where they did. Severity is computed from a fixed perceptual tier (color and font-family are loud, shape and elevation structural, spacing and motion-detail rhythmic) plus presence-floor escalation against the bucket — adding to a silent dimension is louder than tweaking a populated one. Existing `decisions[]`-driven expressions continue to emit the previous structured-section layout; the new rules-driven path activates only when `rules[]` is present. Ships a one-liner curl installer at `install/install.sh` so the skill bundle can be installed without npm or a build step; recipes carry prose fallbacks for the most-called CLI verbs (`scan-status`, `inventory`, `lint`) so the no-CLI path is real, not degraded. diff --git a/.gitignore b/.gitignore index a11daf6..6da3d47 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,6 @@ packages/ghost-ui/.ghost/ # Emitted skill bundle — re-generate with `ghost emit skill` .claude/skills/ + +# Scratch dir for dogfood scans and ad-hoc experiments +.scratch/ diff --git a/CLAUDE.md b/CLAUDE.md index f2597d3..935d653 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,10 +2,9 @@ Agents can ship UI now. The problem: what they ship drifts — wrong palette, wrong density, wrong hierarchy — because they have no canonical answer to "what does this project's design language *actually* look like." -Ghost is the layer that gives them one. The design language lives in your repo as `expression.md`. Agents read it before generating, compare against it after, and either correct the drift or codify the divergence as a deliberate change. Five tools split the loop: +Ghost is the layer that gives them one. The design language lives in your repo as `expression.md`. Agents read it before generating, compare against it after, and either correct the drift or codify the divergence as a deliberate change. A scan runs in three stages — topology (`map.md`) → objective (`bucket.json`) → subjective (`expression.md`) — all owned by `ghost-expression`. Four tools plus a reference design system split the loop: -- **ghost-map** — where the design system lives -- **ghost-expression** — what the design language is (the canonical artifact) +- **ghost-expression** — authors all three scan artifacts (`map.md`, `bucket.json`, `expression.md`); the canonical home of the design language - **ghost-drift** — when generated UI strays - **ghost-fleet** — how the language propagates across many projects - **ghost-ui** — a reference design system to test the loop against @@ -24,7 +23,6 @@ Run any tool's CLI after building: ```bash node packages/ghost-drift/dist/bin.js node packages/ghost-expression/dist/bin.js -node packages/ghost-map/dist/bin.js node packages/ghost-fleet/dist/bin.js # or via the workspace pnpm --filter ghost-drift exec ghost-drift @@ -61,18 +59,19 @@ Run `just` to list all recipes. Key ones: `setup`, `build`, `check`, `fmt`, `tes Ghost is **BYOA (bring-your-own-agent)**. The host agent — Claude Code, Codex, Cursor, Goose, whatever ships next — does the reading, deciding, and writing. The judgement work (profile, review, verify, remediate) lives in [agentskills.io](https://agentskills.io)-compatible skill bundles the agent executes. Ghost's CLIs are the calculator the agent reaches for when it needs a reproducible answer (vector math, schema validation, structural diffs); see [`INVARIANTS.md`](./INVARIANTS.md) §1 and §4 for the underlying constraints. -The repo decomposes into **five tools plus a reference design system**, each with a single responsibility: +The repo decomposes into **four tools plus a reference design system**, each with a single responsibility: ``` -@ghost/core library only — embedding math, types, target resolver, skill loader -ghost-map topology (map.md) — inventory, lint -ghost-expression authoring (expression.md) — lint, describe, diff, emit +@ghost/core library only — embedding math, target resolver, skill loader, + ghost.map/v1 schema, ghost.bucket/v1 schema +ghost-expression scan pipeline (map.md → bucket.json → expression.md) + — inventory, lint, describe, diff, bucket, emit ghost-drift drift (.ghost/*) — compare, ack, track, diverge, emit skill ghost-fleet elevation (fleet.md) — members, view, emit skill ghost-ui reference design system — 97 shadcn components + MCP server ``` -Dependency flow: `@ghost/core` ← everyone. `ghost-expression` ← `ghost-drift`, `ghost-fleet`. `ghost-map` ← `ghost-fleet`. No cycles. +Dependency flow: `@ghost/core` ← everyone. `ghost-expression` ← `ghost-drift`, `ghost-fleet`. No cycles. Each tool lives under `packages//` with the same shape: @@ -86,25 +85,25 @@ Each tool lives under `packages//` with the same shape: | Package | Published? | Description | |---------|-----------|-------------| -| `packages/ghost-core` | ❌ private (`@ghost/core`) | Workspace-only library. Embedding math, shared types, target resolution, skill-bundle loader. No CLI. Consumed by every other tool. | +| `packages/ghost-core` | ❌ private (`@ghost/core`) | Workspace-only library. Embedding math, shared types, target resolution, skill-bundle loader, `ghost.map/v1` schema, `ghost.bucket/v1` schema + lint/merge/fix-ids primitives. No CLI. Consumed by every other tool. | | `packages/ghost-drift` | ✅ `ghost-drift` on npm (v0.2+) | Drift detection. CLI verbs: `compare`, `ack`, `track`, `diverge`, `emit skill`. Skill recipes: `compare.md`, `review.md`, `verify.md`, `remediate.md`. Old `lint`/`describe`/`emit review-command`/`emit context-bundle` stay registered as stub commands that point users to `ghost-expression`. | -| `packages/ghost-expression` | ✅ intended-public (`publishConfig.access: public`, currently v0.0.0) | Authoring & validating `expression.md`. CLI verbs: `lint`, `describe`, `diff`, `emit` (kinds: `review-command`, `context-bundle`, `skill`). Skill recipes: `profile.md`, `schema.md`. Owns the canonical artifact. | -| `packages/ghost-map` | ❌ private (currently) | Generates `map.md` — the navigation card every Ghost tool reads. CLI verbs: `inventory` (raw signals as JSON), `lint`. Will eventually publish; gated on the `describe` and `emit skill` follow-ups. | +| `packages/ghost-expression` | ✅ intended-public (`publishConfig.access: public`, currently v0.0.0) | Owns the three-stage scan pipeline (`map.md` topology → `bucket.json` objective → `expression.md` subjective). CLI verbs: `lint` (auto-detects file kind), `inventory`, `describe`, `diff`, `bucket ` (merge / fix-ids), `emit` (kinds: `review-command`, `context-bundle`, `skill`). Skill recipes: `map.md`, `survey.md`, `profile.md`, `schema.md`. | | `packages/ghost-fleet` | ❌ private | Read-only elevation across many `(map.md, expression.md)` members. CLI verbs: `members`, `view`, `emit skill`. Skill recipes: `target.md`. | | `packages/ghost-ui` | ❌ private | Reference component library — 49 UI primitives + 48 AI elements + theme + hooks, distributed via the shadcn `registry.json`, not npm. Also ships the `ghost-mcp` bin (`src/mcp/`, built via `tsconfig.mcp.json` → `dist-mcp/`) — an MCP server re-exposing the registry to AI assistants (5 tools, 2 resources). | | `apps/docs` | ❌ private | The deployed docs site (`ghost-docs`) — home, drift tooling docs, design language foundations, live component catalogue. Consumes `ghost-ui`. | ## CLI Commands -Verbs are scoped to the tool that owns the artifact. The full surface across all four tools: +Verbs are scoped to the tool that owns the artifact. The full surface across all three tools: | Tool | Command | Description | |------|---------|-------------| -| `ghost-map` | `inventory [path]` | Emit raw repo signals (manifests, language histogram, registry, top-level tree, git remote) as JSON. | -| `ghost-map` | `lint [map]` | Validate `map.md` against `ghost.map/v1`. | -| `ghost-expression` | `lint [expression]` | Validate `expression.md` schema + body/frontmatter coherence. | +| `ghost-expression` | `inventory [path]` | Emit raw repo signals (manifests, language histogram, registry, top-level tree, git remote) as JSON. Feeds the topology recipe. | +| `ghost-expression` | `scan-status [dir]` | Report which scan stages have produced artifacts (`map.md` / `bucket.json` / `expression.md`) and which stage to run next. | +| `ghost-expression` | `lint [file]` | Validate `expression.md`, `map.md`, or `bucket.json` — auto-detects the kind from path/content. | | `ghost-expression` | `describe [expression]` | Print section ranges + token estimates (so agents can selectively load). | -| `ghost-expression` | `diff ` | Structural prose-level diff (decisions + palette roles). **Not** vector distance. | +| `ghost-expression` | `diff ` | Structural prose-level diff between expressions (decisions + palette roles). **Not** vector distance. | +| `ghost-expression` | `bucket [...buckets]` | Operate on `ghost.bucket/v1` files. Ops: `merge` (concat with id-based dedup), `fix-ids` (recompute IDs from content). | | `ghost-expression` | `emit ` | Derive an artifact from `expression.md`: `review-command`, `context-bundle`, or `skill`. | | `ghost-drift` | `compare [...expressions]` | Pairwise (N=2) or composite (N≥3) over expression embeddings. `--semantic`, `--temporal`. | | `ghost-drift` | `ack` | Record a stance toward the tracked expression in `.ghost-sync.json`. | @@ -117,16 +116,16 @@ Verbs are scoped to the tool that owns the artifact. The full surface across all **Workflows (agent recipes).** Each tool ships its own skill-bundle references under `packages//src/skill-bundle/references/`. These are the agent's job, not CLI verbs: -- **Profile** (write `expression.md` from a project) — `ghost-expression/.../profile.md` -- **Map** (write `map.md` from a repo) — `ghost-map/.../map.md` +- **Scan** (orchestrate topology → survey → profile end-to-end) — `ghost-expression/.../scan.md` +- **Map** (write `map.md` from a repo, the topology stage) — `ghost-expression/.../map.md` +- **Survey** (write `bucket.json` from a target, the objective stage) — `ghost-expression/.../survey.md` +- **Profile** (interpret a `bucket.json` into `expression.md`, the subjective stage) — `ghost-expression/.../profile.md` - **Review** (flag drift in PR changes) — `ghost-drift/.../review.md` - **Verify** (generate → review loop) — `ghost-drift/.../verify.md` - **Compare interpretation** — `ghost-drift/.../compare.md` - **Remediate** (suggest minimal fixes for drift) — `ghost-drift/.../remediate.md` - **Fleet narrative** (synthesize `fleet.md` prose from CLI output) — `ghost-fleet/.../target.md` -`discover.md` and `generate.md` are dropped from scope in the decomposition (per `docs/ideas/phase-0-decisions.md`); they are not migrated to any tool. - ## Target Types The `resolveTarget()` function in `@ghost/core` (`packages/ghost-core/src/target-resolver.ts`) accepts: @@ -142,16 +141,17 @@ Used by `resolveTrackedExpression` (in `ghost-drift`) and legacy library consume ## Canonical artifacts -Two canonical Markdown artifacts, each owned by one tool: +Three artifacts produced in sequence by a scan, all owned by `ghost-expression`: -- **`expression.md`** — the design language. Owned by `ghost-expression`. Human-readable, LLM-editable, with YAML frontmatter (machine layer: 49-dim embedding + palette/spacing/typography/surfaces/roles) and a three-section prose body (Character → Signature → Decisions). See `docs/expression-format.md` for the full spec; the condensed reference ships at `packages/ghost-expression/src/skill-bundle/references/schema.md`. -- **`map.md`** — the topology card. Owned by `ghost-map`. Human-readable answer to "where is the design system, which folders matter?" Schema is `ghost.map/v1`, validated by `ghost-map lint`. The condensed reference ships at `packages/ghost-map/src/skill-bundle/references/schema.md`. The repo's own `map.md` lives at the root. +- **`map.md`** — the topology card (stage 1). Human-readable answer to "where is the design system, which folders matter?" Schema is `ghost.map/v1` (lives in `@ghost/core`), validated by `ghost-expression lint map.md`. Authored from `ghost-expression inventory` + the `map.md` skill recipe. The repo's own `map.md` lives at the root. +- **`bucket.json`** — the objective scan (stage 2). Catalogues every concrete design value (colors, spacings, typography, radii, shadows, breakpoints, motion, layout primitives) plus tokens and components observed in the target. Each row carries occurrence counts and a deterministic content-hashed `id`. Schema is `ghost.bucket/v1` (lives in `@ghost/core`); three sections — `values`, `tokens`, `components`. External libraries (icons, primitives, charting) deliberately *do not* have a bucket section — whether a system uses Radix or hand-rolls primitives doesn't change what its design language *is*; load-bearing library choices surface as prose evidence in the interpreter stage. Validated by `ghost-expression lint bucket.json`. Authored via the `survey.md` skill recipe. +- **`expression.md`** — the design language (stage 3, terminal). Human-readable, LLM-editable, with YAML frontmatter (machine layer: 49-dim embedding + palette/spacing/typography/surfaces/roles) and a three-section prose body (Character → Signature → Decisions). Authored by interpreting `bucket.json` per the `profile.md` skill recipe. See `docs/expression-format.md` for the full spec; the condensed reference ships at `packages/ghost-expression/src/skill-bundle/references/schema.md`. ## Releasing & Changesets -`ghost-drift` is the only currently-published package. `ghost-expression` is set up to publish (`publishConfig.access: public`); `ghost-map` and `ghost-fleet` are private workspace-only for now. Releases go through [Changesets](https://github.com/changesets/changesets); the `.github/workflows/release.yml` workflow opens a "Version Packages" PR whenever pending changesets are on `main`, and publishes to npm when that PR merges. +`ghost-drift` is the only currently-published package. `ghost-expression` is set up to publish (`publishConfig.access: public`); `ghost-fleet` is private workspace-only for now. Releases go through [Changesets](https://github.com/changesets/changesets); the `.github/workflows/release.yml` workflow opens a "Version Packages" PR whenever pending changesets are on `main`, and publishes to npm when that PR merges. -The Changesets config ignores private packages (`@ghost/core`, `ghost-fleet`, `ghost-map`, `ghost-ui`, `apps/docs`) — they don't appear in version PRs. +The Changesets config ignores private packages (`@ghost/core`, `ghost-fleet`, `ghost-ui`, `apps/docs`) — they don't appear in version PRs. **When you (the agent) complete a user-visible change to a published package, write a changeset file yourself instead of asking the user to run `pnpm changeset`.** Create `.changeset/.md` with this shape: @@ -188,5 +188,5 @@ The slug should be short and descriptive: `add-temporal-flag.md`, `fix-palette-l - `ghost-drift compare` takes **file paths** to `expression.md`, not target strings. Mode auto-detects from N and flags: `--semantic` / `--temporal` require N=2; N≥3 returns a composite expression. - `ghost-drift ack` / `track` / `diverge` read the local `expression.md`. The host agent is responsible for regenerating `expression.md` (via the `profile` recipe) before acknowledging drift. - `ghost-expression lint` takes a single `expression.md` and reports schema/partition violations. Use as the success gate when authoring an expression. -- `ghost-map lint` takes a single `map.md` and validates against `ghost.map/v1`. Use as the success gate when authoring a map. +- `ghost-expression lint ` validates against `ghost.map/v1` (auto-detected by frontmatter or filename). Use as the success gate when authoring a map. - The CLI manifest at `apps/docs/src/generated/cli-manifest.json` is auto-generated by `pnpm dump:cli-help`. CI guards drift via `pnpm check:cli-manifest`. Re-run `pnpm dump:cli-help` after adding/removing flags or verbs to any tool. diff --git a/INVARIANTS.md b/INVARIANTS.md index ced4361..34ac373 100644 --- a/INVARIANTS.md +++ b/INVARIANTS.md @@ -30,7 +30,7 @@ One on-disk format per expression. No parallel JSON/YAML/TOML/DTCG representatio ## 3. The frontmatter / body partition is strict. -Frontmatter holds the machine layer: identity, tokens, dimension slugs, evidence paths. Body holds the prose layer: Character, Signature, decision rationale. Prose does not leak into frontmatter; structured data does not leak into prose. +Frontmatter holds the machine layer: identity, tokens, dimension slugs, evidence paths. Body holds the prose layer: Character, decision rationale. Prose does not leak into frontmatter; structured data does not leak into prose. **Why:** The partition is what makes expressions simultaneously human-readable and machine-comparable. Blurring it forces one audience to do the other's work. The linter enforces it at the boundary; this invariant enforces it in spirit. diff --git a/README.md b/README.md index da1e7cc..2860501 100644 --- a/README.md +++ b/README.md @@ -12,19 +12,18 @@ Every Ghost workflow runs in the host agent you already use — Claude Code, Cod No API key is required to run any CLI verb. Each tool's `emit skill` verb installs its bundle into your agent. -## The five tools +## The four tools -Ghost is split into one responsibility per tool, around two canonical Markdown artifacts. +Ghost is split into one responsibility per tool. A scan produces three artifacts in sequence — `map.md` (topology) → `bucket.json` (objective values) → `expression.md` (subjective interpretation) — all owned by `ghost-expression`. | Tool | Owns | Verbs | | --- | --- | --- | -| **`ghost-map`** | `map.md` — the topology card answering "where is the design system, which folders matter?" | `inventory`, `lint` | -| **`ghost-expression`** | `expression.md` — the canonical design language artifact | `lint`, `describe`, `diff`, `emit` | +| **`ghost-expression`** | the three-stage scan pipeline (`map.md`, `bucket.json`, `expression.md`) | `inventory`, `lint`, `describe`, `diff`, `bucket `, `emit` | | **`ghost-drift`** | `.ghost/history.jsonl` + `.ghost-sync.json` — drift detection and stance | `compare`, `ack`, `track`, `diverge`, `emit skill` | | **`ghost-fleet`** | `fleet.md` — read-only elevation across many `(map.md, expression.md)` members | `members`, `view`, `emit skill` | | **`ghost-ui`** | A reference design system Ghost dogfoods — 97 shadcn components + an MCP server | (no verbs) | -`@ghost/core` underneath is a workspace-only library with the embedding math, target resolver, and skill-bundle loader the four CLIs share. +`@ghost/core` underneath is a workspace-only library with embedding math, target resolution, skill-bundle loader, and the `ghost.map/v1` + `ghost.bucket/v1` schemas the three CLIs share. ## Why Ghost? @@ -34,23 +33,42 @@ Ghost gives agents four capabilities the design-at-scale problem actually needs: - **Self-govern at author time**: the `review` and `verify` recipes (in the `ghost-drift` skill bundle) run an agent's output against the expression *before* a human sees it. Drift gets caught where it's cheap to fix, not after it ships. - **Detect drift at the right time**: PR-time (via `review`), generation-time (via `verify`), or org-time (via `ghost-drift compare` on N≥3 expressions, or `ghost-fleet view` for the full elevation). Timing is load-bearing: the same drift surfaced a month later is noise; surfaced inline, it's action. - **Remediate with structured intent**: `ack`, `track`, `diverge` are the three moves. Every stance is published with reasoning and full lineage. Drift without intent is noise; drift with intent becomes useful evidence. -- **Human-readable, diff-friendly**: `expression.md` is Markdown with YAML frontmatter (machine layer) plus a three-layer prose body (Character, Signature, Decisions). `map.md` is the same shape for topology. Humans read them, agents consume them, Ghost's CLIs diff them. No DSL to learn. +- **Human-readable, diff-friendly**: `expression.md` is Markdown with YAML frontmatter (machine layer) plus a prose body (Character, Decisions). `map.md` is the same shape for topology. Humans read them, agents consume them, Ghost's CLIs diff them. No DSL to learn. ## Repo layout -Ghost is a pnpm monorepo. Five tools, one reference design system, one docs site. +Ghost is a pnpm monorepo. Four tools, one reference design system, one docs site. | Path | Role | Published? | | ---- | ---- | --- | -| [`packages/ghost-core`](./packages/ghost-core) | Workspace-only shared library — embedding math, types, target resolver, skill loader. | ❌ private (`@ghost/core`) | -| [`packages/ghost-map`](./packages/ghost-map) | `map.md` topology generator + linter. | ❌ private (today) | -| [`packages/ghost-expression`](./packages/ghost-expression) | `expression.md` authoring + emit pipeline. | ✅ intended-public on npm | +| [`packages/ghost-core`](./packages/ghost-core) | Workspace-only shared library — embedding math, target resolver, skill loader, `ghost.map/v1` + `ghost.bucket/v1` schemas. | ❌ private (`@ghost/core`) | +| [`packages/ghost-expression`](./packages/ghost-expression) | The three-stage scan pipeline: `map.md` topology → `bucket.json` objective → `expression.md` subjective. Authoring, lint, describe, diff, bucket ops, emit. | ✅ intended-public on npm | | [`packages/ghost-drift`](./packages/ghost-drift) | Drift detection + governance verbs. | ✅ `ghost-drift` on npm | | [`packages/ghost-fleet`](./packages/ghost-fleet) | Fleet elevation across many members. | ❌ private | | [`packages/ghost-ui`](./packages/ghost-ui) | Reference design system: 97 shadcn components + the `ghost-mcp` MCP server. | ❌ private (distributed via shadcn registry, not npm) | | [`apps/docs`](./apps/docs) | Deployed docs site (`ghost-docs`). | ❌ private | -Dependency flow: `@ghost/core` ← everyone. `ghost-expression` ← `ghost-drift`, `ghost-fleet`. `ghost-map` ← `ghost-fleet`. No cycles. +Dependency flow: `@ghost/core` ← everyone. `ghost-expression` ← `ghost-drift`, `ghost-fleet`. No cycles. + +## Quick install + +If you just want the design-language scan + emit recipes installed into your host agent — no Node, no pnpm, no build: + +```bash +curl -fsSL https://raw.githubusercontent.com/block/ghost/main/install/install.sh | sh +``` + +The installer detects your agent (`claude` / `cursor` / `codex` / `opencode`), drops the `ghost` skill bundle into the right skills directory (e.g. `~/.claude/skills/ghost/`), and tells you what to do next. Pass `--agent claude` (or `--dest `) to override detection. Re-run with `--force` to upgrade. + +After install, in any repo: + +``` +> Scan this project with ghost +``` + +The agent walks `map.md` → `bucket.json` → `expression.md`, then emits a `/design-review` slash command tuned to your design language. The recipes work without any Ghost CLI on PATH — every CLI-using step has a prose fallback. + +If you want the deterministic CLI helpers (faster lint, embedding math, structural diff, fleet view), install from source instead — see *Getting Started* below. ## Getting Started @@ -80,11 +98,11 @@ Once a skill is installed, ask your agent in plain English ("profile this design ### Quick start -**1. Map the repo** (optional but speeds up everything that follows). Ask your host agent to write `map.md`, then validate: +**1. Map the repo** (the topology stage — pre-req for survey + profile). Ask your host agent to write `map.md`, then validate: ```bash -ghost-map inventory # raw signals as JSON (the agent reads this to author map.md) -ghost-map lint # validate ./map.md against ghost.map/v1 +ghost-expression inventory # raw signals as JSON (the agent reads this to author map.md) +ghost-expression lint map.md # validate ./map.md against ghost.map/v1 ``` **2. Profile your design system** — ask your host agent to write `expression.md`. It'll follow the `profile` recipe and validate at the end. You validate manually with: @@ -146,11 +164,11 @@ Verbs are scoped to the tool that owns the artifact. Pure inputs → pure output | Tool | Command | Description | | --- | --- | --- | -| `ghost-map` | `inventory` | Emit raw repo signals (manifests, language histogram, registry presence, top-level tree, git remote) as JSON. | -| `ghost-map` | `lint` | Validate `map.md` against `ghost.map/v1`. | -| `ghost-expression` | `lint` | Validate `expression.md` schema + body/frontmatter coherence. | +| `ghost-expression` | `inventory` | Emit raw repo signals (manifests, language histogram, registry presence, top-level tree, git remote) as JSON. Feeds the topology recipe. | +| `ghost-expression` | `lint` | Validate `expression.md`, `map.md`, or `bucket.json` — auto-detects by extension/content. | | `ghost-expression` | `describe` | Print section ranges + token estimates so agents can selectively load. | | `ghost-expression` | `diff` | Structural prose-level diff between two expressions (NOT vector distance — for that, use `ghost-drift compare`). | +| `ghost-expression` | `bucket ` | Operate on `ghost.bucket/v1` files. Ops: `merge` (concat with id-based dedup, idempotent), `fix-ids` (recompute ids from content). | | `ghost-expression` | `emit` | Derive an artifact from `expression.md`: `review-command`, `context-bundle`, or `skill`. | | `ghost-drift` | `compare` | Pairwise (N=2) or composite (N≥3) over expression embeddings. `--semantic` / `--temporal` add qualitative enrichment. | | `ghost-drift` | `ack` | Record stance toward the tracked expression in `.ghost-sync.json`. | @@ -167,8 +185,9 @@ The interpretive verbs from the pitch (*author, self-govern, detect, remediate*) | Recipe | Bundle | Capability | Triggered by | | --- | --- | --- | --- | -| `map` | `ghost-map` | Author the topology card | "map this repo", "write map.md" | -| `profile` | `ghost-expression` | Author the quality bar | "profile this design language", "write expression.md" | +| `map` | `ghost-expression` | Author the topology card (stage 1) | "map this repo", "write map.md" | +| `survey` | `ghost-expression` | Author the bucket of values (stage 2) | "survey design values", "extract design tokens" | +| `profile` | `ghost-expression` | Author the design language (stage 3) | "profile this design language", "write expression.md" | | `review` | `ghost-drift` | Self-govern at PR time | "review this PR for drift" | | `verify` | `ghost-drift` | Self-govern at generation time | "verify generated UI against the expression" | | `compare` | `ghost-drift` | Detect drift across the org | "why did these two expressions drift?" | @@ -192,20 +211,19 @@ Each CLI auto-loads `.env` and `.env.local` from the working directory. ### The expression -What the agent reads when it authors, reviews, or remediates. The canonical artifact is **`expression.md`** (owned by `ghost-expression`): a Markdown document with YAML frontmatter (machine layer) plus a three-layer prose body. Human-readable, LLM-consumable, diff-friendly: +What the agent reads when it authors, reviews, or remediates. The canonical artifact is **`expression.md`** (owned by `ghost-expression`): a Markdown document with YAML frontmatter (machine layer) plus a prose body. Human-readable, LLM-consumable, diff-friendly: -- **Frontmatter**: 49-dimensional embedding, palette, spacing, typography, surfaces, roles, provenance. The machine layer. +- **Frontmatter**: 49-dimensional embedding, palette, spacing, typography, surfaces, roles, provenance. The machine layer. Also `rules[]` — grep-able review rules with `presence_floor` for codifying absences. - **`# Character`**: the opening atmosphere read, evocative not technical. What an agent quotes to stay on-brand. -- **`# Signature`**: 3–7 distinctive traits that make _this_ system unlike its peers. The drift-sensitive moves. - **`# Decisions`**: abstract, implementation-agnostic choices with evidence. Each decision is embedded so `ghost-drift compare --semantic` can match semantically. Generate one with the `profile` recipe (in the `ghost-expression` skill bundle). See [`docs/expression-format.md`](./docs/expression-format.md) for the full spec, including the 49-dim machine-vector breakdown. ### The map -What every Ghost tool reads to learn the topology of a repo. The canonical artifact is **`map.md`** (owned by `ghost-map`): YAML frontmatter against the `ghost.map/v1` schema (languages, build system, package manifests, registry, design-system paths, UI surface globs, feature areas) plus a short prose body (Identity, Topology, Conventions). The repo's own `map.md` lives at the root. +What every Ghost tool reads to learn the topology of a repo. The canonical artifact is **`map.md`** (owned by `ghost-expression`, stage 1 of a scan): YAML frontmatter against the `ghost.map/v1` schema (languages, build system, package manifests, registry, design-system paths, UI surface globs, feature areas) plus a short prose body (Identity, Topology, Conventions). The repo's own `map.md` lives at the root. -Generate one with the `map` recipe (in the `ghost-map` skill bundle). The agent reads `ghost-map inventory` (raw repo signals as JSON) and synthesizes the prose layer. +Generate one with the `map` recipe (in the `ghost-expression` skill bundle). The agent reads `ghost-expression inventory` (raw repo signals as JSON) and synthesizes the prose layer. ### Author + self-govern loop diff --git a/apps/docs/src/app/tools/expression/page.tsx b/apps/docs/src/app/tools/expression/page.tsx index 478cd5d..7142971 100644 --- a/apps/docs/src/app/tools/expression/page.tsx +++ b/apps/docs/src/app/tools/expression/page.tsx @@ -48,7 +48,7 @@ export default function GhostExpressionLanding() {
- # Signature - - { - "\n\n- Achromatic by default — primary is the extremity of the gray scale\n- Pill-first radius philosophy — buttons / inputs / badges fully round\n to 999px; containers use moderate radii (10–24px)\n- Magazine-scale display typography — line-heights as low as 0.85\n- Shadow hierarchy named by role, not by numeric size\n\n" - } - # Decisions {"\n\n### color-strategy"} diff --git a/apps/docs/src/content/docs/cli-reference.mdx b/apps/docs/src/content/docs/cli-reference.mdx index 5829181..d4f2b81 100644 --- a/apps/docs/src/content/docs/cli-reference.mdx +++ b/apps/docs/src/content/docs/cli-reference.mdx @@ -1,6 +1,6 @@ --- title: CLI Reference -description: Sixteen verbs across four tools. Everything interpretive lives in the skill bundles your host agent runs. +description: Seventeen verbs across three tools. Everything interpretive lives in the skill bundles your host agent runs. kicker: Docs section: guide order: 20 @@ -10,22 +10,20 @@ slug: cli The CLIs are the calculator your host agent reaches for when it needs a -reproducible answer. The canonical artifacts are **`expression.md`** (owned -by `ghost-expression`) and **`map.md`** (owned by `ghost-map`) — Markdown -files with YAML frontmatter (machine layer) plus a prose body. Most commands -accept a path to the relevant artifact; they default to `./expression.md` or -`./map.md` in the current directory. No API key required. +reproducible answer. A scan produces three artifacts in sequence — **`map.md`** +(topology) → **`bucket.json`** (objective) → **`expression.md`** (subjective) — +all owned by `ghost-expression`. Most commands accept a path; they default to +`./expression.md` for kind-aware verbs. No API key required. Verbs are scoped to the tool that owns the artifact: -- **`ghost-map`** — topology: `inventory`, `lint` -- **`ghost-expression`** — design language: `lint`, `describe`, `diff`, `emit` +- **`ghost-expression`** — the scan pipeline: `inventory`, `scan-status`, `lint`, `describe`, `diff`, `bucket `, `emit` - **`ghost-drift`** — drift detection + governance: `compare`, `ack`, `track`, `diverge`, `emit skill` - **`ghost-fleet`** — elevation across many members: `members`, `view`, `emit skill` -Workflows like _profile_, _review_, _verify_, and _remediate_ are skill -recipes your host agent runs — not CLI verbs. Install them with each -tool's `emit skill` verb. +Workflows like _scan_, _map_, _survey_, _profile_, _review_, _verify_, and +_remediate_ are skill recipes your host agent runs — not CLI verbs. Install +them with each tool's `emit skill` verb. The tables below are generated from each CLI's source at build time. If a flag changes in any `cli.ts`, the next `pnpm dump:cli-help` run regenerates @@ -33,46 +31,87 @@ this reference — so these docs can't drift from the binaries. - + + +`ghost-expression` owns the three-stage scan pipeline: `map.md` (topology) → +`bucket.json` (objective) → `expression.md` (subjective). Verbs cover +inventory + lint + describe + diff + bucket ops + emit. Drift detection +lives separately in `ghost-drift`. + +### Topology — `inventory` -`map.md` is the navigation card every Ghost tool reads to learn the -topology of a frontend repo. `ghost-map` ships two verbs: `inventory` -(raw repo signals as JSON, the input the agent reads to author -`map.md`) and `lint` (validate against `ghost.map/v1`). +Emit deterministic raw signals about a frontend repo as JSON: package +manifests, language histogram, candidate config files, registry presence, +top-level tree, git remote. Feeds the topology recipe (the agent's input +when authoring `map.md`). - + ```bash # Inventory the current directory -ghost-map inventory +ghost-expression inventory # Inventory a different repo -ghost-map inventory ../other-repo +ghost-expression inventory ../other-repo ``` - +### Pipeline progress — `scan-status` + +Report which scan stages have produced artifacts in a directory: topology +(`map.md`), objective (`bucket.json`), subjective (`expression.md`). Tells +orchestrators which stage to run next. Pure file-presence check today; +hash-keyed freshness is a planned enhancement. + + ```bash -# Default — reads ./map.md -ghost-map lint +# Status of the current directory +ghost-expression scan-status -# Specific file, JSON output -ghost-map lint packages/ghost-ui/map.md --format json +# Status of a different scan dir +ghost-expression scan-status fleet/members/cash-ios + +# Machine-readable for orchestrators +ghost-expression scan-status . --format json ``` - +Output reports each stage as `present` or `missing` and surfaces the +recommended next stage (or "scan complete" when every artifact is present). - +### Bucket ops — `bucket ` + +Operate on `ghost.bucket/v1` files. Two ops today: -`expression.md` is the canonical design-language artifact. `ghost-expression` -owns authoring (lint + describe + diff + emit) — drift detection lives -separately in `ghost-drift`. +- **`merge`** — concat with id-based dedup, deterministic and idempotent. + The composition primitive: modular rollups (one repo, N feature modules) + and fleet cohort views (N members merged into one cohort bucket) both + reduce to bucket merge. +- **`fix-ids`** — recompute every row's `id` from its content. The survey + recipe instructs the agent to author rows with `"id": ""` and finalize + with this verb in one pass — agents don't compute SHA-256 hashes by hand. + + + +```bash +# Merge two buckets +ghost-expression bucket merge a.json b.json -o merged.json + +# Merge a directory of bucket files +ghost-expression bucket merge fleet/members/*/bucket.json -o cohort.json + +# Populate IDs after authoring rows with empty id fields +ghost-expression bucket fix-ids draft.json -o draft.json + +# Stream output to stdout if -o is omitted +ghost-expression bucket merge a.json b.json | jq '.values | length' +``` ### Validation — `lint` -Validate `expression.md` schema + body/frontmatter coherence. Use this -before declaring an expression valid — the `profile` recipe ends by -calling it. +Validate `expression.md`, `map.md`, or `bucket.json` against its schema — +auto-detects which by `.json` extension, `schema: ghost.map/v1` frontmatter, +or filename. Use this before declaring an artifact valid; every authoring +recipe ends by calling it. @@ -80,14 +119,20 @@ calling it. # Default — reads ./expression.md ghost-expression lint -# Specific file, JSON output +# Validate a map.md (auto-detected by frontmatter) +ghost-expression lint map.md + +# Validate a bucket.json +ghost-expression lint bucket.json + +# JSON output ghost-expression lint path/to/expression.md --format json ``` ### Inspection — `describe` Print a section map of `expression.md` — frontmatter range, body sections -(`# Character`, `# Signature`, `# Decisions`, `# Fragments`), and each +(`# Character`, `# Decisions`, `# Fragments`), and each `### dimension` block under Decisions, with line ranges and token estimates. The host agent uses this to load only the sections it needs instead of the whole file. @@ -119,11 +164,10 @@ expression.md — 309 lines, ~3,955 tokens FRONTMATTER 1–164 ~973 tok [palette, spacing, typography, surfaces, roles, observation, decisions] # Character 166–169 ~159 tok -# Signature 170–179 ~298 tok -# Decisions 180–305 ~2,503 tok - ### color-strategy 182–192 ~250 tok - ### shape-language 205–216 ~181 tok - ### typography-voice 217–229 ~258 tok +# Decisions 170–305 ~2,800 tok + ### color-strategy 172–182 ~250 tok + ### shape-language 195–206 ~181 tok + ### typography-voice 207–219 ~258 tok … # Fragments 306–309 ~21 tok ``` @@ -299,7 +343,9 @@ once, then ask your agent in plain English: | Recipe | Bundle | Trigger | | ---------- | ------------------ | ------------------------------------------------------ | -| `map` | `ghost-map` | "map this repo" / "write map.md" | +| `scan` | `ghost-expression` | "scan this project" / "go end-to-end" — meta-recipe orchestrating topology → survey → profile | +| `map` | `ghost-expression` | "map this repo" / "write map.md" (topology stage) | +| `survey` | `ghost-expression` | "survey design values" / "extract tokens" (objective) | | `profile` | `ghost-expression` | "profile this design language" / "write expression.md" | | `review` | `ghost-drift` | "review this PR for drift" | | `verify` | `ghost-drift` | "verify generated UI against the expression" | diff --git a/apps/docs/src/content/docs/getting-started.mdx b/apps/docs/src/content/docs/getting-started.mdx index 968bbb7..1e4cf7d 100644 --- a/apps/docs/src/content/docs/getting-started.mdx +++ b/apps/docs/src/content/docs/getting-started.mdx @@ -9,17 +9,24 @@ slug: getting-started -Ghost is split into **five small tools**, each with one responsibility, plus a -shared library underneath. Your host agent (Claude Code, Cursor, Goose, Codex, …) -does the interpretive work — profile, review, verify, remediate — using -[agentskills.io](https://agentskills.io)-compatible recipe bundles that each -tool ships. The CLIs are the calculator the agent reaches for when it needs -a reproducible answer. +Ghost is split into **four small tools**, each with one responsibility, plus a +shared library (`@ghost/core`) underneath. Your host agent (Claude Code, Cursor, +Goose, Codex, …) does the interpretive work — profile, review, verify, remediate — +using [agentskills.io](https://agentskills.io)-compatible recipe bundles that +each tool ships. The CLIs are the calculator the agent reaches for when it +needs a reproducible answer. + +A scan runs in three stages, all owned by `ghost-expression`: + +``` +topology → objective → subjective +map.md bucket.json expression.md +ghost.map/v1 ghost.bucket/v1 (the canonical artifact) +``` | Tool | Owns | Verbs | | --- | --- | --- | -| `ghost-map` | `map.md` (topology) | `inventory`, `lint` | -| `ghost-expression` | `expression.md` (design language) | `lint`, `describe`, `diff`, `emit` | +| `ghost-expression` | the three-stage scan pipeline | `inventory`, `scan-status`, `lint`, `describe`, `diff`, `bucket `, `emit` | | `ghost-drift` | drift detection + governance | `compare`, `ack`, `track`, `diverge`, `emit skill` | | `ghost-fleet` | `fleet.md` (elevation across members) | `members`, `view`, `emit skill` | | `ghost-ui` | reference design system (97 shadcn components) | — | @@ -44,8 +51,8 @@ Or install globally: pnpm add -g ghost-drift ghost-expression ``` -`ghost-map` and `ghost-fleet` are workspace-only for now — clone the repo -and `pnpm build` to use them. +`ghost-fleet` is workspace-only for now — clone the repo and `pnpm build` +to use it. No API key is required for any CLI verb. If your host agent uses Anthropic or OpenAI models, it'll handle auth itself. Each CLI auto-loads `.env` and @@ -68,7 +75,7 @@ ghost-expression emit skill # → .claude/skills/ghost-expression Each bundle ships its own `SKILL.md` plus recipes under `references/`: -- **`ghost-expression`** — `profile.md` (write `expression.md` from a project) + `schema.md` (condensed format reference). +- **`ghost-expression`** — `scan.md` (meta-recipe orchestrating topology → survey → profile end-to-end), `map.md` (write `map.md` from a repo, the topology stage), `survey.md` (write `bucket.json`, the objective stage), `profile.md` (interpret a bucket into `expression.md`, the subjective stage), `schema.md` (condensed format reference). - **`ghost-drift`** — `compare.md` (interpretation), `review.md` (PR review), `verify.md` (generation→review loop), `remediate.md` (suggest minimal fixes). - **`ghost-fleet`** — `target.md` (synthesize fleet narrative from `view` output). @@ -81,35 +88,50 @@ CLI whenever it needs a reproducible answer. - + -Profiling is a skill recipe shipped in `ghost-expression`'s bundle, not a -CLI verb. Open your host agent in the project you want to profile and ask -it something like: +Scanning is the host-agent loop, not a single CLI verb. Open your host +agent in the project you want to profile and ask it something like: ```text -Profile this design language into expression.md +Scan this design language end-to-end ``` -The `profile` recipe walks the agent through finding design sources -(tailwind config, theme CSS, token files, component primitives), resolving -variable chains end-to-end, and writing the expression. The final step is -always `ghost-expression lint` — which validates the result with the same -answer every time. +The `scan` recipe checkpoints between stages with `scan-status` and +dispatches into the per-stage recipes: + +1. **Topology (`map.md`)** — the `map` recipe reads + `ghost-expression inventory` (raw repo signals) and writes a `map.md` + describing where the design system lives. Validated by + `ghost-expression lint map.md`. +2. **Objective (`bucket.json`)** — the `survey` recipe walks the source + exhaustively (writing dialect-specific greps; the recipe forbids + sampling) and writes a `bucket.json` cataloguing every concrete value, + token, and component. Validated by `ghost-expression lint bucket.json`. +3. **Subjective (`expression.md`)** — the `profile` recipe interprets the + bucket as ground truth: assigns roles to values, names design decisions + in prose, and writes the `expression.md`. Cannot fabricate values not + in the bucket. Validated by `ghost-expression lint expression.md`. + +If you only want one stage, ask for it directly: "map this repo", "survey +the design values", "profile this expression from the bucket". The recipes +chain via `handoffs`, so the agent surfaces the next stage when ready. An **expression** is a two-layer Markdown file: YAML frontmatter is the machine layer (49-dim vector + palette, spacing, typography, surfaces, -roles), and the body is prose organized into Character, Signature, and +roles, rules), and the body is prose organized into Character and Decisions. Humans can read it. LLMs can consume it. Ghost's CLIs diff it. See [`packages/ghost-ui/expression.md`](https://github.com/block/ghost/blob/main/packages/ghost-ui/expression.md) in this repo for a full real-world example. ```bash -# Validate the result (zero-config — reads ./expression.md) -ghost-expression lint +# Check what stage to run next +ghost-expression scan-status -# Or validate a specific file -ghost-expression lint path/to/expression.md --format json +# Validate any artifact (auto-detects the kind) +ghost-expression lint # ./expression.md +ghost-expression lint map.md # ghost.map/v1 +ghost-expression lint bucket.json # ghost.bucket/v1 ``` @@ -250,6 +272,6 @@ export default defineConfig({ Next: [Drift Workflow](/tools/drift/workflow) for the five-move walkthrough — profile, compare, review, evolve, org — with richer examples for each. Or jump to the [CLI Reference](/docs/cli) for every verb across -all four tools. +all three tools. diff --git a/apps/docs/src/generated/cli-manifest.json b/apps/docs/src/generated/cli-manifest.json index 1052a3d..77c7bb7 100644 --- a/apps/docs/src/generated/cli-manifest.json +++ b/apps/docs/src/generated/cli-manifest.json @@ -1,5 +1,5 @@ { - "generatedAt": "2026-04-27T15:50:16.473Z", + "generatedAt": "2026-04-29T17:56:29.010Z", "tools": [ { "tool": "ghost-drift", @@ -200,8 +200,8 @@ { "tool": "ghost-expression", "name": "lint", - "rawName": "lint [expression]", - "description": "Validate expression.md schema and body/frontmatter coherence", + "rawName": "lint [file]", + "description": "Validate expression.md, map.md, or bucket.json — auto-detects the kind from path/content", "options": [ { "rawName": "--format ", @@ -213,6 +213,29 @@ } ] }, + { + "tool": "ghost-expression", + "name": "scan-status", + "rawName": "scan-status [dir]", + "description": "Report which scan stages have produced artifacts in a directory: topology (map.md), objective (bucket.json), subjective (expression.md). Tells orchestrators which stage to run next.", + "options": [ + { + "rawName": "--format ", + "name": "format", + "description": "Output format: cli or json", + "default": "cli", + "takesValue": true, + "negated": false + } + ] + }, + { + "tool": "ghost-expression", + "name": "inventory", + "rawName": "inventory [path]", + "description": "Emit deterministic raw signals about a frontend repo as JSON: package manifests, language histogram, candidate config files, registry presence, top-level tree, git remote. Feeds the topology recipe (map.md authoring).", + "options": [] + }, { "tool": "ghost-expression", "name": "describe", @@ -245,6 +268,22 @@ } ] }, + { + "tool": "ghost-expression", + "name": "bucket", + "rawName": "bucket [...buckets]", + "description": "Operate on ghost.bucket/v1 files. Ops: merge (concat with id-based dedup, deterministic and idempotent), fix-ids (recompute every row's id from content; use after authoring rows with empty id fields).", + "options": [ + { + "rawName": "-o, --out ", + "name": "out", + "description": "Write the result to this path (default: stdout)", + "default": null, + "takesValue": true, + "negated": false + } + ] + }, { "tool": "ghost-expression", "name": "emit", @@ -325,48 +364,6 @@ } ] }, - { - "tool": "ghost-map", - "commands": [ - { - "tool": "ghost-map", - "name": "inventory", - "rawName": "inventory [path]", - "description": "Emit deterministic raw signals about a frontend repo as JSON: package manifests, language histogram, candidate config files, registry presence, top-level tree, git remote.", - "options": [] - }, - { - "tool": "ghost-map", - "name": "lint", - "rawName": "lint [map]", - "description": "Validate map.md against ghost.map/v1", - "options": [ - { - "rawName": "--format ", - "name": "format", - "description": "Output format: cli or json", - "default": "cli", - "takesValue": true, - "negated": false - } - ] - } - ], - "globalOptions": [ - { - "rawName": "-h, --help", - "name": "help", - "description": "Display this message", - "default": null - }, - { - "rawName": "-v, --version", - "name": "version", - "description": "Display version number", - "default": null - } - ] - }, { "tool": "ghost-fleet", "commands": [ diff --git a/docs/expression-format.md b/docs/expression-format.md index 9f7842d..38be42a 100644 --- a/docs/expression-format.md +++ b/docs/expression-format.md @@ -4,8 +4,8 @@ A Ghost **expression** is a single Markdown file that captures what a design lan The file has two parts, and each owns **different data**: -1. **Frontmatter (YAML)** — the **machine layer**. Identity, tokens, dimension slugs (without rationale or evidence), personality/resembles tags, optional cached embedding. Validated by zod. Read by deterministic tools. -2. **Body (Markdown)** — the **prose layer**. Character paragraph, Signature bullets, Decision rationale, **Evidence bullets**. Read by humans and LLMs. +1. **Frontmatter (YAML)** — the **machine layer**. Identity, tokens, dimension slugs (without rationale or evidence), personality/resembles tags, `rules[]`, optional cached embedding. Validated by zod. Read by deterministic tools. +2. **Body (Markdown)** — the **prose layer**. Character paragraph, Decision rationale, **Evidence bullets**. Read by humans and LLMs. Each field lives in exactly one place. There is no precedence rule because there is nothing to conflict over. @@ -30,12 +30,10 @@ The frontmatter and the body own disjoint fields. The reader unions them into a | `id`, `source`, `timestamp`, `sources` | Frontmatter | top-level | | `observation.personality`, `observation.resembles` | Frontmatter | `observation:` | | `observation.summary` | **Body** | `# Character` | -| `observation.distinctiveTraits` | **Body** | `# Signature` bullets | | `decisions[].dimension`, `decisions[].embedding` | Frontmatter | `decisions:` entry | | `decisions[].decision` (prose rationale) | **Body** | `### dimension` block | | `decisions[].evidence` | **Body** | `**Evidence:**` bullet list under `### dimension` | | `palette`, `spacing`, `typography`, `surfaces` | Frontmatter | top-level | -| `roles[]` (slot → token bindings) | Frontmatter | `roles:` | | `embedding` (49-dim vector) | **Sibling file** | `embedding.md` (referenced from `# Fragments`) | | `metadata` (loose extension bag) | Frontmatter | top-level, open-ended | @@ -71,9 +69,9 @@ sources: # optional, lists the targets that were combin - https://claude.ai # --- expression: narrative tags --- -# NOTE: prose (summary, distinctiveTraits, decision rationale) and the +# NOTE: prose (summary, decision rationale) and the # `**Evidence:**` bullets per decision live in the body under -# # Character, # Signature, ### blocks. +# # Character and ### dimension blocks. observation: personality: [restrained, editorial] resembles: [notion, linear] @@ -115,24 +113,6 @@ surfaces: shadowComplexity: subtle # deliberate-none | subtle | layered borderUsage: moderate # minimal | moderate | heavy -# --- expression: role bindings (optional) --- -# Semantic slot → token bindings. Bridges abstract tokens to rendering: -# a role names a slot (h1, card, button, …) and binds specific tokens -# from the dimensions above. Each sub-block is optional; omit what you -# cannot infer from source. Agents populate these from component files. -roles: - - name: h1 - tokens: - typography: { family: Anthropic Serif, size: 52, weight: 500 } - spacing: { margin: 32 } - evidence: ["components/Heading.tsx:12"] - - name: card - tokens: - surfaces: { borderRadius: 16, shadow: subtle } - spacing: { padding: 24 } - palette: { background: '#f5f4ed' } - evidence: ["components/ui/card.tsx"] - # --- expression: vector layer --- # embedding is OPTIONAL at root. Readers load it from the sibling # `embedding.md` fragment (referenced in the body) or recompute from the @@ -143,9 +123,8 @@ roles: **Required:** `id`, `source`, `timestamp`, `palette`, `spacing`, `typography`, `surfaces`. **Optional:** `embedding` (omit to let readers load from `embedding.md` or recompute), `metadata` (loose key-value extension bag). **Optional narrative tags:** `observation.personality`, `observation.resembles`, `decisions[]`. Omit rather than lie — a missing tag is truer than a fabricated one. -**Optional role bindings:** `roles[]`. Each role requires `name` and `evidence[]` (citations for where the binding was observed); token sub-blocks (`typography`, `spacing`, `surfaces`, `palette`) are independently optional and strict — unknown keys reject. Note: `evidence` belongs *inside* role entries, not on `decisions[]`. **Optional meta:** `name`, `slug`, `generator`, `confidence`, `generated`, `sources`, `extends`. -**Forbidden in frontmatter:** `observation.summary`, `observation.distinctiveTraits`, `decisions[].decision`, `decisions[].evidence`, and any unknown root key (e.g. `schema:`). These either live in the body (prose / evidence) or are not part of the schema. +**Forbidden in frontmatter:** `observation.summary`, `decisions[].decision`, `decisions[].evidence`, and any unknown root key (e.g. `schema:`). These either live in the body (prose / evidence) or are not part of the schema. When `extends:` is present, required expression fields may be omitted — the overlay inherits them from the base expression. The merged result is re-validated against the strict schema. @@ -160,11 +139,6 @@ The body owns prose and evidence. Four section kinds, all optional, in this orde A literary salon reimagined as a product page — warm, unhurried. -# Signature - -- Warm ring-shadows instead of drop-shadows -- Editorial serif/sans split - # Decisions ### warm-only-neutrals @@ -209,52 +183,6 @@ Link rules: --- -## Roles — the slot → token bridge - -Tokens alone are ingredients: "sizes 14, 16, 20, 32, 64 exist." A role is a recipe: "`h1` uses size 64, weight 500." `roles[]` is the layer that names which tokens belong to which semantic slot, so the expression stops being an inventory and becomes something a renderer can act on. - -**Shape.** Each role has three parts: - -- `name` — the slot. Prefer HTML-like or archetype names: `h1`, `h2`, `body`, `caption`, `card`, `button`, `input`, `list-row`. -- `tokens` — the bindings, grouped by dimension. Each sub-block (`typography`, `spacing`, `surfaces`, `palette`) is independently optional and every field inside is optional. A role can be partial when the source only supplies some tokens. -- `evidence` — where the binding was observed. File paths or `path:line` references. - -**Authoring contract.** Only emit roles with direct source evidence. A plausible-but-unobserved role is worse than a missing one. A codebase with no component files may produce no roles at all — that is truthful. - -**Strictness.** The `typography`, `spacing`, and `surfaces` sub-blocks are zod `.strict()` — unknown keys reject, so the schema stays disciplined as it grows. The `palette` sub-block is an open record (Phase 5b widening): slot keys are free-form so consumers can name slots from the conventional vocabulary or extend it. - -### Palette slot vocabulary - -`roles[].tokens.palette` is a `Record`. The recipe should reach for the conventional keys first; add others when they're load-bearing in the codebase. - -**Conventional keys** (use these by default): `background`, `foreground`, `surface`, `border`, `accent`, `muted`, `link`. - -**Extensions** seen in the wild: `ring`, `popover`, `separator`, `input`, `chart-1`, … — fine to use when the codebase justifies them. - -### Token references - -Role palette slot values may be raw hex literals OR token references. The reference syntax is `{.}`: - -```yaml -roles: - - name: button - tokens: - palette: - background: '{palette.dominant.accent}' # resolves to #c96442 - foreground: '{palette.dominant.surface}' # resolves to #f5f4ed - border: '#e8e6dc' # raw hex is fine too - ring: '{base.color.brand.x.light}' # opaque external ref - evidence: ["components/ui/button.tsx:18"] -``` - -**Local namespaces:** `palette.dominant` and `palette.semantic` — the two palette blocks that already carry a `role`. Renames cascade (change the role value in one place, every role that references it updates too), and `ghost-expression lint` reports `broken-role-reference` for references that don't resolve. - -**External / pipeline refs.** Token-pipeline consumers (Style Dictionary, Theo, …) often bind a role to a deeply-nested upstream token like `{base.color.brand.x.light}`. The linter accepts these as opaque passthroughs when the head segment is a recognized external namespace (`base`, `core`, `semantic`, `component`, `tokens`, `ref`, `sys`) or the path has 4+ segments. We don't try to resolve them — that requires walking the upstream package, which is out of scope for the deterministic CLI. - -**What cannot be referenced locally.** `palette.neutrals.steps` is positional (no name). Typography, spacing, and surfaces are inventories, not named vocabularies — role tokens for those dimensions inline raw values. Local refs targeting the `palette.*` namespace beyond `dominant`/`semantic` fire `broken-role-reference`; external refs (heads outside `palette.*`) pass through. - ---- - ## Embedding fragment The 49-dimensional embedding lives in `embedding.md` next to the expression. The file carries only YAML — no prose: @@ -353,7 +281,7 @@ Fragments override inline decisions with the same dimension. Skip with `loadExpr ``` Invalid expression frontmatter: - • observation: Unrecognized keys: "summary", "distinctiveTraits" + • observation: Unrecognized keys: "summary" • decisions.0: Unrecognized keys: "decision", "evidence" • palette.saturationProfile: Invalid enum value... ``` diff --git a/docs/generation-loop.md b/docs/generation-loop.md index 3729deb..03a4444 100644 --- a/docs/generation-loop.md +++ b/docs/generation-loop.md @@ -51,7 +51,7 @@ reads `SKILL.md`. Driven by the host agent. Loads the expression (the agent typically pulls just the sections it needs via `ghost-expression describe`), builds a system -prompt from Character/Signature/Decisions + tokens, asks the underlying +prompt from Character/Decisions + tokens + rules, asks the underlying model, extracts the artifact (HTML/JSX/etc.), and hands it to the `review` recipe for self-check. Retries with drift feedback until it passes or the agent gives up. @@ -103,15 +103,15 @@ distinguish *targeted* drift (a pricing-page prompt leaking spacing) from *incidental* drift (the same prompt leaking color, which it wasn't supposed to stress). -## How the three-layer expression format earns its keep +## How the expression format earns its keep Each layer has a concrete job somewhere in the loop: | Layer | Role in the loop | |---|---| | **Character** | Prompt context — shapes feel | -| **Signature** | Drift-sensitive moves the reviewer weights heavily | -| **Decisions** | Lookup table the generator consults for specific choices | +| **Rules** | Drift-sensitive moves the reviewer enforces; presence-floor rules codify load-bearing absences | +| **Decisions** | Abstract pattern lookup the generator consults for specific choices | If a layer doesn't pull weight somewhere, that's a signal the format is over-specified. The `verify` recipe is the schema-discipline mechanism. diff --git a/docs/ideas/ghost-expression.md b/docs/ideas/ghost-expression.md index ce6a980..3cb95b2 100644 --- a/docs/ideas/ghost-expression.md +++ b/docs/ideas/ghost-expression.md @@ -52,8 +52,8 @@ Lives at `packages/ghost-expression/src/skill-bundle/references/profile.md`. Ref concrete value. 3. From map.md.feature_areas + sub_areas, sample 6–10 product UI files spanning distinct surfaces. Read each well enough to populate `roles[]`. -4. Synthesize expression.md — three sections (Character, Signature, Decisions), - frontmatter holds tokens + evidence paths. +4. Synthesize expression.md — Character + Decisions in the body, + frontmatter holds tokens + rules + evidence paths. 5. `ghost expression lint expression.md` — fix until clean. ``` diff --git a/dogfood/ghost-ui/attempt-1/NOTES.md b/dogfood/ghost-ui/attempt-1/NOTES.md new file mode 100644 index 0000000..596e27a --- /dev/null +++ b/dogfood/ghost-ui/attempt-1/NOTES.md @@ -0,0 +1,54 @@ +# Attempt 1 — ghost-ui scan, 2026-04-29 + +First end-to-end dogfood of the new three-stage scan pipeline (`map.md` → `bucket.json` → `expression.md`) against `packages/ghost-ui`. Authored by an agent (Claude Opus 4.7) following the bundled `map.md`, `survey.md`, `profile.md` skill recipes. All three artifacts lint clean. + +## What worked + +- Pipeline ran end-to-end: every stage produced a lint-clean artifact. +- `bucket fix-ids` worked as designed — agent authored rows with `"id": ""`, finalized in one pass. +- Schema flexed across every value kind (`color`, `spacing`, `radius`, `shadow`, `breakpoint`, `motion`, `typography`, `layout-primitive`). +- Token alias chains captured the 3-deep indirection cleanly (`--color-foreground` → `--foreground` → `--text-default` → `--color-gray-900`). + +## What failed + +### 1. Bucket recall ~10–20% + +Massive undercount across every section: + +| Section | Recorded | Reality | Recall | +|---|---|---|---| +| `components` | 6 | 97 UI primitives + 48 AI elements + 5 themes (~150 registry items) | ~4% | +| `values` (distinct hexes) | 8 | 25 distinct hexes in canonical token layer (139 across `src/`) | ~32% | +| `tokens` | 11 | 80+ named tokens in `main.css` alone | ~14% | +| `libraries` | 6 | 27 distinct `@radix-ui/*` packages alone (rolled into 1 row) | OK at the category level | + +The recipe instructed exhaustiveness; the agent sampled. **A 90% undercount is a failed scan** — the interpreter downstream cannot recover what wasn't recorded. + +### 2. Decision-level coverage 7/11 + +Missed four load-bearing decisions named in the prior expression.md (authored under the old single-pass recipe): + +- **`font-sourcing`** — ghost-ui ships zero bundled fonts (`font-faces.css` is one comment). Critical character claim, missed entirely. +- **`interactive-patterns`** — global `*:focus-visible` discipline applied uniformly; missed. +- **`density`** — "compact controls inside generous structural whitespace" composition; missed. +- **Charts as a sub-strategy** — 5-color warm chart palette deliberately departing from the monochromatic discipline; missed. + +### 3. Quantitative errors in named decisions + +- **`shadow-hierarchy: 4-tier`** is wrong. There are **7 named tiers** (`mini`, `btn`, `card`, `elevated`, `popover`, `modal`, `kbd`) plus 2 special-purpose (`mini-inset`, `date-field-focus`). +- **`color-strategy`** overstated "no brand or accent color." `--background-accent` exists (mapping to `gray-900`). The real claim is *monochrome accent* — a deliberate refusal of chromatic accent. + +### 4. Decision naming bias + +The new bucket-grounded recipe produced more literal/technical names (`color-strategy`, `shape-language`, `shadow-hierarchy`) where the existing expression named patterns at a more useful abstraction (`surface-hierarchy`, `elevation`, `theming-architecture`, `interactive-patterns`). The recipe should reinforce "name the pattern, not the value." + +### 5. Bug in self-distance check (separate from the recipe) + +`ghost-drift compare expression.md expression.md` reports 17.5% self-distance because `loadExpression` doesn't backfill `oklch` on palette colors. `comparePalette` then treats every color as fully unmatched (distance 1). Fix shipping in attempt 2's run; not a recipe issue. + +## Lessons for attempt 2 + +1. **Exhaustiveness is the load-bearing rule** — the agent must enumerate every section's source of truth, not sample. Cross-check counts from two independent passes; a divergence > ~10% means re-pass. Already strengthening this in `survey.md`. +2. **No leading repo-specific guidance** in the recipe (e.g. don't say "use registry.json") — Ghost is BYOA-agnostic. The recipe states the discipline; the agent identifies the canonical signal in this repo. +3. **Profile recipe should reinforce "name the pattern, not the value."** Decision dimensions like `font-sourcing`, `interactive-patterns`, `density` are more useful than restated tokens. +4. **Cross-check decision count against prior art when available.** If the old `expression.md` had 11 dimensions and the new has 7, ask why the four absent ones weren't observable — usually a recall gap upstream. diff --git a/dogfood/ghost-ui/attempt-1/bucket.json b/dogfood/ghost-ui/attempt-1/bucket.json new file mode 100644 index 0000000..7338584 --- /dev/null +++ b/dogfood/ghost-ui/attempt-1/bucket.json @@ -0,0 +1,764 @@ +{ + "schema": "ghost.bucket/v1", + "sources": [ + { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + } + ], + "values": [ + { + "id": "07998cc8e99e422d", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#ffffff", + "raw": "#ffffff", + "spec": { + "space": "srgb", + "hex": "#ffffff", + "rgb": { + "r": 255, + "g": 255, + "b": 255 + } + }, + "occurrences": 24, + "files_count": 4, + "role_hypothesis": "neutral-extreme-light" + }, + { + "id": "f699bc316c60d1df", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#000000", + "raw": "#000000", + "spec": { + "space": "srgb", + "hex": "#000000", + "rgb": { + "r": 0, + "g": 0, + "b": 0 + } + }, + "occurrences": 15, + "files_count": 3, + "role_hypothesis": "neutral-extreme-dark" + }, + { + "id": "b2de7ad8c2837a78", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#1a1a1a", + "raw": "#1a1a1a", + "spec": { + "space": "srgb", + "hex": "#1a1a1a" + }, + "occurrences": 9, + "files_count": 3, + "role_hypothesis": "neutral-darkest-step" + }, + { + "id": "4b8ba7755a7b0ad6", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#f5f5f5", + "raw": "#f5f5f5", + "spec": { + "space": "srgb", + "hex": "#f5f5f5" + }, + "occurrences": 3, + "files_count": 2, + "role_hypothesis": "neutral-lightest-step" + }, + { + "id": "b6756caa068847ff", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#91cb80", + "raw": "#91cb80", + "spec": { + "space": "srgb", + "hex": "#91cb80" + }, + "occurrences": 9, + "files_count": 2, + "role_hypothesis": "semantic-success" + }, + { + "id": "c43f851c1cb8e9bd", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#f94b4b", + "raw": "#f94b4b", + "spec": { + "space": "srgb", + "hex": "#f94b4b" + }, + "occurrences": 5, + "files_count": 2, + "role_hypothesis": "semantic-danger" + }, + { + "id": "22a9e7619e96133b", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#5c98f9", + "raw": "#5c98f9", + "spec": { + "space": "srgb", + "hex": "#5c98f9" + }, + "occurrences": 5, + "files_count": 2, + "role_hypothesis": "semantic-info" + }, + { + "id": "51eea2d9c0c627e3", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#fbcd44", + "raw": "#fbcd44", + "spec": { + "space": "srgb", + "hex": "#fbcd44" + }, + "occurrences": 5, + "files_count": 2, + "role_hypothesis": "semantic-warning" + }, + { + "id": "0e913b5f5566586a", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "8", + "raw": "8px", + "spec": { + "scalar": 8, + "unit": "px" + }, + "occurrences": 21, + "files_count": 2, + "role_hypothesis": "spacing-base-unit" + }, + { + "id": "fcd6f6793118b2d6", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "2", + "raw": "2px", + "spec": { + "scalar": 2, + "unit": "px" + }, + "occurrences": 25, + "files_count": 3 + }, + { + "id": "43e686cb95055b82", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "20", + "raw": "20px", + "spec": { + "scalar": 20, + "unit": "px" + }, + "occurrences": 14, + "files_count": 3, + "role_hypothesis": "spacing-card-radius-unit" + }, + { + "id": "42331c1ca6bbb635", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "100", + "raw": "100px", + "spec": { + "scalar": 100, + "unit": "px" + }, + "occurrences": 3, + "files_count": 1, + "role_hypothesis": "section-padding-vertical" + }, + { + "id": "d77862c3e75ef823", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "kind": "radius", + "value": "999", + "raw": "999px", + "spec": { + "scalar": 999, + "unit": "px" + }, + "occurrences": 9, + "files_count": 2, + "role_hypothesis": "radius-pill" + }, + { + "id": "bcf70cb399fa2f2e", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "kind": "radius", + "value": "20", + "raw": "20px", + "spec": { + "scalar": 20, + "unit": "px" + }, + "occurrences": 4, + "files_count": 1, + "role_hypothesis": "radius-card-default" + }, + { + "id": "13fe983e4d8cad2e", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "kind": "shadow", + "value": "0 2px 8px rgba(76, 76, 76, 0.15)", + "raw": "0 2px 8px rgba(76, 76, 76, 0.15)", + "spec": { + "offset_x": { + "scalar": 0, + "unit": "px" + }, + "offset_y": { + "scalar": 2, + "unit": "px" + }, + "blur": { + "scalar": 8, + "unit": "px" + }, + "color": "rgba(76, 76, 76, 0.15)" + }, + "occurrences": 4, + "files_count": 1, + "role_hypothesis": "shadow-mini-light" + }, + { + "id": "9a298083f034b5d0", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "kind": "breakpoint", + "value": "1440", + "raw": "1440px", + "spec": { + "scalar": 1440, + "unit": "px", + "label": "desktop" + }, + "occurrences": 2, + "files_count": 1, + "role_hypothesis": "breakpoint-desktop" + }, + { + "id": "3ac3a9616596e548", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "kind": "motion", + "value": "0.2s", + "raw": "0.2s", + "spec": { + "duration_ms": 200 + }, + "occurrences": 9, + "files_count": 1, + "role_hypothesis": "duration-normal" + }, + { + "id": "79cec1551cea2c94", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "kind": "motion", + "value": "0.15s", + "raw": "0.15s", + "spec": { + "duration_ms": 150 + }, + "occurrences": 3, + "files_count": 1, + "role_hypothesis": "duration-fast" + }, + { + "id": "9b765541bf0e2a42", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "kind": "motion", + "value": "cubic-bezier(0.33, 1, 0.68, 1)", + "raw": "cubic-bezier(0.33, 1, 0.68, 1)", + "spec": { + "easing": "cubic-bezier(0.33, 1, 0.68, 1)" + }, + "occurrences": 1, + "files_count": 1, + "role_hypothesis": "easing-spring" + }, + { + "id": "6cd2a74b3334e115", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "kind": "typography", + "value": "Geist Mono", + "raw": "\"Geist Mono\"", + "spec": { + "family": "Geist Mono" + }, + "occurrences": 1, + "files_count": 1, + "role_hypothesis": "font-mono" + }, + { + "id": "7e0c4961e87935f8", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "kind": "typography", + "value": "system-ui", + "raw": "system-ui", + "spec": { + "family": "system-ui" + }, + "occurrences": 2, + "files_count": 1, + "role_hypothesis": "font-sans" + }, + { + "id": "8f1fb0ee33db31a0", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "kind": "layout-primitive", + "value": "1440px", + "raw": "1440px", + "spec": { + "kind": "max-width", + "scalar": 1440, + "unit": "px" + }, + "occurrences": 1, + "files_count": 1, + "role_hypothesis": "page-container-max-width" + } + ], + "tokens": [ + { + "id": "a98934fc1e31ad67", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-gray-900", + "alias_chain": [], + "resolved_value": "#1a1a1a", + "occurrences": 9 + }, + { + "id": "12ea54555a0564cf", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "name": "--text-default", + "alias_chain": ["--color-gray-900"], + "resolved_value": "#1a1a1a", + "by_theme": { + "light": "#1a1a1a", + "dark": "#ffffff" + }, + "occurrences": 12 + }, + { + "id": "9e25b641c24e539a", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "name": "--foreground", + "alias_chain": ["--text-default", "--color-gray-900"], + "resolved_value": "#1a1a1a", + "by_theme": { + "light": "#1a1a1a", + "dark": "#ffffff" + }, + "occurrences": 8 + }, + { + "id": "441b3c286c0261ca", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-foreground", + "alias_chain": ["--foreground", "--text-default", "--color-gray-900"], + "resolved_value": "#1a1a1a", + "by_theme": { + "light": "#1a1a1a", + "dark": "#ffffff" + }, + "occurrences": 4 + }, + { + "id": "035086c7634b82d4", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "name": "--background-default", + "alias_chain": ["--color-white"], + "resolved_value": "#ffffff", + "by_theme": { + "light": "#ffffff", + "dark": "#000000" + }, + "occurrences": 11 + }, + { + "id": "ec631d558a6a99d1", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "name": "--radius", + "alias_chain": [], + "resolved_value": "20px", + "occurrences": 5 + }, + { + "id": "b23396b5f5917ece", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "name": "--radius-pill", + "alias_chain": [], + "resolved_value": "999px", + "occurrences": 4 + }, + { + "id": "6495da33b44d2a78", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "name": "--radius-button", + "alias_chain": [], + "resolved_value": "999px", + "occurrences": 3 + }, + { + "id": "d440ce2b5a94d053", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "name": "--shadow-card", + "alias_chain": [], + "resolved_value": "0 2px 8px rgba(76, 76, 76, 0.15)", + "by_theme": { + "light": "0 2px 8px rgba(76, 76, 76, 0.15)", + "dark": "0 2px 8px rgba(0, 0, 0, 0.4)" + }, + "occurrences": 3 + }, + { + "id": "0c2a5e27fb5ab060", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "name": "--ease-spring", + "alias_chain": [], + "resolved_value": "cubic-bezier(0.33, 1, 0.68, 1)", + "occurrences": 1 + }, + { + "id": "ea66cc1267da58ae", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "name": "--breakpoint-desktop", + "alias_chain": [], + "resolved_value": "1440px", + "occurrences": 1 + } + ], + "components": [ + { + "id": "08d82e05c20c6443", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "name": "Button", + "discovered_via": "registry.json", + "variants": [ + "default", + "destructive", + "outline", + "secondary", + "ghost", + "link" + ], + "sizes": ["default", "sm", "lg", "icon"] + }, + { + "id": "c0247ab4b8650edd", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "name": "Input", + "discovered_via": "registry.json" + }, + { + "id": "a5c90d03f29f5c06", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "name": "Card", + "discovered_via": "registry.json" + }, + { + "id": "3d87605ed68215ef", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "name": "Dialog", + "discovered_via": "registry.json" + }, + { + "id": "60b034a3edf75635", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "name": "DropdownMenu", + "discovered_via": "registry.json" + }, + { + "id": "69c55fea7b09ab3e", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "name": "Badge", + "discovered_via": "registry.json" + } + ], + "libraries": [ + { + "id": "6348a4fef3c0b8d4", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "name": "lucide-react", + "kind": "icons", + "version": "^1.7.0" + }, + { + "id": "4f8ef2d2b5cd65ee", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "name": "@radix-ui/react-*", + "kind": "primitives", + "version": "^1.x" + }, + { + "id": "b497cbc85c1baf41", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "name": "cmdk", + "kind": "command-palette", + "version": "^1.1.1" + }, + { + "id": "d7dd2e7250f6c2a1", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "name": "sonner", + "kind": "toast", + "version": "^2.0.7" + }, + { + "id": "07dcc1e35fef16db", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "name": "class-variance-authority", + "kind": "variant-helper", + "version": "^0.7.1" + }, + { + "id": "396e1af18e2813b3", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "5f821cb6f3ef79ca2515091ba1174e6de5cf8227", + "scanned_at": "2026-04-29T13:55:00Z", + "scanner_version": "0.1.0" + }, + "name": "tw-animate-css", + "kind": "animation", + "version": "(imported via main.css)" + } + ] +} diff --git a/dogfood/ghost-ui/attempt-1/expression.md b/dogfood/ghost-ui/attempt-1/expression.md new file mode 100644 index 0000000..4fc0005 --- /dev/null +++ b/dogfood/ghost-ui/attempt-1/expression.md @@ -0,0 +1,117 @@ +--- +id: ghost-ui +source: llm +timestamp: 2026-04-29T13:55:00Z +sources: + - github:block/ghost#packages/ghost-ui + +observation: + personality: [monochromatic, editorial, generous, restrained, deliberate] + resembles: [vercel-geist, linear] + +decisions: + - dimension: color-strategy + - dimension: shape-language + - dimension: typography-voice + - dimension: token-architecture + - dimension: theming + - dimension: motion + - dimension: shadow-hierarchy + +palette: + dominant: + - { role: ink, value: "#1a1a1a" } + neutrals: + steps: ["#ffffff", "#f5f5f5", "#f0f0f0", "#e8e8e8", "#e5e5e5", "#cccccc", "#999999", "#666666", "#333333", "#232323", "#1a1a1a", "#000000"] + count: 12 + semantic: + - { role: success, value: "#91cb80" } + - { role: danger, value: "#f94b4b" } + - { role: info, value: "#5c98f9" } + - { role: warning, value: "#fbcd44" } + saturationProfile: muted + contrast: high + +spacing: + scale: [2, 8, 20, 100] + baseUnit: 4 + regularity: 0.7 + +typography: + families: ["system-ui", "Geist Mono"] + sizeRamp: [10, 11, 12, 14, 16, 20, 28, 44, 64, 96] + weightDistribution: { "300": 1, "600": 4, "700": 2, "900": 1 } + lineHeightPattern: tight + +surfaces: + borderRadii: [10, 14, 16, 20, 24, 999] + shadowComplexity: layered + borderUsage: minimal +--- + +# Character + +`ghost-ui` is a quietly editorial design language: pure-monochromatic neutrals do almost all of the work, semantic colors light up only for state, and every interactive surface lands on a generous pill or rounded card. Headings move on a magazine scale via fluid `clamp()` sizing; bodies sit on system-ui at a measured 1.65 line-height. The whole system is restrained — no brand color, no decorative anything — and gets its character from shape rhythm and shadow cadence rather than chroma. + +# Decisions + +### color-strategy + +The system is monochromatic by default. A 12-step pure-gray scale (white → black) carries surface, border, and text across the entire UI; semantic colors (success, danger, info, warning) appear only when the UI needs to signal state. There is no brand or accent color — distinction is shape and shadow, not chroma. + +**Evidence:** +- Monochromatic ladder white → black: `#ffffff`, `#f5f5f5`, `#f0f0f0`, `#e8e8e8`, `#e5e5e5`, `#cccccc`, `#999999`, `#666666`, `#333333`, `#232323`, `#1a1a1a`, `#000000` (declared as `--color-gray-50` through `--color-gray-900` plus `--color-white`/`--color-black`) +- Semantic-only utility colors: `#91cb80` (success), `#f94b4b` (danger), `#5c98f9` (info), `#fbcd44` (warning) — bound to `--background-success`, `--background-danger`, `--background-info`, `--background-warning` +- `src/styles/main.css` declares no brand color or accent — `--color-*: initial` resets Tailwind's defaults + +### shape-language + +Pill-first interactive radii: every button and input is fully pilled (`999px`) by default; cards stay distinct as soft squares (`20px`); modals (`16px`) and dropdowns (`10px`) live in between. The shape choice is itself a rhythm — interactive vs. structural surfaces are read by their corner radius before any color cue. + +**Evidence:** +- `--radius-pill: 999px`, `--radius-button: 999px`, `--radius-input: 999px` +- `--radius-card: 20px`, `--radius-modal: 16px`, `--radius-dropdown: 10px` + +### typography-voice + +Headings live on a magazine-scale fluid hierarchy: `clamp()` sizing across display (64–96px), section (44–64px), sub (28–40px), and card (20–28px) tiers, with progressively tightening line-heights (0.88 → 1.1) and decreasing negative letter-spacing. Body copy uses `system-ui` at fluid `1rem–1.25rem`; mono is `Geist Mono`. There is no brand display face — the system inherits the OS sans and lets weight + scale do the work. + +**Evidence:** +- `--heading-display-font-size: clamp(64px, 8vw, 96px)` with line-height `0.88`, weight `900` +- `--heading-card-font-size: clamp(20px, 2vw, 28px)` with line-height `1.1`, weight `600` +- `--font-sans: system-ui, …`, `--font-mono: "Geist Mono"` + +### token-architecture + +Tokens layer three deep. The base layer declares raw stepped values (`--color-gray-900: #1a1a1a`). The semantic layer aliases them to roles (`--text-default: var(--color-gray-900)`). The shadcn alias layer wraps the semantic layer (`--foreground: var(--text-default)`, then `--color-foreground: var(--foreground)`). Consumers reach for any layer that matches their intent — Tailwind utilities pull `--color-foreground`; component CSS pulls `--text-default`; raw needs reach `--color-gray-900` directly. + +**Evidence:** +- chain `--color-foreground` → `--foreground` → `--text-default` → `--color-gray-900` +- chain `--background-default` → `--color-white` +- the `@theme inline` block in `src/styles/main.css` re-exposes every semantic alias as a Tailwind color token + +### theming + +Light and dark themes share token names but route through different cascade values: `.dark` overrides the semantic layer (`--background-default`, `--text-default`, etc.) so the alias-chained downstream tokens automatically pick up the new values. Shadows aren't inverted — they're intensified (alpha doubled in dark mode) so depth reads through the darker background. Alpha utility tokens (`--dark-10`, `--dark-40`, `--dark-04`) flip from gray-900-based to white-based across themes. + +**Evidence:** +- `.dark { --background-default: var(--color-black); --text-default: var(--color-white); … }` +- light `--shadow-card: 0 2px 8px rgba(76, 76, 76, 0.15)` → dark `--shadow-card: 0 2px 8px rgba(0, 0, 0, 0.4)` + +### motion + +Three durations (`0.15s` fast, `0.2s` normal, `0.4s` slow) drive every transition; a single shared cubic-bezier easing (`--ease-spring: cubic-bezier(0.33, 1, 0.68, 1)`) gives interactions a consistent decelerating feel. Animations are short, deliberate, and limited to opacity/transform/blur — no heavy motion. + +**Evidence:** +- `--duration-fast: 0.15s`, `--duration-normal: 0.2s`, `--duration-slow: 0.4s` +- `--ease-spring: cubic-bezier(0.33, 1, 0.68, 1)` +- keyframe library: `fade-in/out`, `scale-in/out`, `enter-from-left/right`, `word-reveal` + +### shadow-hierarchy + +Four tiers organize elevation: mini (cards, buttons, kbd), elevated (raised surfaces), popover (floating menus), modal (overlays). Each tier has fixed offset/blur values; the tier is the noun, not the parameters. A separate `mini-inset` exists for inset shadows, and `date-field-focus` is a special-purpose ring. In dark mode every tier's alpha doubles to maintain perceived depth. + +**Evidence:** +- `--shadow-mini`, `--shadow-btn`, `--shadow-card`, `--shadow-elevated`, `--shadow-popover`, `--shadow-modal`, `--shadow-kbd` +- `--shadow-mini-inset` for inset accents +- `--shadow-date-field-focus: 0 0 0 3px rgba(26, 26, 26, 0.15)` diff --git a/dogfood/ghost-ui/attempt-1/map.md b/dogfood/ghost-ui/attempt-1/map.md new file mode 100644 index 0000000..fd5f572 --- /dev/null +++ b/dogfood/ghost-ui/attempt-1/map.md @@ -0,0 +1,103 @@ +--- +schema: ghost.map/v1 +id: ghost-ui +repo: block/ghost +mapped_at: 2026-04-29 +platform: web +languages: + - { name: typescript, files: 116, share: 0.4696 } + - { name: json, files: 113, share: 0.4575 } + - { name: markdown, files: 10, share: 0.0405 } + - { name: css, files: 4, share: 0.0162 } + - { name: javascript, files: 4, share: 0.0162 } +build_system: [pnpm, vite] +package_manifests: + - package.json + - tsconfig.json + - tsconfig.lib.json + - tsconfig.mcp.json + - vite.lib.config.ts + - components.json +composition: + frameworks: + - { name: react, version: "19.1.0" } + - { name: vite, version: "^6.3.0" } + - { name: tailwindcss, version: "^4.2.2" } + - { name: radix-ui } + - { name: lucide-react, version: "^1.7.0" } + - { name: cmdk, version: "^1.1.1" } + - { name: sonner, version: "^2.0.7" } + - { name: class-variance-authority, version: "^0.7.1" } + rendering: react-spa + styling: + - tailwindcss-v4 + - css-custom-properties +registry: + path: ./registry.json + components: 106 +design_system: + paths: + - src/styles + - src/components/theme + - src/lib + entry_files: + - src/styles/main.css + - src/styles/font-faces.css + - src/lib/theme-presets.ts + - src/lib/theme-defaults.ts + - src/lib/theme-utils.ts + - src/lib/theme-provider.tsx + token_source: inline + status: active +ui_surface: + include: + - "src/components/ui/**" + - "src/components/ai-elements/**" + - "src/components/theme/**" + - "src/styles/**" + - "src/lib/theme-*" + exclude: + - "src/mcp/**" + - "scripts/**" + - "dist/**" + - "dist-lib/**" + - "dist-mcp/**" + - "public/r/**" + - "**/*.test.ts" + - "**/*.test.tsx" +feature_areas: + - name: ui-primitives + paths: ["src/components/ui"] + sub_areas: [input, layout, feedback, display, navigation, overlay] + - name: ai-elements + paths: ["src/components/ai-elements"] + sub_areas: [chat, agent-state, artifacts, audio] + - name: theme + paths: ["src/components/theme", "src/lib/theme-presets.ts", "src/lib/theme-defaults.ts", "src/lib/theme-utils.ts", "src/lib/theme-provider.tsx"] + - name: tokens + paths: ["src/styles"] + - name: hooks + paths: ["src/hooks"] + - name: registry-tooling + paths: ["scripts", "registry.json", "components.json"] + - name: mcp-server + paths: ["src/mcp"] +orientation_files: + - README.md + - registry.json + - src/styles/main.css + - src/lib/theme-presets.ts + - src/lib/theme-defaults.ts +--- + +## Identity + +`ghost-ui` is a private workspace package in the `block/ghost` monorepo. It ships a reference design system — 49 shadcn-style UI primitives plus 48 AI-element components plus a theme layer — distributed via a shadcn `registry.json` rather than npm. It also ships an MCP server (`ghost-mcp` bin) that re-exposes the registry to AI assistants. + +## Topology + +The design system lives across three folders. Tokens are inline CSS custom properties declared in `src/styles/main.css` (Tailwind v4 `@theme` blocks plus `:root` and `.dark` variable layers). Theme presets and defaults are TypeScript modules under `src/lib/theme-*.ts`, surfaced through a `theme-provider.tsx` React context. UI primitives live under `src/components/ui/`; AI-specific elements (chat surfaces, agent-state indicators, artifacts) live under `src/components/ai-elements/`. The `registry.json` at the package root indexes 106 distributable items consumed by `shadcn build`. + +## Conventions + +Tailwind v4 with custom theming via `@theme` blocks, CSS custom properties for runtime token resolution, and a class-variance-authority pattern for variant-heavy primitives. Radix UI underlies most interactive primitives. Components are flat (no nested theme variants) and ship as both source files and a baked `registry.json` plus per-item snapshots under `public/r/` (113 JSON files account for the JSON-heavy histogram). Build splits into a Vite library bundle and a separate TypeScript-built MCP server. diff --git a/dogfood/ghost-ui/attempt-2/NOTES.md b/dogfood/ghost-ui/attempt-2/NOTES.md new file mode 100644 index 0000000..87cbb12 --- /dev/null +++ b/dogfood/ghost-ui/attempt-2/NOTES.md @@ -0,0 +1,66 @@ +# Attempt 2 — ghost-ui scan, 2026-04-29 (post-tighten) + +Second dogfood after the survey-recipe tightening (commit `916e728`) and the oklch backfill bug fix. Same target as attempt 1 (`packages/ghost-ui`). + +## Recall delta vs attempt 1 + +| Section | Attempt 1 | Attempt 2 | Reality | +|---|---|---|---| +| `values[]` | 22 | 190 | ~190 (136 distinct hex + 41 distinct spacing + radii + breakpoints + motion + typography) ✓ | +| `tokens[]` | 11 | 238 | 240 unique CSS custom properties in `main.css` — **99% recall** ✓ | +| `components[]` | 6 | 97 | 97 `registry:ui` items in `registry.json` — **100% recall** ✓ | +| `libraries[]` | 6 | 42 | 42 design-surface deps in `package.json` (27 radix primitives + 15 others) — **100% recall** ✓ | + +The bucket file is 242 KB (vs 21 KB in attempt 1). Exhaustiveness is expensive in disk; honest is what matters. + +## Decision-level coverage + +| Attempt 1 (7 decisions) | Attempt 2 (11 decisions) | +|---|---| +| color-strategy | color-strategy | +| — | **chart-strategy** ← new | +| — | **surface-hierarchy** ← new | +| shape-language | shape-language | +| token-architecture | **theming-architecture** ← renamed (pattern-named) | +| typography-voice | typography-voice | +| — | **font-sourcing** ← new (load-bearing absence) | +| — | **density** ← new | +| — | **interactive-patterns** ← new (focus-ring discipline) | +| theming → folded into theming-architecture | — | +| shadow-hierarchy: 4-tier (WRONG) | **elevation: 7 named tiers** (CORRECT, renamed) | +| motion | motion | + +Coverage went from 7/11 (64%) → 11/11 (100%) of the load-bearing decisions identified in the audit of attempt 1. + +## What the recipe tightening produced + +1. **The agent wrote a script.** Following "use shell tools, identify the canonical signal" the agent generated `build-bucket.mjs` (pinned alongside the artifacts) that: + - Walks `main.css` line-by-line, scope-aware (`@theme`, `:root`, `.dark`), captures every `--name: value` declaration with by_theme cascade. + - Reads `registry.json` and emits one component row per `registry:ui` item. + - Categorizes every `package.json` dependency by design surface (icons, primitives, motion, charts, forms, dates, command, toast, drawer, etc.). + - Frequency-clusters values via `rg -oNI` with proper filename suppression. + +2. **No leading repo-specific guidance was needed.** The recipe just said "find the canonical signal." For ghost-ui that's `registry.json`, `package.json`, and `main.css`. For a different repo it would be different files. Recipe stays agnostic. + +3. **Pattern-naming worked.** `surface-hierarchy`, `theming-architecture`, `font-sourcing`, `density`, `interactive-patterns`, `elevation` all read as patterns rather than restated tokens. That's the prose discipline the existing pre-bucket expression had. + +## Bug fixes verified + +- Self-distance is now 0.0% (was 17.5% in attempt 1) — confirms the oklch backfill fix. +- Lint passes with 0 errors, 0 warnings, 0 info — no unused-palette flags, every palette entry cited in evidence. + +## What's still imperfect + +- **Spacing scale messiness**: attempt 2 records 22 distinct px values in the spacing scale. Real ghost-ui has a coherent rem-based component-height system (2rem, 2.75rem, 3rem, 3.25rem) layered on top of an ad-hoc px scatter (1, 2, 3, 4, 6, 8, 10, 12...). The bucket captured both honestly; the expression flattened them into a single `spacing.scale` array. A future iteration might split rem-component-height from px-utility values explicitly in the spec. +- **No dark-mode-specific rows for tokens that diverge between themes**: each token has a `by_theme` field when light/dark differ, but value-level dark-mode rows aren't separate rows. That's by design but worth noting. +- **The agent's heuristic categorizers** (e.g. "999px → radius, 1440px → breakpoint, others → spacing") are still heuristics. Misclassifications are possible; cross-checking against `map.md` topology would catch them. + +## Follow-up bug found + +`ghost-expression diff` reports `dominant primary: #1a1a1a` as a "+" addition when comparing attempt-2's expression to attempt-1's, even though both have the same dominant color (different role name: `ink` vs `primary`). Worth investigating — diff should match dominant entries by value when role names differ, OR surface "dominant role rename" as a distinct category. Filed as a follow-up; not blocking. + +## Lessons for next iteration + +1. **The script-driven extraction pattern is right.** The recipe should explicitly mention this — "for repos where canonical signals are programmatically enumerable (registry, manifest, named CSS declarations), generate a small extraction script and run it. Don't hand-author hundreds of rows." +2. **Spacing kind heuristics should fall through to `map.md` signals when ambiguous.** A 999px scalar is a radius in this repo because `--radius-pill: 999px`; a 1440px scalar is a breakpoint because `--breakpoint-desktop: 1440px`. The agent caught both; the recipe could codify the heuristic. +3. **The script is pinned alongside artifacts.** Anyone re-running the same target gets the same bucket. Reproducibility is a side-benefit of script-driven extraction. diff --git a/dogfood/ghost-ui/attempt-2/bucket.json b/dogfood/ghost-ui/attempt-2/bucket.json new file mode 100644 index 0000000..c791b82 --- /dev/null +++ b/dogfood/ghost-ui/attempt-2/bucket.json @@ -0,0 +1,8293 @@ +{ + "schema": "ghost.bucket/v1", + "sources": [ + { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + } + ], + "values": [ + { + "id": "19c6f0a7a7c5b4ff", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#ffffff", + "raw": "#ffffff", + "spec": { + "space": "srgb", + "hex": "#ffffff" + }, + "occurrences": 24, + "files_count": 3 + }, + { + "id": "eb2504434e5beb8f", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#000000", + "raw": "#000000", + "spec": { + "space": "srgb", + "hex": "#000000" + }, + "occurrences": 15, + "files_count": 3 + }, + { + "id": "c9cea4ef32196600", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#c5a24d", + "raw": "#C5A24D", + "spec": { + "space": "srgb", + "hex": "#c5a24d" + }, + "occurrences": 9, + "files_count": 1 + }, + { + "id": "780ea03543f7b21b", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#91cb80", + "raw": "#91cb80", + "spec": { + "space": "srgb", + "hex": "#91cb80" + }, + "occurrences": 9, + "files_count": 2 + }, + { + "id": "9e6949dfbfe6cd6f", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#1a1a1a", + "raw": "#1a1a1a", + "spec": { + "space": "srgb", + "hex": "#1a1a1a" + }, + "occurrences": 9, + "files_count": 3 + }, + { + "id": "92257d0742fbd5d1", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#1e1528", + "raw": "#1E1528", + "spec": { + "space": "srgb", + "hex": "#1e1528" + }, + "occurrences": 8, + "files_count": 1 + }, + { + "id": "ab0da8f6578c1132", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#333333", + "raw": "#333333", + "spec": { + "space": "srgb", + "hex": "#333333" + }, + "occurrences": 7, + "files_count": 3 + }, + { + "id": "227c3853f470d218", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#ff3a00", + "raw": "#FF3A00", + "spec": { + "space": "srgb", + "hex": "#ff3a00" + }, + "occurrences": 6, + "files_count": 1 + }, + { + "id": "0cf9c817fd862758", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#d4853a", + "raw": "#D4853A", + "spec": { + "space": "srgb", + "hex": "#d4853a" + }, + "occurrences": 6, + "files_count": 1 + }, + { + "id": "88ad01f113746dcb", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#ff5722", + "raw": "#FF5722", + "spec": { + "space": "srgb", + "hex": "#ff5722" + }, + "occurrences": 5, + "files_count": 1 + }, + { + "id": "eb6d87708ade9f6b", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#fbf5ec", + "raw": "#FBF5EC", + "spec": { + "space": "srgb", + "hex": "#fbf5ec" + }, + "occurrences": 5, + "files_count": 1 + }, + { + "id": "f6b6bdc6b200cf85", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#fbcd44", + "raw": "#fbcd44", + "spec": { + "space": "srgb", + "hex": "#fbcd44" + }, + "occurrences": 5, + "files_count": 2 + }, + { + "id": "8edffee467d4bf04", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#fafafa", + "raw": "#FAFAFA", + "spec": { + "space": "srgb", + "hex": "#fafafa" + }, + "occurrences": 5, + "files_count": 1 + }, + { + "id": "b035bc04c23683e0", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#f94b4b", + "raw": "#f94b4b", + "spec": { + "space": "srgb", + "hex": "#f94b4b" + }, + "occurrences": 5, + "files_count": 2 + }, + { + "id": "463fafef98c9af2e", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#f8f6f9", + "raw": "#F8F6F9", + "spec": { + "space": "srgb", + "hex": "#f8f6f9" + }, + "occurrences": 5, + "files_count": 1 + }, + { + "id": "cae6d2e4e2bc5f85", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#f4f9fb", + "raw": "#F4F9FB", + "spec": { + "space": "srgb", + "hex": "#f4f9fb" + }, + "occurrences": 5, + "files_count": 1 + }, + { + "id": "847db1c655cb330f", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#5c98f9", + "raw": "#5c98f9", + "spec": { + "space": "srgb", + "hex": "#5c98f9" + }, + "occurrences": 5, + "files_count": 2 + }, + { + "id": "3b3a5209515d379e", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#3d2b1f", + "raw": "#3D2B1F", + "spec": { + "space": "srgb", + "hex": "#3d2b1f" + }, + "occurrences": 5, + "files_count": 1 + }, + { + "id": "ab13b60f418d759d", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#3ab4d8", + "raw": "#3AB4D8", + "spec": { + "space": "srgb", + "hex": "#3ab4d8" + }, + "occurrences": 5, + "files_count": 1 + }, + { + "id": "f17312b005f7c6b0", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#3a3552", + "raw": "#3A3552", + "spec": { + "space": "srgb", + "hex": "#3a3552" + }, + "occurrences": 5, + "files_count": 1 + }, + { + "id": "0cea77740231c4c1", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#0f3442", + "raw": "#0F3442", + "spec": { + "space": "srgb", + "hex": "#0f3442" + }, + "occurrences": 5, + "files_count": 1 + }, + { + "id": "e7508e1cb473eda9", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#f6b44a", + "raw": "#f6b44a", + "spec": { + "space": "srgb", + "hex": "#f6b44a" + }, + "occurrences": 4, + "files_count": 2 + }, + { + "id": "54ce6bdaa29e6810", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#d76a6a", + "raw": "#d76a6a", + "spec": { + "space": "srgb", + "hex": "#d76a6a" + }, + "occurrences": 4, + "files_count": 2 + }, + { + "id": "dda8f0a3259b940b", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#d185e0", + "raw": "#d185e0", + "spec": { + "space": "srgb", + "hex": "#d185e0" + }, + "occurrences": 4, + "files_count": 2 + }, + { + "id": "4f4b25b2ecab8943", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#a898e0", + "raw": "#A898E0", + "spec": { + "space": "srgb", + "hex": "#a898e0" + }, + "occurrences": 4, + "files_count": 1 + }, + { + "id": "3f674ce0154adc30", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#9c5930", + "raw": "#9C5930", + "spec": { + "space": "srgb", + "hex": "#9c5930" + }, + "occurrences": 4, + "files_count": 1 + }, + { + "id": "bfae2bd55fff6a01", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#999999", + "raw": "#999999", + "spec": { + "space": "srgb", + "hex": "#999999" + }, + "occurrences": 4, + "files_count": 2 + }, + { + "id": "232959ee28f03557", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#8b7ec8", + "raw": "#8B7EC8", + "spec": { + "space": "srgb", + "hex": "#8b7ec8" + }, + "occurrences": 4, + "files_count": 1 + }, + { + "id": "86a115b50556e4a6", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#7585ff", + "raw": "#7585ff", + "spec": { + "space": "srgb", + "hex": "#7585ff" + }, + "occurrences": 4, + "files_count": 2 + }, + { + "id": "87ca69d152c82d28", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#1b6b8a", + "raw": "#1B6B8A", + "spec": { + "space": "srgb", + "hex": "#1b6b8a" + }, + "occurrences": 4, + "files_count": 1 + }, + { + "id": "a8e61d00c667bcd5", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#ffd966", + "raw": "#ffd966", + "spec": { + "space": "srgb", + "hex": "#ffd966" + }, + "occurrences": 3, + "files_count": 2 + }, + { + "id": "35d85861e84c6542", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#ff6b6b", + "raw": "#ff6b6b", + "spec": { + "space": "srgb", + "hex": "#ff6b6b" + }, + "occurrences": 3, + "files_count": 2 + }, + { + "id": "3902eab2dcad405d", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#f5f5f5", + "raw": "#f5f5f5", + "spec": { + "space": "srgb", + "hex": "#f5f5f5" + }, + "occurrences": 3, + "files_count": 2 + }, + { + "id": "f2d542a0dcb4c7d5", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#f0f0f0", + "raw": "#F0F0F0", + "spec": { + "space": "srgb", + "hex": "#f0f0f0" + }, + "occurrences": 3, + "files_count": 1 + }, + { + "id": "0568807befafcbbe", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#f0f0f0", + "raw": "#f0f0f0", + "spec": { + "space": "srgb", + "hex": "#f0f0f0" + }, + "occurrences": 3, + "files_count": 3 + }, + { + "id": "059b769b5259aee3", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#f0e4d4", + "raw": "#F0E4D4", + "spec": { + "space": "srgb", + "hex": "#f0e4d4" + }, + "occurrences": 3, + "files_count": 1 + }, + { + "id": "8b19c166ff830ed4", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#ede8f2", + "raw": "#EDE8F2", + "spec": { + "space": "srgb", + "hex": "#ede8f2" + }, + "occurrences": 3, + "files_count": 1 + }, + { + "id": "05e04926a9ada328", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#e8e8e8", + "raw": "#e8e8e8", + "spec": { + "space": "srgb", + "hex": "#e8e8e8" + }, + "occurrences": 3, + "files_count": 2 + }, + { + "id": "046151ed728caee4", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#e4f0f5", + "raw": "#E4F0F5", + "spec": { + "space": "srgb", + "hex": "#e4f0f5" + }, + "occurrences": 3, + "files_count": 1 + }, + { + "id": "b6ffdaf562b5a3e8", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#cccccc", + "raw": "#cccccc", + "spec": { + "space": "srgb", + "hex": "#cccccc" + }, + "occurrences": 3, + "files_count": 2 + }, + { + "id": "7265b8a354ce811d", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#a3d795", + "raw": "#a3d795", + "spec": { + "space": "srgb", + "hex": "#a3d795" + }, + "occurrences": 3, + "files_count": 2 + }, + { + "id": "e6515f20c05a1858", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#a0d8a0", + "raw": "#A0D8A0", + "spec": { + "space": "srgb", + "hex": "#a0d8a0" + }, + "occurrences": 3, + "files_count": 1 + }, + { + "id": "fa75055b08e6244f", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#a0c0e8", + "raw": "#A0C0E8", + "spec": { + "space": "srgb", + "hex": "#a0c0e8" + }, + "occurrences": 3, + "files_count": 1 + }, + { + "id": "855193a8720b2540", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#8b7355", + "raw": "#8B7355", + "spec": { + "space": "srgb", + "hex": "#8b7355" + }, + "occurrences": 3, + "files_count": 1 + }, + { + "id": "67ce7e085c2ddf59", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#8a82a0", + "raw": "#8A82A0", + "spec": { + "space": "srgb", + "hex": "#8a82a0" + }, + "occurrences": 3, + "files_count": 1 + }, + { + "id": "5911955f073eba51", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#7cacff", + "raw": "#7cacff", + "spec": { + "space": "srgb", + "hex": "#7cacff" + }, + "occurrences": 3, + "files_count": 2 + }, + { + "id": "6675f6e6e3870810", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#7a6b8a", + "raw": "#7A6B8A", + "spec": { + "space": "srgb", + "hex": "#7a6b8a" + }, + "occurrences": 3, + "files_count": 1 + }, + { + "id": "6ae193e06bc97c41", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#666666", + "raw": "#666666", + "spec": { + "space": "srgb", + "hex": "#666666" + }, + "occurrences": 3, + "files_count": 2 + }, + { + "id": "6df0ca145bec8a86", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#5a8a9c", + "raw": "#5A8A9C", + "spec": { + "space": "srgb", + "hex": "#5a8a9c" + }, + "occurrences": 3, + "files_count": 1 + }, + { + "id": "65330b22927d4453", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#3a2c20", + "raw": "#3A2C20", + "spec": { + "space": "srgb", + "hex": "#3a2c20" + }, + "occurrences": 3, + "files_count": 1 + }, + { + "id": "01d7a7ef9c8edd40", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#2a2640", + "raw": "#2A2640", + "spec": { + "space": "srgb", + "hex": "#2a2640" + }, + "occurrences": 3, + "files_count": 1 + }, + { + "id": "9addca8bce9fe4a9", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#2a1e3a", + "raw": "#2A1E3A", + "spec": { + "space": "srgb", + "hex": "#2a1e3a" + }, + "occurrences": 3, + "files_count": 1 + }, + { + "id": "5db75079f6c7895f", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#232323", + "raw": "#232323", + "spec": { + "space": "srgb", + "hex": "#232323" + }, + "occurrences": 3, + "files_count": 2 + }, + { + "id": "f50f989ca8e8027e", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#1a3a4a", + "raw": "#1A3A4A", + "spec": { + "space": "srgb", + "hex": "#1a3a4a" + }, + "occurrences": 3, + "files_count": 1 + }, + { + "id": "6a207a25619dbb7f", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#ffe600", + "raw": "#FFE600", + "spec": { + "space": "srgb", + "hex": "#ffe600" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "0d8323e8d749a2f9", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#ff00aa", + "raw": "#FF00AA", + "spec": { + "space": "srgb", + "hex": "#ff00aa" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "f1d46ef21d821bf9", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#f0edf8", + "raw": "#F0EDF8", + "spec": { + "space": "srgb", + "hex": "#f0edf8" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "925c19cf5e8b79c1", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#e8d0a8", + "raw": "#E8D0A8", + "spec": { + "space": "srgb", + "hex": "#e8d0a8" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "51108f937d5e272f", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#e8d0a0", + "raw": "#E8D0A0", + "spec": { + "space": "srgb", + "hex": "#e8d0a0" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "35d0e142eb6310ca", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#e8b0b0", + "raw": "#E8B0B0", + "spec": { + "space": "srgb", + "hex": "#e8b0b0" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "5fa1d2b9d6e07efe", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#e8a0a0", + "raw": "#E8A0A0", + "spec": { + "space": "srgb", + "hex": "#e8a0a0" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "7414ffac8619cfbd", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#e5e5e5", + "raw": "#e5e5e5", + "spec": { + "space": "srgb", + "hex": "#e5e5e5" + }, + "occurrences": 2, + "files_count": 2 + }, + { + "id": "49543ffe08c5fb7a", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#e2d4c0", + "raw": "#E2D4C0", + "spec": { + "space": "srgb", + "hex": "#e2d4c0" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "6a0a17fee12b600c", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#e0daf0", + "raw": "#E0DAF0", + "spec": { + "space": "srgb", + "hex": "#e0daf0" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "60025e29a38deb1b", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#e07a5f", + "raw": "#E07A5F", + "spec": { + "space": "srgb", + "hex": "#e07a5f" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "ad3d06d55b2e9c2f", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#ddd4e6", + "raw": "#DDD4E6", + "spec": { + "space": "srgb", + "hex": "#ddd4e6" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "ba7b3d12beb7e4a8", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#d8c080", + "raw": "#D8C080", + "spec": { + "space": "srgb", + "hex": "#d8c080" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "819ce1cb7d5d81b0", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#d08080", + "raw": "#D08080", + "spec": { + "space": "srgb", + "hex": "#d08080" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "53267606f845454a", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#c8dee8", + "raw": "#C8DEE8", + "spec": { + "space": "srgb", + "hex": "#c8dee8" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "ea32584ff2696f63", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#c8bee0", + "raw": "#C8BEE0", + "spec": { + "space": "srgb", + "hex": "#c8bee0" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "3ba3ac5218678f50", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#c8a8e0", + "raw": "#C8A8E0", + "spec": { + "space": "srgb", + "hex": "#c8a8e0" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "a5a54bc4897064df", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#c4a882", + "raw": "#C4A882", + "spec": { + "space": "srgb", + "hex": "#c4a882" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "ce97cde5b27f91bf", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#c46b5a", + "raw": "#C46B5A", + "spec": { + "space": "srgb", + "hex": "#c46b5a" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "2b3753ae2f1596b8", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#b8a8c8", + "raw": "#B8A8C8", + "spec": { + "space": "srgb", + "hex": "#b8a8c8" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "e87ec23d893119d8", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#b0d8b0", + "raw": "#B0D8B0", + "spec": { + "space": "srgb", + "hex": "#b0d8b0" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "556b291123a7ad41", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#aaaaaa", + "raw": "#AAAAAA", + "spec": { + "space": "srgb", + "hex": "#aaaaaa" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "80b81f8563e27180", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#a8c8e8", + "raw": "#A8C8E8", + "spec": { + "space": "srgb", + "hex": "#a8c8e8" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "241ecacc33e63903", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#a8c256", + "raw": "#A8C256", + "spec": { + "space": "srgb", + "hex": "#a8c256" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "6e9a5fe1b9295590", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#a8856b", + "raw": "#A8856B", + "spec": { + "space": "srgb", + "hex": "#a8856b" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "c4e5bfb7641524bd", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#8bb8ca", + "raw": "#8BB8CA", + "spec": { + "space": "srgb", + "hex": "#8bb8ca" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "19b6f0b70d7c9b7c", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#8baa72", + "raw": "#8BAA72", + "spec": { + "space": "srgb", + "hex": "#8baa72" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "2188e08f1e3d01d0", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#80c080", + "raw": "#80C080", + "spec": { + "space": "srgb", + "hex": "#80c080" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "7e7b935653f266dd", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#80a8d8", + "raw": "#80A8D8", + "spec": { + "space": "srgb", + "hex": "#80a8d8" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "b2ccf30094439216", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#7bc4b8", + "raw": "#7BC4B8", + "spec": { + "space": "srgb", + "hex": "#7bc4b8" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "63111f684b2a6d2f", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#7b9ea8", + "raw": "#7B9EA8", + "spec": { + "space": "srgb", + "hex": "#7b9ea8" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "e6c812fb93eb0d8c", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#2a1f18", + "raw": "#2A1F18", + "spec": { + "space": "srgb", + "hex": "#2a1f18" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "2c97663a7f4c9018", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#1e1a2a", + "raw": "#1E1A2A", + "spec": { + "space": "srgb", + "hex": "#1e1a2a" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "a4239710e3533bbd", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#1c1410", + "raw": "#1C1410", + "spec": { + "space": "srgb", + "hex": "#1c1410" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "8894e10eae2c9e91", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#1a1224", + "raw": "#1A1224", + "spec": { + "space": "srgb", + "hex": "#1a1224" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "9b548e5a304e60ae", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#141414", + "raw": "#141414", + "spec": { + "space": "srgb", + "hex": "#141414" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "89979c3fd52e671b", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#14121c", + "raw": "#14121C", + "spec": { + "space": "srgb", + "hex": "#14121c" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "999ef1660677cce0", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#112a36", + "raw": "#112A36", + "spec": { + "space": "srgb", + "hex": "#112a36" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "38c643c29595d778", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#0e0a14", + "raw": "#0E0A14", + "spec": { + "space": "srgb", + "hex": "#0e0a14" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "197147f82859ac50", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#0a1e28", + "raw": "#0A1E28", + "spec": { + "space": "srgb", + "hex": "#0a1e28" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "2e2cb806cc4984e1", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#00ff66", + "raw": "#00FF66", + "spec": { + "space": "srgb", + "hex": "#00ff66" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "54ac874bd0d0d787", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#00e5ff", + "raw": "#00E5FF", + "spec": { + "space": "srgb", + "hex": "#00e5ff" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "551f4550a36511ca", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#f5f2fa", + "raw": "#F5F2FA", + "spec": { + "space": "srgb", + "hex": "#f5f2fa" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "c4f97253730d3825", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#f5ecdf", + "raw": "#F5ECDF", + "spec": { + "space": "srgb", + "hex": "#f5ecdf" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "b3ffa060b9c93e9f", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#f0ecf4", + "raw": "#F0ECF4", + "spec": { + "space": "srgb", + "hex": "#f0ecf4" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "84e1d552d06565a2", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#eaf3f8", + "raw": "#EAF3F8", + "spec": { + "space": "srgb", + "hex": "#eaf3f8" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "9c7bba2b22963597", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#e8e4f4", + "raw": "#E8E4F4", + "spec": { + "space": "srgb", + "hex": "#e8e4f4" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "aded0fd258969b40", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#e0c880", + "raw": "#E0C880", + "spec": { + "space": "srgb", + "hex": "#e0c880" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "572bb5470b2fe221", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#e0a0a0", + "raw": "#E0A0A0", + "spec": { + "space": "srgb", + "hex": "#e0a0a0" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "1a035341d6f5bbe8", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#e08090", + "raw": "#E08090", + "spec": { + "space": "srgb", + "hex": "#e08090" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "6a9824cde5818e6b", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#d76a7a", + "raw": "#D76A7A", + "spec": { + "space": "srgb", + "hex": "#d76a7a" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "e785c7e322562e82", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#d4c2aa", + "raw": "#D4C2AA", + "spec": { + "space": "srgb", + "hex": "#d4c2aa" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "16533ebdfa4ea177", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#d2cae4", + "raw": "#D2CAE4", + "spec": { + "space": "srgb", + "hex": "#d2cae4" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "321697b0028fa79c", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#cccccc", + "raw": "#CCCCCC", + "spec": { + "space": "srgb", + "hex": "#cccccc" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "02731578fe6457a7", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#ccc0d8", + "raw": "#CCC0D8", + "spec": { + "space": "srgb", + "hex": "#ccc0d8" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "05bbd9850580c178", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#c0a060", + "raw": "#C0A060", + "spec": { + "space": "srgb", + "hex": "#c0a060" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "6cdcf36f88536052", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#c07070", + "raw": "#C07070", + "spec": { + "space": "srgb", + "hex": "#c07070" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "f4d6726b8205140b", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#b0d0de", + "raw": "#B0D0DE", + "spec": { + "space": "srgb", + "hex": "#b0d0de" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "d5f58d0c2067ee47", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#a88de0", + "raw": "#A88DE0", + "spec": { + "space": "srgb", + "hex": "#a88de0" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "107e45ed8d8224b1", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#8bd09a", + "raw": "#8BD09A", + "spec": { + "space": "srgb", + "hex": "#8bd09a" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "be6002f69635c0f5", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#8b6cc1", + "raw": "#8B6CC1", + "spec": { + "space": "srgb", + "hex": "#8b6cc1" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "2181ccf72319678c", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#7cb88a", + "raw": "#7CB88A", + "spec": { + "space": "srgb", + "hex": "#7cb88a" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "eedfd511fe5df7b4", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#7b92e8", + "raw": "#7B92E8", + "spec": { + "space": "srgb", + "hex": "#7b92e8" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "553969977351d418", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#7aaee5", + "raw": "#7AAEE5", + "spec": { + "space": "srgb", + "hex": "#7aaee5" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "3755ec248b128cf6", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#70a870", + "raw": "#70A870", + "spec": { + "space": "srgb", + "hex": "#70a870" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "67775c09416227f6", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#7090c0", + "raw": "#7090C0", + "spec": { + "space": "srgb", + "hex": "#7090c0" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "fca56628cd29e9ad", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#6b5940", + "raw": "#6B5940", + "spec": { + "space": "srgb", + "hex": "#6b5940" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "63b83ac5febec423", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#6a6280", + "raw": "#6A6280", + "spec": { + "space": "srgb", + "hex": "#6a6280" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "585282f198381c47", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#5e8ec5", + "raw": "#5E8EC5", + "spec": { + "space": "srgb", + "hex": "#5e8ec5" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "15338af0b39cd617", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#5b78d8", + "raw": "#5B78D8", + "spec": { + "space": "srgb", + "hex": "#5b78d8" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "6f593af2b46be5c7", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#5a4b6a", + "raw": "#5A4B6A", + "spec": { + "space": "srgb", + "hex": "#5a4b6a" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "6911e439b8d87426", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#5a4530", + "raw": "#5A4530", + "spec": { + "space": "srgb", + "hex": "#5a4530" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "2e838cdd9695eb8f", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#555555", + "raw": "#555555", + "spec": { + "space": "srgb", + "hex": "#555555" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "16283661e7d78622", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#4a4468", + "raw": "#4A4468", + "spec": { + "space": "srgb", + "hex": "#4a4468" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "80e91911eeee4bc6", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#4a3a60", + "raw": "#4A3A60", + "spec": { + "space": "srgb", + "hex": "#4a3a60" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "913fe0e9ce6e566a", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#4a382a", + "raw": "#4A382A", + "spec": { + "space": "srgb", + "hex": "#4a382a" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "7abc5834ffe55841", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#444444", + "raw": "#444444", + "spec": { + "space": "srgb", + "hex": "#444444" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "3b279b5f71f9a19f", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#3a6a7c", + "raw": "#3A6A7C", + "spec": { + "space": "srgb", + "hex": "#3a6a7c" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "73d67c6dd5257942", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#3a6070", + "raw": "#3A6070", + "spec": { + "space": "srgb", + "hex": "#3a6070" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "104b94e69ea070cd", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#3a3452", + "raw": "#3A3452", + "spec": { + "space": "srgb", + "hex": "#3a3452" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "9375bb4401b8da8b", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#3a2a50", + "raw": "#3A2A50", + "spec": { + "space": "srgb", + "hex": "#3a2a50" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "07d56eef40fd7967", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#2e9ab8", + "raw": "#2E9AB8", + "spec": { + "space": "srgb", + "hex": "#2e9ab8" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "5340ffc0902b8603", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#2a5060", + "raw": "#2A5060", + "spec": { + "space": "srgb", + "hex": "#2a5060" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "a735c6cadff938d8", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#0a0a0a", + "raw": "#0A0A0A", + "spec": { + "space": "srgb", + "hex": "#0a0a0a" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "6ee6a2239705aa9a", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "color", + "value": "#0a0a0a", + "raw": "#0a0a0a", + "spec": { + "space": "srgb", + "hex": "#0a0a0a" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "f88ec6215e9e2f44", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "8", + "raw": "8px", + "spec": { + "scalar": 8, + "unit": "px" + }, + "occurrences": 11, + "files_count": 1 + }, + { + "id": "48bcc0086b6d331a", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "2", + "raw": "2px", + "spec": { + "scalar": 2, + "unit": "px" + }, + "occurrences": 9, + "files_count": 1 + }, + { + "id": "17609981db70ed1c", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "20", + "raw": "20px", + "spec": { + "scalar": 20, + "unit": "px" + }, + "occurrences": 6, + "files_count": 1 + }, + { + "id": "26cb1d9a65ee3afc", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "radius", + "value": "999", + "raw": "999px", + "spec": { + "scalar": 999, + "unit": "px" + }, + "occurrences": 4, + "files_count": 1 + }, + { + "id": "0467bb0de326d5f0", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "4", + "raw": "4px", + "spec": { + "scalar": 4, + "unit": "px" + }, + "occurrences": 4, + "files_count": 1 + }, + { + "id": "3fe85b6317b95865", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "3", + "raw": "3px", + "spec": { + "scalar": 3, + "unit": "px" + }, + "occurrences": 4, + "files_count": 1 + }, + { + "id": "d5c420f261f6973b", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "200", + "raw": "200px", + "spec": { + "scalar": 200, + "unit": "px" + }, + "occurrences": 4, + "files_count": 1 + }, + { + "id": "ab7276c9e6e8ca10", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "12", + "raw": "12px", + "spec": { + "scalar": 12, + "unit": "px" + }, + "occurrences": 3, + "files_count": 1 + }, + { + "id": "7a150407b58b558c", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "10", + "raw": "10px", + "spec": { + "scalar": 10, + "unit": "px" + }, + "occurrences": 3, + "files_count": 1 + }, + { + "id": "03a8b74377468929", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "6", + "raw": "6px", + "spec": { + "scalar": 6, + "unit": "px" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "fe6e7ab8374e9190", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "64", + "raw": "64px", + "spec": { + "scalar": 64, + "unit": "px" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "8cca12472e29bc05", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "60", + "raw": "60px", + "spec": { + "scalar": 60, + "unit": "px" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "2acd9bfe599f9583", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "30", + "raw": "30px", + "spec": { + "scalar": 30, + "unit": "px" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "0dd21229fa72968b", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "28", + "raw": "28px", + "spec": { + "scalar": 28, + "unit": "px" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "d4b1e28865890fd2", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "1", + "raw": "1px", + "spec": { + "scalar": 1, + "unit": "px" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "6526e84b7bb2bc0e", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "breakpoint", + "value": "1440", + "raw": "1440px", + "spec": { + "scalar": 1440, + "unit": "px", + "label": "desktop" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "ac8952a956684a7c", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "96", + "raw": "96px", + "spec": { + "scalar": 96, + "unit": "px" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "0732c4cb48b5aad7", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "75", + "raw": "75px", + "spec": { + "scalar": 75, + "unit": "px" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "2f37ade927aa362c", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "44", + "raw": "44px", + "spec": { + "scalar": 44, + "unit": "px" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "5f6a00b7bd08f3bb", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "40", + "raw": "40px", + "spec": { + "scalar": 40, + "unit": "px" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "1e6bd58b860da10e", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "24", + "raw": "24px", + "spec": { + "scalar": 24, + "unit": "px" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "f4b0897a52c84421", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "16", + "raw": "16px", + "spec": { + "scalar": 16, + "unit": "px" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "b1f6f180edabd98d", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "14", + "raw": "14px", + "spec": { + "scalar": 14, + "unit": "px" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "dcc100963409d134", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "11", + "raw": "11px", + "spec": { + "scalar": 11, + "unit": "px" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "d2b1f15320a09f0b", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "100", + "raw": "100px", + "spec": { + "scalar": 100, + "unit": "px" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "ef05426d706131d3", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "8rem", + "raw": "8rem", + "spec": { + "scalar": 8, + "unit": "rem" + }, + "occurrences": 7, + "files_count": 7 + }, + { + "id": "4a89665e88473cbd", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "2rem", + "raw": "2rem", + "spec": { + "scalar": 2, + "unit": "rem" + }, + "occurrences": 3, + "files_count": 5 + }, + { + "id": "e44d4f64633fe41d", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "1rem", + "raw": "1rem", + "spec": { + "scalar": 1, + "unit": "rem" + }, + "occurrences": 3, + "files_count": 2 + }, + { + "id": "e114143269d04dc1", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "3rem", + "raw": "3rem", + "spec": { + "scalar": 3, + "unit": "rem" + }, + "occurrences": 2, + "files_count": 2 + }, + { + "id": "3d43f141d946f406", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "2.75rem", + "raw": "2.75rem", + "spec": { + "scalar": 2.75, + "unit": "rem" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "3554cc7c7aa186f9", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "12rem", + "raw": "12rem", + "spec": { + "scalar": 12, + "unit": "rem" + }, + "occurrences": 2, + "files_count": 2 + }, + { + "id": "de007fd1f6d77ba4", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "0.35rem", + "raw": "0.35rem", + "spec": { + "scalar": 0.35, + "unit": "rem" + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "e2aaf87eb6be1f6d", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "3.25rem", + "raw": "3.25rem", + "spec": { + "scalar": 3.25, + "unit": "rem" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "613d6504ba5c0849", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "22rem", + "raw": "22rem", + "spec": { + "scalar": 22, + "unit": "rem" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "8e1a94f5a893855f", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "2.5rem", + "raw": "2.5rem", + "spec": { + "scalar": 2.5, + "unit": "rem" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "3a3e22185485472c", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "18rem", + "raw": "18rem", + "spec": { + "scalar": 18, + "unit": "rem" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "be5b44549f78a831", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "16rem", + "raw": "16rem", + "spec": { + "scalar": 16, + "unit": "rem" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "584c9878e3fc9659", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "1.5rem", + "raw": "1.5rem", + "spec": { + "scalar": 1.5, + "unit": "rem" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "aa49aace62129a2b", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "1.25rem", + "raw": "1.25rem", + "spec": { + "scalar": 1.25, + "unit": "rem" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "0f74cf2da58aa928", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "1.15rem", + "raw": "1.15rem", + "spec": { + "scalar": 1.15, + "unit": "rem" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "6f1117620b51e086", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "0.8rem", + "raw": "0.8rem", + "spec": { + "scalar": 0.8, + "unit": "rem" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "62f944d2471eb834", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "0.4rem", + "raw": "0.4rem", + "spec": { + "scalar": 0.4, + "unit": "rem" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "07e474ab5bbd6a8b", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "spacing", + "value": "0.45rem", + "raw": "0.45rem", + "spec": { + "scalar": 0.45, + "unit": "rem" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "6335ef9d74e4dd51", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "motion", + "value": "0.2s", + "raw": "0.2s", + "spec": { + "duration_ms": 200 + }, + "occurrences": 9, + "files_count": 1 + }, + { + "id": "ea16f36543cd855d", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "motion", + "value": "0.15s", + "raw": "0.15s", + "spec": { + "duration_ms": 150 + }, + "occurrences": 3, + "files_count": 1 + }, + { + "id": "2974e3e705d96895", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "motion", + "value": "0.4s", + "raw": "0.4s", + "spec": { + "duration_ms": 400 + }, + "occurrences": 2, + "files_count": 1 + }, + { + "id": "a9d736217da6ce85", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "motion", + "value": "1s", + "raw": "1s", + "spec": { + "duration_ms": 1000 + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "aa47604665d17f01", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "typography", + "value": "system-ui", + "raw": "\"system-ui\"", + "spec": { + "family": "system-ui" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "dcdaff2a70d84bdc", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "typography", + "value": "Geist Mono", + "raw": "\"Geist Mono\"", + "spec": { + "family": "Geist Mono" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "14cdd5d410cd5ce4", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "typography", + "value": "serif", + "raw": "\"serif\"", + "spec": { + "family": "serif" + }, + "occurrences": 1, + "files_count": 1 + }, + { + "id": "ea0413ae97feefb2", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "kind": "motion", + "value": "cubic-bezier(0.33, 1, 0.68, 1)", + "raw": "cubic-bezier(0.33, 1, 0.68, 1)", + "spec": { + "easing": "cubic-bezier(0.33, 1, 0.68, 1)" + }, + "occurrences": 1, + "files_count": 1 + } + ], + "tokens": [ + { + "id": "cb0df5de955ad73c", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-white", + "alias_chain": [], + "resolved_value": "#ffffff", + "occurrences": 1 + }, + { + "id": "fa8cbe5ea824054a", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-black", + "alias_chain": [], + "resolved_value": "#000000", + "occurrences": 1 + }, + { + "id": "8213ca5fa4a4268a", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-gray-50", + "alias_chain": [], + "resolved_value": "#f5f5f5", + "occurrences": 1 + }, + { + "id": "541f124f7eb85071", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-gray-100", + "alias_chain": [], + "resolved_value": "#f0f0f0", + "occurrences": 1 + }, + { + "id": "03d2bcc1bbdc7d28", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-gray-200", + "alias_chain": [], + "resolved_value": "#e8e8e8", + "occurrences": 1 + }, + { + "id": "97ecf43a5736e8f2", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-gray-300", + "alias_chain": [], + "resolved_value": "#e5e5e5", + "occurrences": 1 + }, + { + "id": "8edd4046d074e50f", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-gray-400", + "alias_chain": [], + "resolved_value": "#cccccc", + "occurrences": 1 + }, + { + "id": "de44e9c887444944", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-gray-500", + "alias_chain": [], + "resolved_value": "#999999", + "occurrences": 1 + }, + { + "id": "30372bd858f83fee", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-gray-600", + "alias_chain": [], + "resolved_value": "#666666", + "occurrences": 1 + }, + { + "id": "1664b730ed23be81", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-gray-700", + "alias_chain": [], + "resolved_value": "#333333", + "occurrences": 1 + }, + { + "id": "ef061dbeaf5caa16", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-gray-800", + "alias_chain": [], + "resolved_value": "#232323", + "occurrences": 1 + }, + { + "id": "d62a715d0e50f18a", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-gray-900", + "alias_chain": [], + "resolved_value": "#1a1a1a", + "occurrences": 1 + }, + { + "id": "26e50e6bf93448d4", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-red-100", + "alias_chain": [], + "resolved_value": "#ff6b6b", + "occurrences": 1 + }, + { + "id": "b7965dbbcc900c46", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-red-200", + "alias_chain": [], + "resolved_value": "#f94b4b", + "occurrences": 1 + }, + { + "id": "88fd00e599db4d4e", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-blue-100", + "alias_chain": [], + "resolved_value": "#7cacff", + "occurrences": 1 + }, + { + "id": "88691479ae0d01fd", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-blue-200", + "alias_chain": [], + "resolved_value": "#5c98f9", + "occurrences": 1 + }, + { + "id": "26162117e66af8dc", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-green-100", + "alias_chain": [], + "resolved_value": "#a3d795", + "occurrences": 1 + }, + { + "id": "4cae2b881dc01f65", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-green-200", + "alias_chain": [], + "resolved_value": "#91cb80", + "occurrences": 1 + }, + { + "id": "41ef8bfe752c8d36", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-yellow-100", + "alias_chain": [], + "resolved_value": "#ffd966", + "occurrences": 1 + }, + { + "id": "66f38cbf9eeaadd3", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-yellow-200", + "alias_chain": [], + "resolved_value": "#fbcd44", + "occurrences": 1 + }, + { + "id": "2a33cb35eaa43967", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--radius", + "alias_chain": [], + "resolved_value": "20px", + "occurrences": 1 + }, + { + "id": "89d494ebb1262332", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--background-accent", + "alias_chain": ["--color-gray-900"], + "resolved_value": "#1a1a1a", + "occurrences": 1, + "by_theme": { + "light": "var(--color-gray-900)", + "dark": "var(--color-white)" + } + }, + { + "id": "d9657bca82623241", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--border-accent", + "alias_chain": ["--color-gray-900"], + "resolved_value": "#1a1a1a", + "occurrences": 1, + "by_theme": { + "light": "var(--color-gray-900)", + "dark": "var(--color-white)" + } + }, + { + "id": "19902eb417500d9d", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--text-accent", + "alias_chain": ["--color-gray-900"], + "resolved_value": "#1a1a1a", + "occurrences": 1, + "by_theme": { + "light": "var(--color-gray-900)", + "dark": "var(--color-white)" + } + }, + { + "id": "d05df9802efeb06e", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--background-default", + "alias_chain": ["--color-white"], + "resolved_value": "#ffffff", + "occurrences": 1, + "by_theme": { + "light": "var(--color-white)", + "dark": "var(--color-black)" + } + }, + { + "id": "355b9a2a4df5856f", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--background-alt", + "alias_chain": ["--color-gray-50"], + "resolved_value": "#f5f5f5", + "occurrences": 1, + "by_theme": { + "light": "var(--color-gray-50)", + "dark": "var(--color-gray-800)" + } + }, + { + "id": "0f4f1d6b1fe7318e", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--background-medium", + "alias_chain": ["--color-gray-400"], + "resolved_value": "#cccccc", + "occurrences": 1, + "by_theme": { + "light": "var(--color-gray-400)", + "dark": "var(--color-gray-700)" + } + }, + { + "id": "e0cf37d1da4a10a0", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--background-muted", + "alias_chain": ["--color-gray-100"], + "resolved_value": "#f0f0f0", + "occurrences": 1, + "by_theme": { + "light": "var(--color-gray-100)", + "dark": "var(--color-gray-800)" + } + }, + { + "id": "92b74be249662f38", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--background-inverse", + "alias_chain": ["--color-black"], + "resolved_value": "#000000", + "occurrences": 1, + "by_theme": { + "light": "var(--color-black)", + "dark": "var(--color-white)" + } + }, + { + "id": "11dfcdb269185b66", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--background-danger", + "alias_chain": ["--color-red-200"], + "resolved_value": "#f94b4b", + "occurrences": 1, + "by_theme": { + "light": "var(--color-red-200)", + "dark": "var(--color-red-100)" + } + }, + { + "id": "e94c478eeb3cfaf2", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--background-success", + "alias_chain": ["--color-green-200"], + "resolved_value": "#91cb80", + "occurrences": 1, + "by_theme": { + "light": "var(--color-green-200)", + "dark": "var(--color-green-100)" + } + }, + { + "id": "8e33834f0bd6e5e2", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--background-info", + "alias_chain": ["--color-blue-200"], + "resolved_value": "#5c98f9", + "occurrences": 1, + "by_theme": { + "light": "var(--color-blue-200)", + "dark": "var(--color-blue-100)" + } + }, + { + "id": "8ff9f2f4275738bc", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--background-warning", + "alias_chain": ["--color-yellow-200"], + "resolved_value": "#fbcd44", + "occurrences": 1, + "by_theme": { + "light": "var(--color-yellow-200)", + "dark": "var(--color-yellow-100)" + } + }, + { + "id": "11472fd804d05770", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--border-default", + "alias_chain": ["--color-gray-200"], + "resolved_value": "#e8e8e8", + "occurrences": 1, + "by_theme": { + "light": "var(--color-gray-200)", + "dark": "var(--color-gray-700)" + } + }, + { + "id": "bc1fa882feabc1b7", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--border-input", + "alias_chain": ["--color-gray-300"], + "resolved_value": "#e5e5e5", + "occurrences": 1, + "by_theme": { + "light": "var(--color-gray-300)", + "dark": "var(--color-gray-700)" + } + }, + { + "id": "7f81a8855762e4db", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--border-input-hover", + "alias_chain": ["--color-gray-400"], + "resolved_value": "#cccccc", + "occurrences": 1, + "by_theme": { + "light": "var(--color-gray-400)", + "dark": "var(--color-gray-600)" + } + }, + { + "id": "a480eff48092292b", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--border-strong", + "alias_chain": ["--color-gray-900"], + "resolved_value": "#1a1a1a", + "occurrences": 1, + "by_theme": { + "light": "var(--color-gray-900)", + "dark": "var(--color-white)" + } + }, + { + "id": "117eeeaad69b709f", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--border-card", + "alias_chain": ["--color-gray-200"], + "resolved_value": "#e8e8e8", + "occurrences": 1, + "by_theme": { + "light": "var(--color-gray-200)", + "dark": "var(--color-gray-700)" + } + }, + { + "id": "8cf1d6060432038e", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--border-inverse", + "alias_chain": ["--color-black"], + "resolved_value": "#000000", + "occurrences": 1, + "by_theme": { + "light": "var(--color-black)", + "dark": "var(--color-white)" + } + }, + { + "id": "7ce7823ae7307dae", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--border-danger", + "alias_chain": ["--color-red-200"], + "resolved_value": "#f94b4b", + "occurrences": 1 + }, + { + "id": "ae2c824e34b376aa", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--border-success", + "alias_chain": ["--color-green-200"], + "resolved_value": "#91cb80", + "occurrences": 1 + }, + { + "id": "03fe4b69ac215996", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--border-warning", + "alias_chain": ["--color-yellow-200"], + "resolved_value": "#fbcd44", + "occurrences": 1 + }, + { + "id": "0a201ab94de43e6b", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--border-info", + "alias_chain": ["--color-blue-200"], + "resolved_value": "#5c98f9", + "occurrences": 1 + }, + { + "id": "1a5199cdcf5849d2", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--text-default", + "alias_chain": ["--color-gray-900"], + "resolved_value": "#1a1a1a", + "occurrences": 1, + "by_theme": { + "light": "var(--color-gray-900)", + "dark": "var(--color-white)" + } + }, + { + "id": "3b99b302eb9364f7", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--text-muted", + "alias_chain": ["--color-gray-500"], + "resolved_value": "#999999", + "occurrences": 1 + }, + { + "id": "0bbb574cd3a22a8c", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--text-alt", + "alias_chain": ["--color-gray-600"], + "resolved_value": "#666666", + "occurrences": 1, + "by_theme": { + "light": "var(--color-gray-600)", + "dark": "var(--color-gray-500)" + } + }, + { + "id": "4254b03cc0a95a35", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--text-inverse", + "alias_chain": ["--color-white"], + "resolved_value": "#ffffff", + "occurrences": 1, + "by_theme": { + "light": "var(--color-white)", + "dark": "var(--color-black)" + } + }, + { + "id": "746df1a3500d78fe", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--text-danger", + "alias_chain": ["--color-red-200"], + "resolved_value": "#f94b4b", + "occurrences": 1, + "by_theme": { + "light": "var(--color-red-200)", + "dark": "var(--color-red-100)" + } + }, + { + "id": "1d64349b18172cf0", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--text-success", + "alias_chain": ["--color-green-200"], + "resolved_value": "#91cb80", + "occurrences": 1, + "by_theme": { + "light": "var(--color-green-200)", + "dark": "var(--color-green-100)" + } + }, + { + "id": "4d3efb919e0bb7f6", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--text-warning", + "alias_chain": ["--color-yellow-200"], + "resolved_value": "#fbcd44", + "occurrences": 1, + "by_theme": { + "light": "var(--color-yellow-200)", + "dark": "var(--color-yellow-100)" + } + }, + { + "id": "2e1dc0f80458dcca", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--text-info", + "alias_chain": ["--color-blue-200"], + "resolved_value": "#5c98f9", + "occurrences": 1, + "by_theme": { + "light": "var(--color-blue-200)", + "dark": "var(--color-blue-100)" + } + }, + { + "id": "eb3ac5c681596680", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--ring", + "alias_chain": ["--border-strong", "--color-gray-900"], + "resolved_value": "#1a1a1a", + "occurrences": 1 + }, + { + "id": "9041300d208ee096", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--dark-10", + "alias_chain": [], + "resolved_value": "rgba(26, 26, 26, 0.1)", + "occurrences": 1, + "by_theme": { + "light": "rgba(26, 26, 26, 0.1)", + "dark": "rgba(242, 242, 242, 0.1)" + } + }, + { + "id": "b81ed2736c6b3480", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--dark-40", + "alias_chain": [], + "resolved_value": "rgba(26, 26, 26, 0.4)", + "occurrences": 1, + "by_theme": { + "light": "rgba(26, 26, 26, 0.4)", + "dark": "rgba(242, 242, 242, 0.4)" + } + }, + { + "id": "f55746f6fc8c61f8", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--dark-04", + "alias_chain": [], + "resolved_value": "rgba(26, 26, 26, 0.04)", + "occurrences": 1, + "by_theme": { + "light": "rgba(26, 26, 26, 0.04)", + "dark": "rgba(242, 242, 242, 0.04)" + } + }, + { + "id": "9529fad07f81a343", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--shadow-mini", + "alias_chain": [], + "resolved_value": "0 2px 8px rgba(76, 76, 76, 0.15)", + "occurrences": 1, + "by_theme": { + "light": "0 2px 8px rgba(76, 76, 76, 0.15)", + "dark": "0 2px 8px rgba(0, 0, 0, 0.4)" + } + }, + { + "id": "3ff2382fa48f5140", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--shadow-mini-inset", + "alias_chain": [], + "resolved_value": "0 1px 4px rgba(76, 76, 76, 0.1) inset", + "occurrences": 1, + "by_theme": { + "light": "0 1px 4px rgba(76, 76, 76, 0.1) inset", + "dark": "0 1px 4px rgba(0, 0, 0, 0.5) inset" + } + }, + { + "id": "000205efc8c4b4cd", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--shadow-btn", + "alias_chain": [], + "resolved_value": "0 2px 8px rgba(76, 76, 76, 0.15)", + "occurrences": 1, + "by_theme": { + "light": "0 2px 8px rgba(76, 76, 76, 0.15)", + "dark": "0 2px 8px rgba(0, 0, 0, 0.3)" + } + }, + { + "id": "ad9824735daec0fc", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--shadow-card", + "alias_chain": [], + "resolved_value": "0 2px 8px rgba(76, 76, 76, 0.15)", + "occurrences": 1, + "by_theme": { + "light": "0 2px 8px rgba(76, 76, 76, 0.15)", + "dark": "0 2px 8px rgba(0, 0, 0, 0.4)" + } + }, + { + "id": "472f662bf8aaa623", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--shadow-elevated", + "alias_chain": [], + "resolved_value": "0 3px 12px rgba(76, 76, 76, 0.22)", + "occurrences": 1, + "by_theme": { + "light": "0 3px 12px rgba(76, 76, 76, 0.22)", + "dark": "0 3px 12px rgba(0, 0, 0, 0.5)" + } + }, + { + "id": "48611e920d74e1d5", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--shadow-popover", + "alias_chain": [], + "resolved_value": "0 8px 30px rgba(0, 0, 0, 0.12)", + "occurrences": 1, + "by_theme": { + "light": "0 8px 30px rgba(0, 0, 0, 0.12)", + "dark": "0 8px 30px rgba(0, 0, 0, 0.4)" + } + }, + { + "id": "dc162be1adc241d4", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--shadow-modal", + "alias_chain": [], + "resolved_value": "0 20px 60px rgba(0, 0, 0, 0.2)", + "occurrences": 1, + "by_theme": { + "light": "0 20px 60px rgba(0, 0, 0, 0.2)", + "dark": "0 20px 60px rgba(0, 0, 0, 0.6)" + } + }, + { + "id": "d658051aeb18fc51", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--shadow-kbd", + "alias_chain": [], + "resolved_value": "0 2px 8px rgba(76, 76, 76, 0.15)", + "occurrences": 1, + "by_theme": { + "light": "0 2px 8px rgba(76, 76, 76, 0.15)", + "dark": "0 2px 8px rgba(0, 0, 0, 0.4)" + } + }, + { + "id": "0ff3d81715e7d7eb", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--shadow-date-field-focus", + "alias_chain": [], + "resolved_value": "0 0 0 3px rgba(26, 26, 26, 0.15)", + "occurrences": 1, + "by_theme": { + "light": "0 0 0 3px rgba(26, 26, 26, 0.15)", + "dark": "0 0 0 3px rgba(244, 244, 245, 0.1)" + } + }, + { + "id": "d1debf06e38b290d", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--surface-dark", + "alias_chain": [], + "resolved_value": "#0a0a0a", + "occurrences": 1 + }, + { + "id": "112b3131d3f14654", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--surface-dark-text", + "alias_chain": [], + "resolved_value": "#f5f5f5", + "occurrences": 1 + }, + { + "id": "a923fd620357dc73", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--surface-dark-muted", + "alias_chain": [], + "resolved_value": "rgba(255, 255, 255, 0.5)", + "occurrences": 1 + }, + { + "id": "8a465ae1e5bf1c44", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--surface-dark-border", + "alias_chain": [], + "resolved_value": "rgba(255, 255, 255, 0.08)", + "occurrences": 1 + }, + { + "id": "983010c94c6731f9", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--chart-1", + "alias_chain": [], + "resolved_value": "#f6b44a", + "occurrences": 1 + }, + { + "id": "5ef2ec5c4825446e", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--chart-2", + "alias_chain": [], + "resolved_value": "#7585ff", + "occurrences": 1 + }, + { + "id": "a80dd35c1a205c2f", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--chart-3", + "alias_chain": [], + "resolved_value": "#d76a6a", + "occurrences": 1 + }, + { + "id": "36d0a070971af8f0", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--chart-4", + "alias_chain": [], + "resolved_value": "#d185e0", + "occurrences": 1 + }, + { + "id": "883d7b69cdf9cb53", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--chart-5", + "alias_chain": [], + "resolved_value": "#91cb80", + "occurrences": 1 + }, + { + "id": "d8d4435b5f3b5746", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--background", + "alias_chain": ["--background-default", "--color-white"], + "resolved_value": "#ffffff", + "occurrences": 1 + }, + { + "id": "6105084f233c6c53", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--foreground", + "alias_chain": ["--text-default", "--color-gray-900"], + "resolved_value": "#1a1a1a", + "occurrences": 1 + }, + { + "id": "6145c141293a5512", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--card", + "alias_chain": ["--background-default", "--color-white"], + "resolved_value": "#ffffff", + "occurrences": 1 + }, + { + "id": "5266020d816d9cb0", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--card-foreground", + "alias_chain": ["--text-default", "--color-gray-900"], + "resolved_value": "#1a1a1a", + "occurrences": 1 + }, + { + "id": "d5129e2925a69e62", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--popover", + "alias_chain": ["--background-default", "--color-white"], + "resolved_value": "#ffffff", + "occurrences": 1 + }, + { + "id": "7b4957e2dbb73224", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--popover-foreground", + "alias_chain": ["--text-default", "--color-gray-900"], + "resolved_value": "#1a1a1a", + "occurrences": 1 + }, + { + "id": "098a05bb762e4e97", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--primary", + "alias_chain": ["--background-accent", "--color-gray-900"], + "resolved_value": "#1a1a1a", + "occurrences": 1 + }, + { + "id": "7fab282fbb18b512", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--primary-foreground", + "alias_chain": ["--text-inverse", "--color-white"], + "resolved_value": "#ffffff", + "occurrences": 1 + }, + { + "id": "3bf5dce55a0f2b5f", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--secondary", + "alias_chain": ["--background-muted", "--color-gray-100"], + "resolved_value": "#f0f0f0", + "occurrences": 1 + }, + { + "id": "fc38505d2f4a2288", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--secondary-foreground", + "alias_chain": ["--text-default", "--color-gray-900"], + "resolved_value": "#1a1a1a", + "occurrences": 1 + }, + { + "id": "243245a7bf35e886", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--muted", + "alias_chain": ["--background-muted", "--color-gray-100"], + "resolved_value": "#f0f0f0", + "occurrences": 1 + }, + { + "id": "5db27d0ba94e075c", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--muted-foreground", + "alias_chain": ["--text-muted", "--color-gray-500"], + "resolved_value": "#999999", + "occurrences": 1 + }, + { + "id": "87d78b323afb3dd1", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--accent", + "alias_chain": ["--background-muted", "--color-gray-100"], + "resolved_value": "#f0f0f0", + "occurrences": 1 + }, + { + "id": "1fae63f3e9256ec9", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--accent-foreground", + "alias_chain": ["--text-default", "--color-gray-900"], + "resolved_value": "#1a1a1a", + "occurrences": 1 + }, + { + "id": "39319932a8a5dc2d", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--destructive", + "alias_chain": ["--background-danger", "--color-red-200"], + "resolved_value": "#f94b4b", + "occurrences": 1 + }, + { + "id": "fb90fa581191a837", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--destructive-foreground", + "alias_chain": [], + "resolved_value": "#ffffff", + "occurrences": 1 + }, + { + "id": "8eb09c0bc090bf82", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--border", + "alias_chain": ["--border-default", "--color-gray-200"], + "resolved_value": "#e8e8e8", + "occurrences": 1 + }, + { + "id": "fe2f98046ee3c6c4", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--input", + "alias_chain": ["--border-input", "--color-gray-300"], + "resolved_value": "#e5e5e5", + "occurrences": 1 + }, + { + "id": "3f8318aca2ab2c3c", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--sidebar", + "alias_chain": ["--background-default", "--color-white"], + "resolved_value": "#ffffff", + "occurrences": 1 + }, + { + "id": "88ecacad45734ced", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--sidebar-foreground", + "alias_chain": ["--text-default", "--color-gray-900"], + "resolved_value": "#1a1a1a", + "occurrences": 1 + }, + { + "id": "4769e59a81b0a386", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--sidebar-primary", + "alias_chain": ["--background-accent", "--color-gray-900"], + "resolved_value": "#1a1a1a", + "occurrences": 1 + }, + { + "id": "43f6f131de162abe", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--sidebar-primary-foreground", + "alias_chain": ["--text-inverse", "--color-white"], + "resolved_value": "#ffffff", + "occurrences": 1 + }, + { + "id": "c548facc6d30daa6", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--sidebar-accent", + "alias_chain": ["--background-muted", "--color-gray-100"], + "resolved_value": "#f0f0f0", + "occurrences": 1 + }, + { + "id": "834547a9fe9a2912", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--sidebar-accent-foreground", + "alias_chain": ["--text-default", "--color-gray-900"], + "resolved_value": "#1a1a1a", + "occurrences": 1 + }, + { + "id": "9559d09e51cfa072", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--sidebar-border", + "alias_chain": ["--border-default", "--color-gray-200"], + "resolved_value": "#e8e8e8", + "occurrences": 1 + }, + { + "id": "135e42097f86b628", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--sidebar-ring", + "alias_chain": ["--border-default", "--color-gray-200"], + "resolved_value": "#e8e8e8", + "occurrences": 1 + }, + { + "id": "bc4fc3c0259843a6", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--heading-display-font-size", + "alias_chain": [], + "resolved_value": "clamp(64px, 8vw, 96px)", + "occurrences": 1 + }, + { + "id": "bf70c74ab5d7c9d6", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--heading-display-line-height", + "alias_chain": [], + "resolved_value": "0.88", + "occurrences": 1 + }, + { + "id": "e64e51adadbdc369", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--heading-display-letter-spacing", + "alias_chain": [], + "resolved_value": "-0.05em", + "occurrences": 1 + }, + { + "id": "551ad94ab6b75a8c", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--heading-display-font-weight", + "alias_chain": [], + "resolved_value": "900", + "occurrences": 1 + }, + { + "id": "b37ffe360f471a83", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--heading-section-font-size", + "alias_chain": [], + "resolved_value": "clamp(44px, 5vw, 64px)", + "occurrences": 1 + }, + { + "id": "da67417bf64196e2", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--heading-section-line-height", + "alias_chain": [], + "resolved_value": "0.95", + "occurrences": 1 + }, + { + "id": "54b440772a141ec8", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--heading-section-letter-spacing", + "alias_chain": [], + "resolved_value": "-0.035em", + "occurrences": 1 + }, + { + "id": "e276ab90d053a3e3", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--heading-section-font-weight", + "alias_chain": [], + "resolved_value": "700", + "occurrences": 1 + }, + { + "id": "36c7fd9100309290", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--heading-sub-font-size", + "alias_chain": [], + "resolved_value": "clamp(28px, 3vw, 40px)", + "occurrences": 1 + }, + { + "id": "5dc95c6e6ec16bcf", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--heading-sub-line-height", + "alias_chain": [], + "resolved_value": "1", + "occurrences": 1 + }, + { + "id": "f92cd03e60b413ae", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--heading-sub-letter-spacing", + "alias_chain": [], + "resolved_value": "-0.02em", + "occurrences": 1 + }, + { + "id": "161f1f253f80ef66", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--heading-sub-font-weight", + "alias_chain": [], + "resolved_value": "700", + "occurrences": 1 + }, + { + "id": "f24c816af3ae7ad4", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--heading-card-font-size", + "alias_chain": [], + "resolved_value": "clamp(20px, 2vw, 28px)", + "occurrences": 1 + }, + { + "id": "3199e634efff3440", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--heading-card-line-height", + "alias_chain": [], + "resolved_value": "1.1", + "occurrences": 1 + }, + { + "id": "aa72e28a8b70df29", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--heading-card-letter-spacing", + "alias_chain": [], + "resolved_value": "-0.01em", + "occurrences": 1 + }, + { + "id": "361cc48210f57061", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--heading-card-font-weight", + "alias_chain": [], + "resolved_value": "600", + "occurrences": 1 + }, + { + "id": "1b05c7cb8a06bb35", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--display-size", + "alias_chain": [], + "resolved_value": "clamp(3rem, 12vw, 12rem)", + "occurrences": 1 + }, + { + "id": "3e6f0ab2d1b542b4", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--display-line-height", + "alias_chain": [], + "resolved_value": "0.85", + "occurrences": 1 + }, + { + "id": "0c597fc3e248ef4c", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--display-letter-spacing", + "alias_chain": [], + "resolved_value": "-0.05em", + "occurrences": 1 + }, + { + "id": "1527da444af8397f", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--body-reading-size", + "alias_chain": [], + "resolved_value": "clamp(1rem, 1.3vw, 1.25rem)", + "occurrences": 1 + }, + { + "id": "70c24c634c52d9a2", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--body-reading-line-height", + "alias_chain": [], + "resolved_value": "1.65", + "occurrences": 1 + }, + { + "id": "71e46d03cfad1ee4", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--body-reading-letter-spacing", + "alias_chain": [], + "resolved_value": "-0.01em", + "occurrences": 1 + }, + { + "id": "53a79d6d04e55cd2", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--label-font-size", + "alias_chain": [], + "resolved_value": "11px", + "occurrences": 1 + }, + { + "id": "3c2391e1e2903976", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--label-letter-spacing", + "alias_chain": [], + "resolved_value": "0.12em", + "occurrences": 1 + }, + { + "id": "0b849e85006c32a9", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--label-font-weight", + "alias_chain": [], + "resolved_value": "600", + "occurrences": 1 + }, + { + "id": "4c200e8abf9af09e", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--label-line-height", + "alias_chain": [], + "resolved_value": "1.2", + "occurrences": 1 + }, + { + "id": "c5269bdf6889b8cf", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--pullquote-size", + "alias_chain": [], + "resolved_value": "clamp(1.5rem, 3vw, 2.5rem)", + "occurrences": 1 + }, + { + "id": "10329852f513a96f", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--pullquote-line-height", + "alias_chain": [], + "resolved_value": "1.3", + "occurrences": 1 + }, + { + "id": "3fc0583efc3fa861", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--pullquote-weight", + "alias_chain": [], + "resolved_value": "300", + "occurrences": 1 + }, + { + "id": "249b61c001ad3634", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--pullquote-letter-spacing", + "alias_chain": [], + "resolved_value": "-0.02em", + "occurrences": 1 + }, + { + "id": "862fd726a776cfdf", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--page-container-max-width", + "alias_chain": [], + "resolved_value": "1440px", + "occurrences": 1 + }, + { + "id": "908e2d48bf37c420", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--page-container-side-gutter", + "alias_chain": [], + "resolved_value": "20px", + "occurrences": 1 + }, + { + "id": "64b82c560e2c97bb", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--section-padding-vertical", + "alias_chain": [], + "resolved_value": "100px", + "occurrences": 1 + }, + { + "id": "a17e9285e0b94474", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--section-heading-margin-bottom", + "alias_chain": [], + "resolved_value": "75px", + "occurrences": 1 + }, + { + "id": "f64dee4e923913d9", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--ease-spring", + "alias_chain": [], + "resolved_value": "cubic-bezier(0.33, 1, 0.68, 1)", + "occurrences": 1 + }, + { + "id": "6d4fbc30ccefe31c", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--duration-fast", + "alias_chain": [], + "resolved_value": "0.15s", + "occurrences": 1 + }, + { + "id": "fc10b79ac07b5678", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--duration-normal", + "alias_chain": [], + "resolved_value": "0.2s", + "occurrences": 1 + }, + { + "id": "2d3684d4b61e80d1", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--duration-slow", + "alias_chain": [], + "resolved_value": "0.4s", + "occurrences": 1 + }, + { + "id": "2f5a9a0a95255dac", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-background", + "alias_chain": ["--background", "--background-default", "--color-white"], + "resolved_value": "#ffffff", + "occurrences": 1 + }, + { + "id": "d55dd8bb9f8db72f", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-foreground", + "alias_chain": ["--foreground", "--text-default", "--color-gray-900"], + "resolved_value": "#1a1a1a", + "occurrences": 1 + }, + { + "id": "d47fcc342b506fc2", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-card", + "alias_chain": ["--card", "--background-default", "--color-white"], + "resolved_value": "#ffffff", + "occurrences": 1 + }, + { + "id": "fe1308ed2b42865b", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-card-foreground", + "alias_chain": [ + "--card-foreground", + "--text-default", + "--color-gray-900" + ], + "resolved_value": "#1a1a1a", + "occurrences": 1 + }, + { + "id": "bfa4e1a7ac7d11f6", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-popover", + "alias_chain": ["--popover", "--background-default", "--color-white"], + "resolved_value": "#ffffff", + "occurrences": 1 + }, + { + "id": "f2aa215413a9b752", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-popover-foreground", + "alias_chain": [ + "--popover-foreground", + "--text-default", + "--color-gray-900" + ], + "resolved_value": "#1a1a1a", + "occurrences": 1 + }, + { + "id": "124c24bd81747864", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-primary", + "alias_chain": ["--primary", "--background-accent", "--color-gray-900"], + "resolved_value": "#1a1a1a", + "occurrences": 1 + }, + { + "id": "6120c1833d1be4df", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-primary-foreground", + "alias_chain": [ + "--primary-foreground", + "--text-inverse", + "--color-white" + ], + "resolved_value": "#ffffff", + "occurrences": 1 + }, + { + "id": "b7a55a2d42e28314", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-secondary", + "alias_chain": ["--secondary", "--background-muted", "--color-gray-100"], + "resolved_value": "#f0f0f0", + "occurrences": 1 + }, + { + "id": "f004e1aae65a45d2", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-secondary-foreground", + "alias_chain": [ + "--secondary-foreground", + "--text-default", + "--color-gray-900" + ], + "resolved_value": "#1a1a1a", + "occurrences": 1 + }, + { + "id": "1912ee78976cbd5c", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-muted", + "alias_chain": ["--muted", "--background-muted", "--color-gray-100"], + "resolved_value": "#f0f0f0", + "occurrences": 1 + }, + { + "id": "1098dbdb603807e7", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-muted-foreground", + "alias_chain": ["--muted-foreground", "--text-muted", "--color-gray-500"], + "resolved_value": "#999999", + "occurrences": 1 + }, + { + "id": "da871c151bf65f6e", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-accent", + "alias_chain": ["--accent", "--background-muted", "--color-gray-100"], + "resolved_value": "#f0f0f0", + "occurrences": 1 + }, + { + "id": "89dc62abf78f41aa", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-accent-foreground", + "alias_chain": [ + "--accent-foreground", + "--text-default", + "--color-gray-900" + ], + "resolved_value": "#1a1a1a", + "occurrences": 1 + }, + { + "id": "04de6b660bb3368b", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-destructive", + "alias_chain": [ + "--destructive", + "--background-danger", + "--color-red-200" + ], + "resolved_value": "#f94b4b", + "occurrences": 1 + }, + { + "id": "1bf4a1e53b1e1f1c", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-destructive-foreground", + "alias_chain": ["--destructive-foreground"], + "resolved_value": "#ffffff", + "occurrences": 1 + }, + { + "id": "fdeee77bfff0d520", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-border", + "alias_chain": ["--border", "--border-default", "--color-gray-200"], + "resolved_value": "#e8e8e8", + "occurrences": 1 + }, + { + "id": "f157958260bde4c8", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-input", + "alias_chain": ["--input", "--border-input", "--color-gray-300"], + "resolved_value": "#e5e5e5", + "occurrences": 1 + }, + { + "id": "17578aea56422a67", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-background-default", + "alias_chain": ["--background-default", "--color-white"], + "resolved_value": "#ffffff", + "occurrences": 1 + }, + { + "id": "4bb6af94eae82ddd", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-background-alt", + "alias_chain": ["--background-alt", "--color-gray-50"], + "resolved_value": "#f5f5f5", + "occurrences": 1 + }, + { + "id": "fba9a3795903c1e6", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-background-medium", + "alias_chain": ["--background-medium", "--color-gray-400"], + "resolved_value": "#cccccc", + "occurrences": 1 + }, + { + "id": "132c998aeeba0459", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-background-inverse", + "alias_chain": ["--background-inverse", "--color-black"], + "resolved_value": "#000000", + "occurrences": 1 + }, + { + "id": "cc35408daf49c54c", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-background-muted", + "alias_chain": ["--background-muted", "--color-gray-100"], + "resolved_value": "#f0f0f0", + "occurrences": 1 + }, + { + "id": "146080977cd2d2f8", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-background-danger", + "alias_chain": ["--background-danger", "--color-red-200"], + "resolved_value": "#f94b4b", + "occurrences": 1 + }, + { + "id": "d484c0bf1937e4f3", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-background-success", + "alias_chain": ["--background-success", "--color-green-200"], + "resolved_value": "#91cb80", + "occurrences": 1 + }, + { + "id": "3ff3806e84dea446", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-background-info", + "alias_chain": ["--background-info", "--color-blue-200"], + "resolved_value": "#5c98f9", + "occurrences": 1 + }, + { + "id": "26c01a376a4604b0", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-background-warning", + "alias_chain": ["--background-warning", "--color-yellow-200"], + "resolved_value": "#fbcd44", + "occurrences": 1 + }, + { + "id": "97e90e168f5c001a", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-background-accent", + "alias_chain": ["--background-accent", "--color-gray-900"], + "resolved_value": "#1a1a1a", + "occurrences": 1 + }, + { + "id": "247eb91270339db0", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-border-accent", + "alias_chain": ["--border-accent", "--color-gray-900"], + "resolved_value": "#1a1a1a", + "occurrences": 1 + }, + { + "id": "eb4d5b18c284ea59", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-text-accent", + "alias_chain": ["--text-accent", "--color-gray-900"], + "resolved_value": "#1a1a1a", + "occurrences": 1 + }, + { + "id": "f867629e002fd0b1", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-border-default", + "alias_chain": ["--border-default", "--color-gray-200"], + "resolved_value": "#e8e8e8", + "occurrences": 1 + }, + { + "id": "1843e8391f966c9a", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-border-input", + "alias_chain": ["--border-input", "--color-gray-300"], + "resolved_value": "#e5e5e5", + "occurrences": 1 + }, + { + "id": "e1a84bf222df3be8", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-border-input-hover", + "alias_chain": ["--border-input-hover", "--color-gray-400"], + "resolved_value": "#cccccc", + "occurrences": 1 + }, + { + "id": "46f223bf0f902a7f", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-border-strong", + "alias_chain": ["--border-strong", "--color-gray-900"], + "resolved_value": "#1a1a1a", + "occurrences": 1 + }, + { + "id": "8e470a2735606b23", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-border-card", + "alias_chain": ["--border-card", "--color-gray-200"], + "resolved_value": "#e8e8e8", + "occurrences": 1 + }, + { + "id": "d0f8eb7be75b1a80", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-border-inverse", + "alias_chain": ["--border-inverse", "--color-black"], + "resolved_value": "#000000", + "occurrences": 1 + }, + { + "id": "35b229488430db7a", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-border-danger", + "alias_chain": ["--border-danger", "--color-red-200"], + "resolved_value": "#f94b4b", + "occurrences": 1 + }, + { + "id": "ca8197eb296733d3", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-border-success", + "alias_chain": ["--border-success", "--color-green-200"], + "resolved_value": "#91cb80", + "occurrences": 1 + }, + { + "id": "96aac5df8697f71e", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-border-warning", + "alias_chain": ["--border-warning", "--color-yellow-200"], + "resolved_value": "#fbcd44", + "occurrences": 1 + }, + { + "id": "3086a21e95a91d0e", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-border-info", + "alias_chain": ["--border-info", "--color-blue-200"], + "resolved_value": "#5c98f9", + "occurrences": 1 + }, + { + "id": "4ae94e5ebf5883a0", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-text-default", + "alias_chain": ["--text-default", "--color-gray-900"], + "resolved_value": "#1a1a1a", + "occurrences": 1 + }, + { + "id": "9ee34ec9f755ead1", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-text-muted", + "alias_chain": ["--text-muted", "--color-gray-500"], + "resolved_value": "#999999", + "occurrences": 1 + }, + { + "id": "a986f406d8779b03", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-text-alt", + "alias_chain": ["--text-alt", "--color-gray-600"], + "resolved_value": "#666666", + "occurrences": 1 + }, + { + "id": "ecede937cb1b7045", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-text-inverse", + "alias_chain": ["--text-inverse", "--color-white"], + "resolved_value": "#ffffff", + "occurrences": 1 + }, + { + "id": "e374daadeb796e74", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-text-danger", + "alias_chain": ["--text-danger", "--color-red-200"], + "resolved_value": "#f94b4b", + "occurrences": 1 + }, + { + "id": "17d913ce57d61691", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-text-success", + "alias_chain": ["--text-success", "--color-green-200"], + "resolved_value": "#91cb80", + "occurrences": 1 + }, + { + "id": "47375d24c38b7d67", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-text-warning", + "alias_chain": ["--text-warning", "--color-yellow-200"], + "resolved_value": "#fbcd44", + "occurrences": 1 + }, + { + "id": "decd88c38537ed08", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-text-info", + "alias_chain": ["--text-info", "--color-blue-200"], + "resolved_value": "#5c98f9", + "occurrences": 1 + }, + { + "id": "a2a3704df5466718", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-dark-10", + "alias_chain": ["--dark-10"], + "resolved_value": "rgba(26, 26, 26, 0.1)", + "occurrences": 1 + }, + { + "id": "4d9cdbc68318c955", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-dark-40", + "alias_chain": ["--dark-40"], + "resolved_value": "rgba(26, 26, 26, 0.4)", + "occurrences": 1 + }, + { + "id": "1ff7670a50a0469f", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-dark-04", + "alias_chain": ["--dark-04"], + "resolved_value": "rgba(26, 26, 26, 0.04)", + "occurrences": 1 + }, + { + "id": "918291ab84c5d96b", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-surface-dark", + "alias_chain": ["--surface-dark"], + "resolved_value": "#0a0a0a", + "occurrences": 1 + }, + { + "id": "b63a5e70dfb903d9", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-surface-dark-text", + "alias_chain": ["--surface-dark-text"], + "resolved_value": "#f5f5f5", + "occurrences": 1 + }, + { + "id": "78e4178e847afd35", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-surface-dark-muted", + "alias_chain": ["--surface-dark-muted"], + "resolved_value": "rgba(255, 255, 255, 0.5)", + "occurrences": 1 + }, + { + "id": "13e3f662de4dcb25", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-surface-dark-border", + "alias_chain": ["--surface-dark-border"], + "resolved_value": "rgba(255, 255, 255, 0.08)", + "occurrences": 1 + }, + { + "id": "d3bd382b85d91bd7", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--font-mono", + "alias_chain": [], + "resolved_value": "\"Geist Mono\", monospace", + "occurrences": 1 + }, + { + "id": "2420c9d2714f81f4", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--font-serif", + "alias_chain": [], + "resolved_value": "serif", + "occurrences": 1 + }, + { + "id": "21da98d0c3d9411e", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--radius-pill", + "alias_chain": [], + "resolved_value": "999px", + "occurrences": 1 + }, + { + "id": "d1ac92a1e9843734", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--radius-button", + "alias_chain": [], + "resolved_value": "999px", + "occurrences": 1 + }, + { + "id": "b945503b5a257f07", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--radius-input", + "alias_chain": [], + "resolved_value": "999px", + "occurrences": 1 + }, + { + "id": "8bcca92bea5231ea", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--radius-card", + "alias_chain": [], + "resolved_value": "20px", + "occurrences": 1 + }, + { + "id": "2821782150c5a4a1", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--radius-card-lg", + "alias_chain": [], + "resolved_value": "24px", + "occurrences": 1 + }, + { + "id": "ac4a8dc675ffaaf2", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--radius-card-sm", + "alias_chain": [], + "resolved_value": "14px", + "occurrences": 1 + }, + { + "id": "fa2a7dbad3526104", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--radius-dropdown", + "alias_chain": [], + "resolved_value": "10px", + "occurrences": 1 + }, + { + "id": "cbc6369b605db513", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--radius-modal", + "alias_chain": [], + "resolved_value": "16px", + "occurrences": 1 + }, + { + "id": "37ce03d30c7d193c", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--radius-sm", + "alias_chain": [], + "resolved_value": "calc(var(--radius) - 4px)", + "occurrences": 1 + }, + { + "id": "f6d81cb173ac5b89", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--radius-md", + "alias_chain": [], + "resolved_value": "calc(var(--radius) - 2px)", + "occurrences": 1 + }, + { + "id": "92e39c945832caa3", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--radius-lg", + "alias_chain": ["--radius"], + "resolved_value": "20px", + "occurrences": 1 + }, + { + "id": "d67c6f689629e889", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--radius-xl", + "alias_chain": [], + "resolved_value": "calc(var(--radius) + 4px)", + "occurrences": 1 + }, + { + "id": "df28f5a6707b3437", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-ring", + "alias_chain": ["--ring", "--border-strong", "--color-gray-900"], + "resolved_value": "#1a1a1a", + "occurrences": 1 + }, + { + "id": "0ce9081c13f1ca82", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--spacing-input", + "alias_chain": [], + "resolved_value": "3.25rem", + "occurrences": 1 + }, + { + "id": "3fb3e9f3b95501bf", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--spacing-input-sm", + "alias_chain": [], + "resolved_value": "2.75rem", + "occurrences": 1 + }, + { + "id": "72d8da27e0578a21", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--spacing-button", + "alias_chain": [], + "resolved_value": "2.75rem", + "occurrences": 1 + }, + { + "id": "f85ad488352a8258", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--spacing-button-sm", + "alias_chain": [], + "resolved_value": "2rem", + "occurrences": 1 + }, + { + "id": "b067a6136af76dff", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--text-xxs", + "alias_chain": [], + "resolved_value": "10px", + "occurrences": 1 + }, + { + "id": "18951054bbfbd351", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-chart-1", + "alias_chain": ["--chart-1"], + "resolved_value": "#f6b44a", + "occurrences": 1 + }, + { + "id": "bf8c7a0fc1e09641", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-chart-2", + "alias_chain": ["--chart-2"], + "resolved_value": "#7585ff", + "occurrences": 1 + }, + { + "id": "b2cf1391a0213ab5", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-chart-3", + "alias_chain": ["--chart-3"], + "resolved_value": "#d76a6a", + "occurrences": 1 + }, + { + "id": "c141c9907497b584", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-chart-4", + "alias_chain": ["--chart-4"], + "resolved_value": "#d185e0", + "occurrences": 1 + }, + { + "id": "519224717a35080d", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-chart-5", + "alias_chain": ["--chart-5"], + "resolved_value": "#91cb80", + "occurrences": 1 + }, + { + "id": "f92a8bae3de0133c", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-sidebar", + "alias_chain": ["--sidebar", "--background-default", "--color-white"], + "resolved_value": "#ffffff", + "occurrences": 1 + }, + { + "id": "9eea5d9d33bf7efb", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-sidebar-foreground", + "alias_chain": [ + "--sidebar-foreground", + "--text-default", + "--color-gray-900" + ], + "resolved_value": "#1a1a1a", + "occurrences": 1 + }, + { + "id": "48ffdceb752eb219", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-sidebar-primary", + "alias_chain": [ + "--sidebar-primary", + "--background-accent", + "--color-gray-900" + ], + "resolved_value": "#1a1a1a", + "occurrences": 1 + }, + { + "id": "8519e3a792896662", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-sidebar-primary-foreground", + "alias_chain": [ + "--sidebar-primary-foreground", + "--text-inverse", + "--color-white" + ], + "resolved_value": "#ffffff", + "occurrences": 1 + }, + { + "id": "1c65904fc353164c", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-sidebar-accent", + "alias_chain": [ + "--sidebar-accent", + "--background-muted", + "--color-gray-100" + ], + "resolved_value": "#f0f0f0", + "occurrences": 1 + }, + { + "id": "f7cecc91f460093f", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-sidebar-accent-foreground", + "alias_chain": [ + "--sidebar-accent-foreground", + "--text-default", + "--color-gray-900" + ], + "resolved_value": "#1a1a1a", + "occurrences": 1 + }, + { + "id": "944c77060111eb66", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-sidebar-border", + "alias_chain": [ + "--sidebar-border", + "--border-default", + "--color-gray-200" + ], + "resolved_value": "#e8e8e8", + "occurrences": 1 + }, + { + "id": "7e059285384c56af", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--color-sidebar-ring", + "alias_chain": ["--sidebar-ring", "--border-default", "--color-gray-200"], + "resolved_value": "#e8e8e8", + "occurrences": 1 + }, + { + "id": "7fe05bd17b9112f6", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--breakpoint-desktop", + "alias_chain": [], + "resolved_value": "1440px", + "occurrences": 1 + }, + { + "id": "b8dee9dd85440ad8", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--animate-accordion-down", + "alias_chain": [], + "resolved_value": "accordion-down 0.2s ease-out", + "occurrences": 1 + }, + { + "id": "67bfc0e3b3b985dd", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--animate-accordion-up", + "alias_chain": [], + "resolved_value": "accordion-up 0.2s ease-out", + "occurrences": 1 + }, + { + "id": "60cdf505ddf4991c", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--animate-caret-blink", + "alias_chain": [], + "resolved_value": "caret-blink 1s ease-out infinite", + "occurrences": 1 + }, + { + "id": "2e09deb7f2b3ab22", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--animate-scale-in", + "alias_chain": [], + "resolved_value": "scale-in 0.2s ease", + "occurrences": 1 + }, + { + "id": "f3b4e8c63d670772", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--animate-scale-out", + "alias_chain": [], + "resolved_value": "scale-out 0.15s ease", + "occurrences": 1 + }, + { + "id": "9ba43f9f8fb98c62", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--animate-fade-in", + "alias_chain": [], + "resolved_value": "fade-in 0.2s ease", + "occurrences": 1 + }, + { + "id": "c646397fffd6efee", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--animate-fade-out", + "alias_chain": [], + "resolved_value": "fade-out 0.15s ease", + "occurrences": 1 + }, + { + "id": "cb69b598d14d802a", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--animate-enter-from-left", + "alias_chain": [], + "resolved_value": "enter-from-left 0.2s ease", + "occurrences": 1 + }, + { + "id": "46fc321d9b36fe46", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--animate-enter-from-right", + "alias_chain": [], + "resolved_value": "enter-from-right 0.2s ease", + "occurrences": 1 + }, + { + "id": "a3dbc93ee468a3a9", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--animate-exit-to-left", + "alias_chain": [], + "resolved_value": "exit-to-left 0.2s ease", + "occurrences": 1 + }, + { + "id": "df9f5b8dafce8b3e", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--animate-exit-to-right", + "alias_chain": [], + "resolved_value": "exit-to-right 0.2s ease", + "occurrences": 1 + }, + { + "id": "da96512485cd3080", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "--animate-word-reveal", + "alias_chain": [], + "resolved_value": "word-reveal 0.4s ease-out", + "occurrences": 1 + } + ], + "components": [ + { + "id": "6d2f12160726c41d", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "accordion", + "discovered_via": "registry.json" + }, + { + "id": "979dafb281d630f9", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "alert-dialog", + "discovered_via": "registry.json" + }, + { + "id": "a103296963ca9c32", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "alert", + "discovered_via": "registry.json" + }, + { + "id": "7c241aa68487bbbf", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "aspect-ratio", + "discovered_via": "registry.json" + }, + { + "id": "276c3ad242788403", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "avatar", + "discovered_via": "registry.json" + }, + { + "id": "90b4746fa86fdb72", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "badge", + "discovered_via": "registry.json" + }, + { + "id": "5455f2e25e8ec7f4", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "breadcrumb", + "discovered_via": "registry.json" + }, + { + "id": "35dd4b60a7b42c9b", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "button-group", + "discovered_via": "registry.json" + }, + { + "id": "9176e496c60179cd", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "button", + "discovered_via": "registry.json" + }, + { + "id": "afd37c648aa0c22e", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "calendar", + "discovered_via": "registry.json" + }, + { + "id": "5d193da58fbd809a", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "card", + "discovered_via": "registry.json" + }, + { + "id": "b624ff585a15ed20", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "carousel", + "discovered_via": "registry.json" + }, + { + "id": "68401b063c6bf4f9", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "chart", + "discovered_via": "registry.json" + }, + { + "id": "b0f2dd5251ea78ad", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "checkbox", + "discovered_via": "registry.json" + }, + { + "id": "e3ee864d9d917916", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "collapsible", + "discovered_via": "registry.json" + }, + { + "id": "3011ac0c599ddee7", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "command", + "discovered_via": "registry.json" + }, + { + "id": "c43a0f72a5e34bb0", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "context-menu", + "discovered_via": "registry.json" + }, + { + "id": "37230c20c1ce99bf", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "dialog", + "discovered_via": "registry.json" + }, + { + "id": "357a10b4cc9729ed", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "drawer", + "discovered_via": "registry.json" + }, + { + "id": "6ca8e64d5d97c13a", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "dropdown-menu", + "discovered_via": "registry.json" + }, + { + "id": "2bd39bd7797b6827", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "form", + "discovered_via": "registry.json" + }, + { + "id": "0fb13b7b70402039", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "hover-card", + "discovered_via": "registry.json" + }, + { + "id": "0cd49ce69c55b1bb", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "input-group", + "discovered_via": "registry.json" + }, + { + "id": "f8b4a54676fffd32", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "input-otp", + "discovered_via": "registry.json" + }, + { + "id": "d2b93a51f8251454", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "input", + "discovered_via": "registry.json" + }, + { + "id": "e0c3ab25a876c27b", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "label", + "discovered_via": "registry.json" + }, + { + "id": "230fb7b9f6c51e6b", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "menubar", + "discovered_via": "registry.json" + }, + { + "id": "be965ddce05f6bae", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "navigation-menu", + "discovered_via": "registry.json" + }, + { + "id": "67073848018858c5", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "pagination", + "discovered_via": "registry.json" + }, + { + "id": "9eed09f2d64026bc", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "popover", + "discovered_via": "registry.json" + }, + { + "id": "7520f45b90a7d790", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "progress", + "discovered_via": "registry.json" + }, + { + "id": "a5bc1e1d5eece144", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "radio-group", + "discovered_via": "registry.json" + }, + { + "id": "57f4dbc96133af92", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "resizable", + "discovered_via": "registry.json" + }, + { + "id": "744f55a017613174", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "scroll-area", + "discovered_via": "registry.json" + }, + { + "id": "cbb1b273705b8c3a", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "select", + "discovered_via": "registry.json" + }, + { + "id": "a6731007b390a7cb", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "separator", + "discovered_via": "registry.json" + }, + { + "id": "5ac98c3861fd2bbe", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "sheet", + "discovered_via": "registry.json" + }, + { + "id": "f722d6d84cd1d84c", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "sidebar", + "discovered_via": "registry.json" + }, + { + "id": "0dd70a30d7933f92", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "skeleton", + "discovered_via": "registry.json" + }, + { + "id": "cff16fe49fb0dc06", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "slider", + "discovered_via": "registry.json" + }, + { + "id": "4a1fc20196bceda8", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "sonner", + "discovered_via": "registry.json" + }, + { + "id": "1a2cc13af69b439b", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "spinner", + "discovered_via": "registry.json" + }, + { + "id": "6196b5284e21d11d", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "switch", + "discovered_via": "registry.json" + }, + { + "id": "951d177329479541", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "table", + "discovered_via": "registry.json" + }, + { + "id": "80278466976164ec", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "tabs", + "discovered_via": "registry.json" + }, + { + "id": "a350560e9254a3d8", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "textarea", + "discovered_via": "registry.json" + }, + { + "id": "8df8c5bff7382fa9", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "toggle-group", + "discovered_via": "registry.json" + }, + { + "id": "4413cbe20cc2c103", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "toggle", + "discovered_via": "registry.json" + }, + { + "id": "7612061697dea683", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "tooltip", + "discovered_via": "registry.json" + }, + { + "id": "cef5e1893ef71cfa", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "agent", + "discovered_via": "registry.json" + }, + { + "id": "cb2246a1a0a594a0", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "artifact", + "discovered_via": "registry.json" + }, + { + "id": "54fcaf7ad9ec7034", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "attachments", + "discovered_via": "registry.json" + }, + { + "id": "347e4b2dd05be0a3", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "audio-player", + "discovered_via": "registry.json" + }, + { + "id": "d27fd6c7bd17b6a4", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "canvas", + "discovered_via": "registry.json" + }, + { + "id": "fb83ef54dd58def9", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "chain-of-thought", + "discovered_via": "registry.json" + }, + { + "id": "4acb9604bfe89289", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "checkpoint", + "discovered_via": "registry.json" + }, + { + "id": "2812a746582e9ab5", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "code-block", + "discovered_via": "registry.json" + }, + { + "id": "bbb940137f1828e3", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "commit", + "discovered_via": "registry.json" + }, + { + "id": "2d3f67917ed7c328", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "confirmation", + "discovered_via": "registry.json" + }, + { + "id": "8393143ec2de398a", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "connection", + "discovered_via": "registry.json" + }, + { + "id": "707f20d3130f35d5", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "context", + "discovered_via": "registry.json" + }, + { + "id": "d9318af758610849", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "controls", + "discovered_via": "registry.json" + }, + { + "id": "2d8c76aef78cd232", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "conversation", + "discovered_via": "registry.json" + }, + { + "id": "719fdb1140e927f9", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "edge", + "discovered_via": "registry.json" + }, + { + "id": "b682bd4dcb160070", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "environment-variables", + "discovered_via": "registry.json" + }, + { + "id": "ad229aee1410e523", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "file-tree", + "discovered_via": "registry.json" + }, + { + "id": "94beb1c2d9ff2845", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "image", + "discovered_via": "registry.json" + }, + { + "id": "e51180938ec14b8c", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "inline-citation", + "discovered_via": "registry.json" + }, + { + "id": "7b90233a11529ce9", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "jsx-preview", + "discovered_via": "registry.json" + }, + { + "id": "ef2cb361cccba988", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "message", + "discovered_via": "registry.json" + }, + { + "id": "4be18327f5b08c6c", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "mic-selector", + "discovered_via": "registry.json" + }, + { + "id": "e70c3de12bb6d8ea", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "model-selector", + "discovered_via": "registry.json" + }, + { + "id": "32ad3eafd71afa12", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "node", + "discovered_via": "registry.json" + }, + { + "id": "a4dc760565bee6c2", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "open-in-chat", + "discovered_via": "registry.json" + }, + { + "id": "5fff8ca1cc7e8a23", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "package-info", + "discovered_via": "registry.json" + }, + { + "id": "2c0fefee5e534ee4", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "panel", + "discovered_via": "registry.json" + }, + { + "id": "75c3ac0441dece32", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "persona", + "discovered_via": "registry.json" + }, + { + "id": "9315380db9a8bda5", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "plan", + "discovered_via": "registry.json" + }, + { + "id": "c5d442865455dabf", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "prompt-input", + "discovered_via": "registry.json" + }, + { + "id": "751cbf26da331c5e", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "queue", + "discovered_via": "registry.json" + }, + { + "id": "51627933795cfd9f", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "reasoning", + "discovered_via": "registry.json" + }, + { + "id": "d50f9446f8cf4163", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "sandbox", + "discovered_via": "registry.json" + }, + { + "id": "dca1fcd6caf24479", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "schema-display", + "discovered_via": "registry.json" + }, + { + "id": "0c8f9133335d0f1f", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "shimmer", + "discovered_via": "registry.json" + }, + { + "id": "faf08e9917809e42", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "snippet", + "discovered_via": "registry.json" + }, + { + "id": "ac8f38e930abf05d", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "sources", + "discovered_via": "registry.json" + }, + { + "id": "2a1ad587392af5c9", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "speech-input", + "discovered_via": "registry.json" + }, + { + "id": "30fea3317b5c23ac", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "stack-trace", + "discovered_via": "registry.json" + }, + { + "id": "3699dfde5fb9bda5", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "suggestion", + "discovered_via": "registry.json" + }, + { + "id": "0be350bee51fb4c3", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "task", + "discovered_via": "registry.json" + }, + { + "id": "471c27e53ec79afc", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "terminal", + "discovered_via": "registry.json" + }, + { + "id": "c9a84b2b2c637f82", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "test-results", + "discovered_via": "registry.json" + }, + { + "id": "6b0d88cc6cfbed59", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "tool", + "discovered_via": "registry.json" + }, + { + "id": "a8b9f241eeb744e2", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "toolbar", + "discovered_via": "registry.json" + }, + { + "id": "5dba9261593e5451", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "transcription", + "discovered_via": "registry.json" + }, + { + "id": "251644133d18d6c0", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "voice-selector", + "discovered_via": "registry.json" + }, + { + "id": "b980390231ee0de8", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "web-preview", + "discovered_via": "registry.json" + } + ], + "libraries": [ + { + "id": "955edb1977d13f5b", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "@hookform/resolvers", + "kind": "forms", + "version": "^5.2.2" + }, + { + "id": "2ab539264bf22bb3", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "@radix-ui/react-accordion", + "kind": "primitives", + "version": "^1.2.12" + }, + { + "id": "7870c62c4e9db858", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "@radix-ui/react-alert-dialog", + "kind": "primitives", + "version": "^1.1.15" + }, + { + "id": "a2ec396fadd46b86", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "@radix-ui/react-aspect-ratio", + "kind": "primitives", + "version": "^1.1.8" + }, + { + "id": "d8ba0badfa7f52e7", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "@radix-ui/react-avatar", + "kind": "primitives", + "version": "^1.1.11" + }, + { + "id": "d2a20eec838ad1a9", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "@radix-ui/react-checkbox", + "kind": "primitives", + "version": "^1.3.3" + }, + { + "id": "7a0a367b40c09440", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "@radix-ui/react-collapsible", + "kind": "primitives", + "version": "^1.1.12" + }, + { + "id": "f474b047d92d3e5b", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "@radix-ui/react-context-menu", + "kind": "primitives", + "version": "^2.2.16" + }, + { + "id": "23b6506604a19c9b", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "@radix-ui/react-dialog", + "kind": "primitives", + "version": "^1.1.15" + }, + { + "id": "be43a3944427cb99", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "@radix-ui/react-dropdown-menu", + "kind": "primitives", + "version": "^2.1.16" + }, + { + "id": "996b036e73d549f8", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "@radix-ui/react-hover-card", + "kind": "primitives", + "version": "^1.1.15" + }, + { + "id": "329bd4e2ac504f3b", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "@radix-ui/react-label", + "kind": "primitives", + "version": "^2.1.8" + }, + { + "id": "b6cb1ae55ed03033", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "@radix-ui/react-menubar", + "kind": "primitives", + "version": "^1.1.16" + }, + { + "id": "37cdb92d1d6bfabd", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "@radix-ui/react-navigation-menu", + "kind": "primitives", + "version": "^1.2.14" + }, + { + "id": "b21ba96ead7c36d0", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "@radix-ui/react-popover", + "kind": "primitives", + "version": "^1.1.15" + }, + { + "id": "86cd9fe57015a3e9", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "@radix-ui/react-progress", + "kind": "primitives", + "version": "^1.1.8" + }, + { + "id": "be0a890be5ec59e2", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "@radix-ui/react-radio-group", + "kind": "primitives", + "version": "^1.3.8" + }, + { + "id": "fe5a84aedcbdc51b", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "@radix-ui/react-scroll-area", + "kind": "primitives", + "version": "^1.2.10" + }, + { + "id": "e9af4efbb2db6972", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "@radix-ui/react-select", + "kind": "primitives", + "version": "^2.2.6" + }, + { + "id": "e40834caa601d650", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "@radix-ui/react-separator", + "kind": "primitives", + "version": "^1.1.8" + }, + { + "id": "93fde33bb4b4e7d8", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "@radix-ui/react-slider", + "kind": "primitives", + "version": "^1.3.6" + }, + { + "id": "1e65491f959715a0", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "@radix-ui/react-slot", + "kind": "primitives", + "version": "^1.2.4" + }, + { + "id": "1389356cc2bb1302", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "@radix-ui/react-switch", + "kind": "primitives", + "version": "^1.2.6" + }, + { + "id": "760326142ce687a9", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "@radix-ui/react-tabs", + "kind": "primitives", + "version": "^1.1.13" + }, + { + "id": "1319a10801a33732", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "@radix-ui/react-toggle", + "kind": "primitives", + "version": "^1.1.10" + }, + { + "id": "f1eeb95b3e63e4d2", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "@radix-ui/react-toggle-group", + "kind": "primitives", + "version": "^1.1.11" + }, + { + "id": "2a3f4691d0aa5e7a", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "@radix-ui/react-tooltip", + "kind": "primitives", + "version": "^1.2.8" + }, + { + "id": "675b993d39faab35", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "@radix-ui/react-use-controllable-state", + "kind": "primitives", + "version": "^1.2.2" + }, + { + "id": "c3b9988c1ecab4ba", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "class-variance-authority", + "kind": "variant-helper", + "version": "^0.7.1" + }, + { + "id": "2bd418c0a6034d0f", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "clsx", + "kind": "class-utils", + "version": "^2.1.1" + }, + { + "id": "5fe7d1a752cdcb60", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "cmdk", + "kind": "command-palette", + "version": "^1.1.1" + }, + { + "id": "a56b700c1acf0781", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "date-fns", + "kind": "date", + "version": "^4.1.0" + }, + { + "id": "1e4e1f78d154a64c", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "lucide-react", + "kind": "icons", + "version": "^1.7.0" + }, + { + "id": "c1113c9292278754", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "motion", + "kind": "motion", + "version": "^12.38.0" + }, + { + "id": "f257b0b1f90ce7f4", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "react-day-picker", + "kind": "date", + "version": "9.14.0" + }, + { + "id": "3ff7b7953dbf64fa", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "react-hook-form", + "kind": "forms", + "version": "^7.72.0" + }, + { + "id": "cacd86649a83786e", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "recharts", + "kind": "charts", + "version": "^3.8.1" + }, + { + "id": "b5e83ed0f2d963ed", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "sonner", + "kind": "toast", + "version": "^2.0.7" + }, + { + "id": "248a95b02f4fa83e", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "tailwind-merge", + "kind": "class-utils", + "version": "^3.5.0" + }, + { + "id": "5d67e888930bf3ea", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "tw-animate-css", + "kind": "animation", + "version": "^1.4.0" + }, + { + "id": "45342a3452d38062", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "vaul", + "kind": "drawer", + "version": "^1.1.2" + }, + { + "id": "8351e3f559ab3dd3", + "source": { + "target": "github:block/ghost#packages/ghost-ui", + "commit": "916e728489f603407c6d00b798f49acd3975e7bb", + "scanned_at": "2026-04-29T16:10:00Z", + "scanner_version": "0.1.0" + }, + "name": "zod", + "kind": "validation", + "version": "^4.3.6" + } + ] +} diff --git a/dogfood/ghost-ui/attempt-2/build-bucket.mjs b/dogfood/ghost-ui/attempt-2/build-bucket.mjs new file mode 100644 index 0000000..c77faa6 --- /dev/null +++ b/dogfood/ghost-ui/attempt-2/build-bucket.mjs @@ -0,0 +1,316 @@ +#!/usr/bin/env node +// Survey-recipe extraction script for packages/ghost-ui. +// Enumerates exhaustively: tokens from main.css, components from registry.json, +// libraries from package.json, values from frequency-clustered hex/scalar/etc. +// Writes a bucket.json with empty IDs (fix-ids will populate). + +import { execSync } from "node:child_process"; +import { readFileSync, writeFileSync } from "node:fs"; +import { resolve } from "node:path"; + +const ROOT = "/Users/nahiyan/Development/ghost/packages/ghost-ui"; +const SOURCE = { + target: "github:block/ghost#packages/ghost-ui", + commit: execSync("git rev-parse HEAD", { + cwd: "/Users/nahiyan/Development/ghost", + }) + .toString() + .trim(), + scanned_at: "2026-04-29T16:10:00Z", + scanner_version: "0.1.0", +}; + +// ---- 1. Tokens — every named CSS custom property in main.css ---- + +const mainCss = readFileSync(resolve(ROOT, "src/styles/main.css"), "utf-8"); + +// Walk main.css line-by-line. Each `--name: value;` is a declaration. +// We dedupe by name, prefer the :root / @theme declaration as the canonical +// source, and capture per-theme overrides under by_theme. +const tokens = new Map(); // name -> { name, declarations: { theme -> value } } +let currentScope = "theme"; // "theme" | "root" | "dark" +const lines = mainCss.split("\n"); +for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + // Update scope on block boundaries. + if (/^\s*@theme\s*\{/.test(line)) currentScope = "theme"; + else if (/^\s*@theme\s+inline\s*\{/.test(line)) currentScope = "theme-inline"; + else if (/^\s*:root\s*\{/.test(line)) currentScope = "root"; + else if (/^\s*\.dark\s*\{/.test(line)) currentScope = "dark"; + else if (/^\s*@layer\s+/.test(line)) currentScope = "layer"; + + // Match a custom property declaration. + const m = line.match(/^\s*(--[a-z0-9-]+)\s*:\s*([^;]+?)\s*;\s*$/i); + if (!m) continue; + const [, name, value] = m; + + if (!tokens.has(name)) { + tokens.set(name, { name, scopes: {} }); + } + const t = tokens.get(name); + if (!t.scopes[currentScope]) t.scopes[currentScope] = value; +} + +// Build token rows. Resolve simple alias chains by following var() refs. +function resolveChain(name, byScope, depth = 0) { + if (depth > 10) + return { chain: [], resolved: byScope[name]?.scopes?.root ?? "" }; + const scoped = + byScope[name]?.scopes?.root ?? + byScope[name]?.scopes?.theme ?? + byScope[name]?.scopes?.["theme-inline"] ?? + ""; + const m = scoped.match(/^var\(\s*(--[a-z0-9-]+)/i); + if (!m) return { chain: [], resolved: scoped }; + const next = m[1]; + const sub = resolveChain(next, byScope, depth + 1); + return { chain: [next, ...sub.chain], resolved: sub.resolved }; +} + +const byName = Object.fromEntries(tokens); +const tokenRows = []; +for (const t of tokens.values()) { + const { chain, resolved } = resolveChain(t.name, byName); + const row = { + id: "", + source: SOURCE, + name: t.name, + alias_chain: chain, + resolved_value: resolved, + occurrences: 1, + }; + // by_theme: capture light vs dark divergence when both scopes have a declaration. + const root = t.scopes.root; + const dark = t.scopes.dark; + if (root && dark && root !== dark) { + row.by_theme = { light: root, dark }; + } + tokenRows.push(row); +} +console.error(`tokens: ${tokenRows.length}`); + +// ---- 2. Components — every registry:ui item ---- + +const registry = JSON.parse( + readFileSync(resolve(ROOT, "registry.json"), "utf-8"), +); +const componentRows = registry.items + .filter((i) => i.type === "registry:ui") + .map((i) => ({ + id: "", + source: SOURCE, + name: i.name, + discovered_via: "registry.json", + })); +console.error(`components: ${componentRows.length}`); + +// ---- 3. Libraries — every external dep that contributes design surface ---- + +const pkg = JSON.parse(readFileSync(resolve(ROOT, "package.json"), "utf-8")); +const deps = pkg.dependencies || {}; +const LIBRARY_KIND = (name) => { + if (name.startsWith("@radix-ui/react-")) return "primitives"; + if (name === "lucide-react") return "icons"; + if (name === "recharts") return "charts"; + if (name === "tw-animate-css") return "animation"; + if (name === "motion" || name === "framer-motion") return "motion"; + if (name === "@hookform/resolvers" || name === "react-hook-form") + return "forms"; + if (name === "zod") return "validation"; + if (name === "date-fns" || name === "react-day-picker") return "date"; + if (name === "cmdk") return "command-palette"; + if (name === "sonner") return "toast"; + if (name === "vaul") return "drawer"; + if (name === "class-variance-authority") return "variant-helper"; + if (name === "clsx" || name === "tailwind-merge") return "class-utils"; + return null; +}; +const libraryRows = []; +for (const [name, version] of Object.entries(deps)) { + const kind = LIBRARY_KIND(name); + if (!kind) continue; + libraryRows.push({ + id: "", + source: SOURCE, + name, + kind, + version, + }); +} +console.error(`libraries: ${libraryRows.length}`); + +// ---- 4. Values — frequency-clustered literals across the design system ---- + +function rgFreq(pattern, glob) { + try { + const out = execSync( + `rg -oNI '${pattern}' -g '${glob}' src/ 2>/dev/null | sort | uniq -c | sort -rn`, + { cwd: ROOT, encoding: "utf-8" }, + ); + return out + .split("\n") + .filter(Boolean) + .map((l) => { + const m = l.match(/^\s*(\d+)\s+(.+)$/); + return m ? { count: Number(m[1]), value: m[2] } : null; + }) + .filter(Boolean); + } catch { + return []; + } +} + +function rgFilesContaining(pattern, glob) { + try { + const out = execSync(`rg -lN '${pattern}' -g '${glob}' src/ 2>/dev/null`, { + cwd: ROOT, + encoding: "utf-8", + }); + return out.split("\n").filter(Boolean).length; + } catch { + return 0; + } +} + +const valueRows = []; + +// Hex colors +const hexes = rgFreq("#[0-9a-fA-F]{6}", "*.{ts,tsx,css}"); +for (const { count, value } of hexes) { + if (count < 1) continue; + valueRows.push({ + id: "", + source: SOURCE, + kind: "color", + value: value.toLowerCase(), + raw: value, + spec: { space: "srgb", hex: value.toLowerCase() }, + occurrences: count, + files_count: rgFilesContaining(value, "*.{ts,tsx,css}"), + }); +} + +// px scalars (split into spacing / radius / breakpoint / shadow-blur / layout-primitive) +const pxScalars = rgFreq("\\b[0-9]+(\\.[0-9]+)?px\\b", "*.{css}"); +for (const { count, value } of pxScalars) { + const num = parseFloat(value); + // Heuristic kind by value range — agent's role-hypothesis guess + let kind = "spacing"; + if (num === 999 || num === 1440) { + kind = num === 1440 ? "breakpoint" : "radius"; + } else if (num >= 1000) { + kind = "layout-primitive"; + } + valueRows.push({ + id: "", + source: SOURCE, + kind, + value: String(num), + raw: value, + spec: + kind === "breakpoint" + ? { + scalar: num, + unit: "px", + label: num === 1440 ? "desktop" : undefined, + } + : { scalar: num, unit: "px" }, + occurrences: count, + files_count: rgFilesContaining(value, "*.{css}"), + }); +} + +// rem scalars +const remScalars = rgFreq("\\b[0-9]+(\\.[0-9]+)?rem\\b", "*.{ts,tsx,css}"); +for (const { count, value } of remScalars) { + valueRows.push({ + id: "", + source: SOURCE, + kind: "spacing", + value, + raw: value, + spec: { scalar: parseFloat(value), unit: "rem" }, + occurrences: count, + files_count: rgFilesContaining(value, "*.{ts,tsx,css}"), + }); +} + +// motion durations +const durations = rgFreq("\\b[0-9]+(\\.[0-9]+)?s\\b", "*.{css}"); +for (const { count, value } of durations) { + // Skip values likely not durations (e.g. 999s would be junk; capping) + const seconds = parseFloat(value); + if (seconds > 5) continue; + valueRows.push({ + id: "", + source: SOURCE, + kind: "motion", + value, + raw: value, + spec: { duration_ms: Math.round(seconds * 1000) }, + occurrences: count, + files_count: rgFilesContaining(value, "*.{css}"), + }); +} + +// font-family declarations (string literals in @theme + @theme inline) +const families = new Set(); +const fontMatches = mainCss.match(/--font-[a-z-]+:\s*([^;]+);/gi) || []; +for (const m of fontMatches) { + const valueMatch = m.match(/--font-[a-z-]+:\s*([^;]+);/i); + if (!valueMatch) continue; + // Pull primary family name (first comma-separated entry, dequoted) + const primary = valueMatch[1] + .split(",")[0] + .trim() + .replace(/^["']|["']$/g, ""); + if (primary && primary !== "var") families.add(primary); +} +for (const family of families) { + valueRows.push({ + id: "", + source: SOURCE, + kind: "typography", + value: family, + raw: `"${family}"`, + spec: { family }, + occurrences: 1, + files_count: 1, + }); +} + +// easing +const easings = mainCss.match(/cubic-bezier\([^)]+\)/g) || []; +for (const easing of new Set(easings)) { + valueRows.push({ + id: "", + source: SOURCE, + kind: "motion", + value: easing, + raw: easing, + spec: { easing }, + occurrences: 1, + files_count: 1, + }); +} + +console.error(`values: ${valueRows.length}`); + +// ---- 5. Write ---- + +const bucket = { + schema: "ghost.bucket/v1", + sources: [SOURCE], + values: valueRows, + tokens: tokenRows, + components: componentRows, + libraries: libraryRows, +}; + +writeFileSync( + "/tmp/ghost-ui-scan/bucket.json", + JSON.stringify(bucket, null, 2) + "\n", +); +console.error( + `\nbucket totals: values=${valueRows.length} tokens=${tokenRows.length} components=${componentRows.length} libraries=${libraryRows.length}`, +); diff --git a/dogfood/ghost-ui/attempt-2/expression.md b/dogfood/ghost-ui/attempt-2/expression.md new file mode 100644 index 0000000..82049a6 --- /dev/null +++ b/dogfood/ghost-ui/attempt-2/expression.md @@ -0,0 +1,181 @@ +--- +id: ghost-ui +source: llm +timestamp: 2026-04-29T16:15:00Z +sources: + - github:block/ghost#packages/ghost-ui + +observation: + personality: [monochromatic, editorial, restrained, generous, rhythmic] + resembles: [vercel-geist, linear] + +decisions: + - dimension: color-strategy + - dimension: chart-strategy + - dimension: surface-hierarchy + - dimension: shape-language + - dimension: theming-architecture + - dimension: typography-voice + - dimension: font-sourcing + - dimension: density + - dimension: interactive-patterns + - dimension: elevation + - dimension: motion + +palette: + dominant: + - { role: ink, value: "#1a1a1a" } + neutrals: + steps: ["#ffffff", "#f5f5f5", "#f0f0f0", "#e8e8e8", "#e5e5e5", "#cccccc", "#999999", "#666666", "#333333", "#232323", "#1a1a1a", "#000000"] + count: 12 + semantic: + - { role: success, value: "#91cb80" } + - { role: success-light, value: "#a3d795" } + - { role: danger, value: "#f94b4b" } + - { role: danger-light, value: "#ff6b6b" } + - { role: info, value: "#5c98f9" } + - { role: info-light, value: "#7cacff" } + - { role: warning, value: "#fbcd44" } + - { role: warning-light, value: "#ffd966" } + - { role: chart-1, value: "#f6b44a" } + - { role: chart-2, value: "#7585ff" } + - { role: chart-3, value: "#d76a6a" } + - { role: chart-4, value: "#d185e0" } + - { role: chart-5, value: "#91cb80" } + saturationProfile: muted + contrast: high + +spacing: + scale: [1, 2, 3, 4, 6, 8, 10, 12, 14, 16, 20, 24, 28, 30, 40, 44, 60, 64, 75, 96, 100, 200] + baseUnit: 4 + regularity: 0.6 + +typography: + families: ["system-ui", "Geist Mono", "serif"] + sizeRamp: [10, 11, 12, 14, 16, 20, 28, 44, 64, 96] + weightDistribution: { "300": 1, "600": 4, "700": 2, "900": 1 } + lineHeightPattern: tight + +surfaces: + borderRadii: [10, 14, 16, 20, 24, 999] + shadowComplexity: layered + borderUsage: minimal +--- + +# Character + +`ghost-ui` is an editorial design language: a 12-step pure-monochromatic neutral scale carries surface, border, and text across the whole UI; chroma appears only when the UI signals state or surfaces data. Headings move on a magazine-scale fluid hierarchy and inputs/buttons land on full pills, so the visual rhythm comes from shape and shadow rather than color. The system ships no bundled fonts and no brand color — character is in the discipline, not the ornament. + +# Decisions + +### color-strategy + +Treat hue as opt-in communication, not ambient decoration. A 12-step pure-monochromatic neutral ladder (`#ffffff` through `#000000`) carries surface, border, and text across the entire UI; eight semantic colors light up only when the UI signals state (success, danger, info, warning, each in a default + light variant for theme cascade). There is no brand color, no accent hue. Distinction comes from shape and shadow, not chroma. + +**Evidence:** +- Monochromatic ladder: `#ffffff`, `#f5f5f5`, `#f0f0f0`, `#e8e8e8`, `#e5e5e5`, `#cccccc`, `#999999`, `#666666`, `#333333`, `#232323`, `#1a1a1a`, `#000000` (declared as `--color-gray-50` through `--color-gray-900` plus `--color-white`/`--color-black`) +- Semantic state colors with light/dark cascade: `#91cb80` / `#a3d795` (success), `#f94b4b` / `#ff6b6b` (danger), `#5c98f9` / `#7cacff` (info), `#fbcd44` / `#ffd966` (warning) +- The `@theme` block in `src/styles/main.css` opens with `--color-*: initial` — explicitly resetting Tailwind's default palette so nothing chromatic leaks in. +- `--background-accent: var(--color-gray-900)` — the "accent" maps to the extreme of the gray scale, not a brand hue. The accent slot itself is monochrome. + +### chart-strategy + +Data visualization gets a deliberately separate, warm-leaning 5-color palette (`#f6b44a` orange, `#7585ff` periwinkle, `#d76a6a` coral, `#d185e0` lilac, `#91cb80` green) that intentionally departs from the monochromatic discipline applied everywhere else. Charts need categorical distinction; the rest of the UI doesn't. Naming the chart palette as a separate sub-strategy keeps the discipline gate clear: chroma here is signal, not seepage. + +**Evidence:** +- `--chart-1: #f6b44a`, `--chart-2: #7585ff`, `--chart-3: #d76a6a`, `--chart-4: #d185e0`, `--chart-5: #91cb80` +- Same five chart values declared identically in `:root` and `.dark` — the chart palette is the one thing that doesn't theme-cascade. Cross-theme consistency is the goal for data. + +### surface-hierarchy + +Name surfaces by intent rather than by shade number. Backgrounds, borders, and text each get their own semantic vocabulary (`default`, `alt`, `medium`, `muted`, `inverse`, `accent`, plus `danger`/`success`/`info`/`warning` for state and `strong`/`card`/`input`/`input-hover` for borders). A theme preset can remap every value without touching component logic — the slot names are the contract. + +**Evidence:** +- 9 background slots: `--background-default`, `--background-alt`, `--background-medium`, `--background-muted`, `--background-inverse`, `--background-accent`, `--background-danger`, `--background-success`, `--background-info`, `--background-warning` +- 10 border slots: `--border-default`, `--border-input`, `--border-input-hover`, `--border-strong`, `--border-card`, `--border-inverse`, `--border-accent`, plus state borders +- 8 text slots mirroring the same vocabulary +- A separate `--sidebar-*` namespace (8 slots) lets sidebar UI carry its own surface vocabulary parallel to the main one — intentional surface-zone separation. + +### shape-language + +Apply a pill-first radius philosophy that sorts surfaces by interaction. Buttons, inputs, and toggles are fully pilled (`999px`) — interactive surfaces are circular at the ends. Cards stay rounded but distinct as soft squares (`14`/`20`/`24px`). Modals (`16px`) and dropdowns (`10px`) live in between. The shape choice itself is a rhythm: interactive vs. structural surfaces are read by their corner radius before any color cue. + +**Evidence:** +- `--radius-pill: 999px`, `--radius-button: 999px`, `--radius-input: 999px` +- `--radius-card: 20px`, `--radius-card-lg: 24px`, `--radius-card-sm: 14px` +- `--radius-modal: 16px`, `--radius-dropdown: 10px` +- `--radius: 20px` (the system base) with derived `--radius-sm/md/lg/xl` via `calc()` + +### theming-architecture + +Cascade three layers — raw stepped values → semantic aliases → shadcn aliases — so the entire visual language swaps at runtime through CSS custom property injection without touching component code. Consumers reach for whichever layer matches their intent: Tailwind utilities pull `--color-foreground`; component CSS pulls `--text-default`; raw needs reach `--color-gray-900`. Five bundled theme presets (default plus four overrides under `src/lib/theme-presets.ts`) validate that shape, color, and contrast can all be remapped. + +**Evidence:** +- chain `--color-foreground` → `--foreground` → `--text-default` → `--color-gray-900` +- chain `--background-default` → `--color-white` (light) / `--color-black` (dark) +- The `@theme inline` block in `main.css` re-exposes every semantic alias as a Tailwind color token (`--color-background-default: var(--background-default)`, etc.) +- `src/lib/theme-presets.ts` ships 5 named presets that each remap base + semantic layers without changing the alias contract. + +### typography-voice + +Use a magazine-scale type hierarchy where display headings and body sizes scale with viewport via `clamp()` rather than living on a fixed ramp. Four heading tiers (`display`, `section`, `sub`, `card`) each carry their own progressively-tightening line-height (0.88 → 1.1) and decreasing negative letter-spacing. Editorial helpers — `--label-*` (uppercase kicker), `--pullquote-*` (light contrast voice) — supply visual punctuation for longform content. + +**Evidence:** +- `--heading-display-font-size: clamp(64px, 8vw, 96px)`, line-height `0.88`, letter-spacing `-0.05em`, weight `900` +- `--heading-section-font-size: clamp(44px, 5vw, 64px)`, line-height `0.95`, weight `700` +- `--heading-sub-font-size: clamp(28px, 3vw, 40px)`, line-height `1`, weight `700` +- `--heading-card-font-size: clamp(20px, 2vw, 28px)`, line-height `1.1`, weight `600` +- `--display-size: clamp(3rem, 12vw, 12rem)` — hero-scale fluid display +- `--body-reading-size: clamp(1rem, 1.3vw, 1.25rem)`, line-height `1.65` — longform reading rhythm +- `--label-font-size: 11px`, letter-spacing `0.12em`, weight `600` — uppercase kicker type +- `--pullquote-weight: 300`, line-height `1.3` — light contrast voice for editorial punctuation + +### font-sourcing + +Ship no bundled typefaces. The system declares a `system-ui` sans stack, `Geist Mono` with a monospace fallback, and a generic `serif` — typography responsibility moves to the consumer. The visual language adapts to the host platform's default face, which keeps the library dependency-free and lets hosts impose their own brand font without overriding. + +**Evidence:** +- `src/styles/font-faces.css` is one comment: `/* Design language ships with no bundled fonts — consumers bring their own. */` — zero `@font-face` declarations. +- `--font-sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif` +- `--font-mono: "Geist Mono", monospace` +- `--font-serif: serif` +- `--font-display: system-ui, …` (intentionally same as sans — no separate display face) + +### density + +Maintain compact interactive controls inside generous structural whitespace. Buttons and inputs sit in the 32–40px height range (`h-8`/`h-9`/`h-10` Tailwind utilities backed by `--spacing-button: 2.75rem` and `--spacing-input: 3.25rem` tokens), while page sections use lavish padding (`--section-padding-vertical: 100px`, `--section-heading-margin-bottom: 75px`). The result is a publishing-oriented reading rhythm in the structural layer, not a dense tool-UI feel. + +**Evidence:** +- Component-height tokens declared explicitly: `--spacing-button: 2.75rem` (44px), `--spacing-button-sm: 2rem` (32px), `--spacing-input: 3.25rem` (52px), `--spacing-input-sm: 2.75rem` (44px) +- Page-rhythm tokens: `--page-container-max-width: 1440px`, `--page-container-side-gutter: 20px`, `--section-padding-vertical: 100px`, `--section-heading-margin-bottom: 75px` +- Button cva (in `src/components/ui/button.tsx`) declares fixed sizes `default: h-9`, `sm: h-8`, `lg: h-10`, plus three icon sizes — interactive surfaces have committed heights, not derived ones. + +### interactive-patterns + +Standardize focus states as a 2-ring with offset using the `--ring` token, applied uniformly across every component, plus a global `*:focus-visible` fallback for browser-default elements. Browser default outlines are replaced with a consistent, theme-aware focus indicator that works in both light and dark modes. + +**Evidence:** +- Global rule in `main.css`: `*:not(body):not(.focus-override) { outline: none !important; &:focus-visible { @apply focus-visible:ring-ring focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:outline-hidden; } }` +- `--ring: var(--border-strong)` in light, same alias resolves to white in dark — focus ring intensity follows theme contrast. +- Button cva declares `focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[1px]` — component-level reinforcement. +- Link helper class: `.link { @apply ... focus-visible:ring-ring focus-visible:ring-offset-background ... }` — same pattern applied to inline links. + +### elevation + +Name shadows by structural role rather than by intensity level, and intensify shadow alpha in dark mode rather than removing them. Seven named tiers (`mini`, `btn`, `card`, `elevated`, `popover`, `modal`, `kbd`) plus two special-purpose (`mini-inset`, `date-field-focus`) give designers a vocabulary tied to component context instead of a numeric scale. Depth cues stay legible on dark surfaces because alpha doubles, not because the shadow flips. + +**Evidence:** +- 7 named tiers: `--shadow-mini`, `--shadow-btn`, `--shadow-card`, `--shadow-elevated`, `--shadow-popover`, `--shadow-modal`, `--shadow-kbd` +- 2 special-purpose: `--shadow-mini-inset`, `--shadow-date-field-focus` +- Light: `--shadow-card: 0 2px 8px rgba(76, 76, 76, 0.15)` → Dark: `--shadow-card: 0 2px 8px rgba(0, 0, 0, 0.4)` — alpha intensifies (~3×), shadow stays. +- Light: `--shadow-modal: 0 20px 60px rgba(0, 0, 0, 0.2)` → Dark: `--shadow-modal: 0 20px 60px rgba(0, 0, 0, 0.6)` — same intensification pattern at the heaviest tier. + +### motion + +Keep transitions functional and brief — three duration tiers (`0.15s` fast, `0.2s` normal, `0.4s` slow) drive every interaction, and a single shared cubic-bezier easing (`cubic-bezier(0.33, 1, 0.68, 1)`) gives the system a consistent decelerating feel. Animations are limited to opacity, transform, and blur; there is no decorative motion, no kinetic ornament. + +**Evidence:** +- `--duration-fast: 0.15s`, `--duration-normal: 0.2s`, `--duration-slow: 0.4s` +- `--ease-spring: cubic-bezier(0.33, 1, 0.68, 1)` +- 12 named animation tokens (`--animate-*`): `accordion-down/up`, `caret-blink`, `enter-from-left/right`, `exit-to-left/right`, `fade-in/out`, `scale-in/out`, `word-reveal` +- `tw-animate-css` is the only motion library imported via `main.css` — `motion` is in `dependencies` but not pulled into the styles entry point. diff --git a/dogfood/ghost-ui/attempt-2/map.md b/dogfood/ghost-ui/attempt-2/map.md new file mode 100644 index 0000000..fd5f572 --- /dev/null +++ b/dogfood/ghost-ui/attempt-2/map.md @@ -0,0 +1,103 @@ +--- +schema: ghost.map/v1 +id: ghost-ui +repo: block/ghost +mapped_at: 2026-04-29 +platform: web +languages: + - { name: typescript, files: 116, share: 0.4696 } + - { name: json, files: 113, share: 0.4575 } + - { name: markdown, files: 10, share: 0.0405 } + - { name: css, files: 4, share: 0.0162 } + - { name: javascript, files: 4, share: 0.0162 } +build_system: [pnpm, vite] +package_manifests: + - package.json + - tsconfig.json + - tsconfig.lib.json + - tsconfig.mcp.json + - vite.lib.config.ts + - components.json +composition: + frameworks: + - { name: react, version: "19.1.0" } + - { name: vite, version: "^6.3.0" } + - { name: tailwindcss, version: "^4.2.2" } + - { name: radix-ui } + - { name: lucide-react, version: "^1.7.0" } + - { name: cmdk, version: "^1.1.1" } + - { name: sonner, version: "^2.0.7" } + - { name: class-variance-authority, version: "^0.7.1" } + rendering: react-spa + styling: + - tailwindcss-v4 + - css-custom-properties +registry: + path: ./registry.json + components: 106 +design_system: + paths: + - src/styles + - src/components/theme + - src/lib + entry_files: + - src/styles/main.css + - src/styles/font-faces.css + - src/lib/theme-presets.ts + - src/lib/theme-defaults.ts + - src/lib/theme-utils.ts + - src/lib/theme-provider.tsx + token_source: inline + status: active +ui_surface: + include: + - "src/components/ui/**" + - "src/components/ai-elements/**" + - "src/components/theme/**" + - "src/styles/**" + - "src/lib/theme-*" + exclude: + - "src/mcp/**" + - "scripts/**" + - "dist/**" + - "dist-lib/**" + - "dist-mcp/**" + - "public/r/**" + - "**/*.test.ts" + - "**/*.test.tsx" +feature_areas: + - name: ui-primitives + paths: ["src/components/ui"] + sub_areas: [input, layout, feedback, display, navigation, overlay] + - name: ai-elements + paths: ["src/components/ai-elements"] + sub_areas: [chat, agent-state, artifacts, audio] + - name: theme + paths: ["src/components/theme", "src/lib/theme-presets.ts", "src/lib/theme-defaults.ts", "src/lib/theme-utils.ts", "src/lib/theme-provider.tsx"] + - name: tokens + paths: ["src/styles"] + - name: hooks + paths: ["src/hooks"] + - name: registry-tooling + paths: ["scripts", "registry.json", "components.json"] + - name: mcp-server + paths: ["src/mcp"] +orientation_files: + - README.md + - registry.json + - src/styles/main.css + - src/lib/theme-presets.ts + - src/lib/theme-defaults.ts +--- + +## Identity + +`ghost-ui` is a private workspace package in the `block/ghost` monorepo. It ships a reference design system — 49 shadcn-style UI primitives plus 48 AI-element components plus a theme layer — distributed via a shadcn `registry.json` rather than npm. It also ships an MCP server (`ghost-mcp` bin) that re-exposes the registry to AI assistants. + +## Topology + +The design system lives across three folders. Tokens are inline CSS custom properties declared in `src/styles/main.css` (Tailwind v4 `@theme` blocks plus `:root` and `.dark` variable layers). Theme presets and defaults are TypeScript modules under `src/lib/theme-*.ts`, surfaced through a `theme-provider.tsx` React context. UI primitives live under `src/components/ui/`; AI-specific elements (chat surfaces, agent-state indicators, artifacts) live under `src/components/ai-elements/`. The `registry.json` at the package root indexes 106 distributable items consumed by `shadcn build`. + +## Conventions + +Tailwind v4 with custom theming via `@theme` blocks, CSS custom properties for runtime token resolution, and a class-variance-authority pattern for variant-heavy primitives. Radix UI underlies most interactive primitives. Components are flat (no nested theme variants) and ship as both source files and a baked `registry.json` plus per-item snapshots under `public/r/` (113 JSON files account for the JSON-heavy histogram). Build splits into a Vite library bundle and a separate TypeScript-built MCP server. diff --git a/dogfood/managerbot/bucket-gen.mjs b/dogfood/managerbot/bucket-gen.mjs new file mode 100644 index 0000000..0dd6f16 --- /dev/null +++ b/dogfood/managerbot/bucket-gen.mjs @@ -0,0 +1,398 @@ +#!/usr/bin/env node +import { readdirSync, readFileSync, statSync } from "node:fs"; +import { join } from "node:path"; + +const TARGET_ROOT = "/Users/nahiyan/Development/square-web/apps/managerbot"; +const UI_ROOT = `${TARGET_ROOT}/libs/managerbot-ui`; +const THEME_CSS = `${UI_ROOT}/src/styles/theme.css`; +const GLOBALS_CSS = `${UI_ROOT}/src/styles/globals.css`; +const COMPONENTS_DIR = `${UI_ROOT}/src/components`; + +const SOURCE = { + target: "squareup/square-web/apps/managerbot", + scanned_at: "2026-04-30T00:00:00Z", +}; + +const themeRaw = readFileSync(THEME_CSS, "utf8"); +const globalsRaw = readFileSync(GLOBALS_CSS, "utf8"); + +// Walk the file linearly, tracking current selector/at-rule context. +// We only need three contexts: :root, .dark, @theme inline. +function parseDecls(css) { + const lines = css.split("\n"); + const stack = []; + const decls = []; // { context, name, value, line } + for (let i = 0; i < lines.length; i++) { + const raw = lines[i]; + const trimmed = raw.trim(); + if (trimmed.endsWith("{")) { + const head = trimmed.replace(/\s*\{$/, "").trim(); + stack.push(head); + continue; + } + if (trimmed === "}") { + stack.pop(); + continue; + } + const m = raw.match(/^\s*(--[a-zA-Z0-9_-]+)\s*:\s*(.+?);?\s*$/); + if (m) { + const ctx = stack[stack.length - 1] || ""; + decls.push({ + context: ctx, + name: m[1], + value: m[2].replace(/;$/, "").trim(), + line: i + 1, + }); + } + } + return decls; +} + +const themeDecls = parseDecls(themeRaw); +// Filter out the @theme inline `--color-*` re-exports (mechanical Tailwind 4 +// alias layer for class atom generation; not part of the canonical token set). +function isMechanicalReExport(d) { + // @theme inline block holds reexports prefixed with --color-, plus radius / shadow / text / etc. + // We keep radius, shadow, text, font, animate, etc. — those are canonical. + // We drop --color- if its value is exactly var(--). + if (!d.context.startsWith("@theme")) return false; + if (!d.name.startsWith("--color-")) return false; + const stripped = d.name.replace(/^--color-/, ""); + return d.value === `var(--${stripped})`; +} + +const canonical = themeDecls.filter((d) => !isMechanicalReExport(d)); + +// Group by name → resolve aliases / by_theme. +// :root + @theme are the "default" theme; .dark and @utility .force-light add variants. +const byName = new Map(); +for (const d of canonical) { + const slot = byName.get(d.name) || { + name: d.name, + default: null, + variants: {}, + }; + if (d.context === ":root" || d.context.startsWith("@theme")) { + if (!slot.default) slot.default = d; + } else if (d.context === ".dark") { + slot.variants.dark = d; + } else if (d.context.includes("force-light")) { + slot.variants.forceLight = d; + } else { + // unknown — record under context name + slot.variants[d.context] = d; + } + byName.set(d.name, slot); +} + +// Resolve var() chains within :root scope to literal where possible. +function resolveLiteral(value, scope) { + // scope: Map — recursion-safe via depth limit + const seen = new Set(); + let cur = value; + for (let depth = 0; depth < 8; depth++) { + const m = cur.match(/^var\((--[a-zA-Z0-9_-]+)\)$/); + if (!m) return cur; + if (seen.has(m[1])) return cur; + seen.add(m[1]); + const next = scope.get(m[1]); + if (!next) return cur; + cur = next; + } + return cur; +} + +const rootScope = new Map(); +for (const [name, slot] of byName) { + if (slot.default) rootScope.set(name, slot.default.value); +} + +// kind classifier +function classify(name, value) { + if (name.startsWith("--shadow-")) return "shadow"; + if (name.startsWith("--radius-")) return "radius"; + if (name.startsWith("--text-")) return "typography"; + if (name === "--font-sans") return "typography"; + if ( + name.startsWith("--width-") || + name === "--page-x-padding" || + name.startsWith("--core-icon-") + ) + return "spacing"; + if (name === "--border-width-hairline") return "spacing"; + if (name.startsWith("--animate-")) return "motion"; + // colors: anything that resolves to oklch(...) or oklab(...) or rgba(...) or # or var-of-color + if (/oklch|oklab|rgba?|hsla?|^#[0-9a-fA-F]/i.test(value)) return "color"; + // semantic color names + if ( + /^--(background|foreground|card|popover|primary|secondary|muted|subtle|accent|destructive|danger|success|warning|trend|border|input|ring|sidebar|panel|elevation|chart|popover-border|color-mix)/.test( + name, + ) + ) + return "color"; + // ramp-named tokens default to color + if ( + /^--(black|white|neutral|red|yellow|green|blue|purple|cashapp|square|slate|gray|zinc|stone|rose|orange|amber|lime|emerald|teal|cyan|sky|indigo|violet|fuchsia|pink)/.test( + name, + ) + ) + return "color"; + return "unknown"; +} + +// Compute occurrences of each TOKEN NAME across the UI surface. +// We do this by reading every .tsx/.ts/.css file under src/. +function walk(dir, out) { + const entries = readdirSync(dir, { withFileTypes: true }); + for (const e of entries) { + if (e.name === "node_modules" || e.name === "dist") continue; + const full = join(dir, e.name); + if (e.isDirectory()) walk(full, out); + else if (/\.(tsx?|css|jsx?|mdx?)$/.test(e.name)) out.push(full); + } + return out; +} + +const allFiles = walk(`${UI_ROOT}/src`, []); +const fileTexts = new Map(); +for (const f of allFiles) { + try { + fileTexts.set(f, readFileSync(f, "utf8")); + } catch {} +} + +function countOccurrences(needle) { + let occ = 0; + let files = 0; + for (const [, txt] of fileTexts) { + let m = 0; + let idx = 0; + while ((idx = txt.indexOf(needle, idx)) !== -1) { + m++; + idx += needle.length; + } + if (m > 0) { + occ += m; + files++; + } + } + return { occ, files }; +} + +// Build tokens[] +const tokens = []; +for (const [name, slot] of byName) { + const def = slot.default; + if (!def) continue; + const kind = classify(name, def.value); + const aliasMatch = def.value.match(/^var\((--[a-zA-Z0-9_-]+)\)$/); + const alias_chain = aliasMatch ? [name, aliasMatch[1]] : []; + const resolved = resolveLiteral(def.value, rootScope); + + // Tailwind utility users reference tokens via class atoms (e.g. `bg-primary`, + // `text-neutral-500`). Counting `--token` matches CSS-var references only, + // which undercounts for the @theme inline tokens — but it's the most reliable + // cross-codebase signal we can produce without a full Tailwind pass. + const { occ, files } = countOccurrences(name); + + const row = { + id: "", + source: SOURCE, + name, + alias_chain, + resolved_value: resolved, + occurrences: occ, + files_count: files, + kind, + }; + + // by_theme — only for tokens with .dark variant + const variants = {}; + if (slot.variants.dark) variants.dark = slot.variants.dark.value; + if (slot.variants.forceLight) + variants.force_light = slot.variants.forceLight.value; + if (Object.keys(variants).length) { + row.by_theme = { default: def.value, ...variants }; + } + tokens.push(row); +} + +// Build values[] from distinct literal values observed in the design system. +// Each row should be a concrete literal that ships. +// Strategy: for each token whose resolved_value is a literal (oklch/rgba/px/rem/...), +// register one value row keyed by the literal, accumulating occurrences across tokens +// that share that literal. +const valueAcc = new Map(); // key: kind|literal → row + +function addValue(kind, literal, source_token, occ, files) { + const key = `${kind}|${literal}`; + if (!valueAcc.has(key)) { + valueAcc.set(key, { + id: "", + source: SOURCE, + kind, + value: literal, + raw: literal, + spec: deriveSpec(kind, literal), + occurrences: 0, + files_count: 0, + tokens: [], + }); + } + const row = valueAcc.get(key); + row.occurrences += occ; + row.files_count = Math.max(row.files_count, files); + row.tokens.push(source_token); +} + +function deriveSpec(kind, lit) { + if (kind === "color") { + if (lit.startsWith("oklch")) return { space: "oklch", source: lit }; + if (lit.startsWith("oklab")) return { space: "oklab", source: lit }; + if (lit.startsWith("rgba") || lit.startsWith("rgb")) + return { space: "srgb", source: lit }; + if (lit.startsWith("#")) return { space: "srgb", hex: lit }; + return { source: lit }; + } + if (kind === "radius" || kind === "spacing") { + const m = lit.match(/^([0-9.]+)(px|rem|em|%|vh|vw)$/); + if (m) return { unit: m[2], magnitude: Number(m[1]) }; + return { source: lit }; + } + if (kind === "typography") { + const m = lit.match(/^([0-9.]+)(px|rem|em)$/); + if (m) return { unit: m[2], magnitude: Number(m[1]) }; + return { source: lit }; + } + if (kind === "shadow") return { source: lit }; + return { source: lit }; +} + +for (const t of tokens) { + const lit = t.resolved_value; + if (lit.startsWith("var(")) continue; // unresolved alias — skip; the chain captures it + if (t.kind === "unknown") continue; + addValue(t.kind, lit, t.name, t.occurrences || 1, t.files_count || 1); +} + +// Add breakpoint values from globals.css and theme.css (basic regex on @media). +const bpHits = [ + ...themeRaw.matchAll(/@media \(min-width:\s*(\d+)px\)/g), + ...globalsRaw.matchAll(/@media \(min-width:\s*(\d+)px\)/g), +]; +const bpSet = new Set(); +for (const m of bpHits) bpSet.add(`${m[1]}px`); +for (const px of bpSet) { + const key = `breakpoint|${px}`; + valueAcc.set(key, { + id: "", + source: SOURCE, + kind: "breakpoint", + value: px, + raw: px, + spec: { unit: "px", magnitude: Number(px.replace("px", "")) }, + occurrences: 1, + files_count: 1, + }); +} + +const values = Array.from(valueAcc.values()); + +// Build components[] from filesystem. +// Recurse into design_system paths but exclude visx-charts and rjsf per ui_surface. +function pascal(name) { + return name + .replace(/\.(tsx|ts)$/, "") + .replace(/(^|[-_/])([a-z])/g, (_, __, c) => c.toUpperCase()); +} + +const compFiles = []; +function collectComponents(dir, prefix = "") { + const entries = readdirSync(dir, { withFileTypes: true }); + for (const e of entries) { + if ( + e.name === "visx-charts" || + e.name === "rjsf" || + e.name === "icon-map.tsx" || + e.name === "icons.tsx" || + e.name === "icons.stories.tsx" + ) + continue; + const full = join(dir, e.name); + if (e.isDirectory()) { + collectComponents(full, prefix ? `${prefix}/${e.name}` : e.name); + } else if ( + /\.tsx$/.test(e.name) && + !/\.stories\.tsx$/.test(e.name) && + !/\.spec\.tsx$/.test(e.name) + ) { + compFiles.push({ + file: full, + slug: e.name.replace(/\.tsx$/, ""), + group: prefix, + }); + } + } +} +collectComponents(COMPONENTS_DIR); + +// Detect slug collisions across groups so we can disambiguate component names +// (componentRowId hashes only source + name, so duplicate names → duplicate IDs). +const slugCount = compFiles.reduce( + (m, c) => ((m[c.slug] = (m[c.slug] || 0) + 1), m), + {}, +); + +const components = compFiles.map(({ file, slug, group }) => { + const txt = readFileSync(file, "utf8"); + // Detect named exports (PascalCase). + const exports = new Set(); + for (const m of txt.matchAll( + /export\s+(?:const|function|class)\s+([A-Z][A-Za-z0-9_]*)/g, + )) { + exports.add(m[1]); + } + const variants = []; + // Detect cva() variant keys + const cvaMatch = txt.match(/cva\([\s\S]*?variants:\s*\{([\s\S]*?)\}\s*[,}]/m); + if (cvaMatch) { + const variantBlock = cvaMatch[1]; + for (const v of variantBlock.matchAll(/^\s*(\w+):\s*\{([^}]*)\}/gm)) { + variants.push({ + prop: v[1], + options: [...v[2].matchAll(/['"]?([\w-]+)['"]?\s*:/g)].map((x) => x[1]), + }); + } + } + const sizes = (variants.find((v) => v.prop === "size") || {}).options || []; + const variantValues = + (variants.find((v) => v.prop === "variant") || {}).options || []; + const baseName = pascal(slug); + const qualifiedName = + slugCount[slug] > 1 && group ? `${group}/${baseName}` : baseName; + return { + id: "", + source: SOURCE, + name: qualifiedName, + discovered_via: "heuristic", + file: file.replace(`${TARGET_ROOT}/`, ""), + group: group || "primitive", + exports: [...exports], + variants: variantValues, + sizes, + occurrences: 1, + }; +}); + +const bucket = { + schema: "ghost.bucket/v1", + sources: [SOURCE], + values, + tokens, + components, +}; + +console.log(JSON.stringify(bucket, null, 2)); +process.stderr.write( + `tokens: ${tokens.length}\nvalues: ${values.length}\ncomponents: ${components.length}\n`, +); diff --git a/dogfood/managerbot/bucket.json b/dogfood/managerbot/bucket.json new file mode 100644 index 0000000..fafa25d --- /dev/null +++ b/dogfood/managerbot/bucket.json @@ -0,0 +1,14563 @@ +{ + "schema": "ghost.bucket/v1", + "sources": [ + { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + } + ], + "values": [ + { + "id": "9c119194ade6b8d8", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(0.00% 0.0000 0.00)", + "raw": "oklch(0.00% 0.0000 0.00)", + "spec": { + "space": "oklch", + "source": "oklch(0.00% 0.0000 0.00)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--black"] + }, + { + "id": "075a9f126ada5697", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(100.00% 0.0000 89.88)", + "raw": "oklch(100.00% 0.0000 89.88)", + "spec": { + "space": "oklch", + "source": "oklch(100.00% 0.0000 89.88)" + }, + "occurrences": 40, + "files_count": 2, + "tokens": [ + "--white", + "--background", + "--card", + "--popover", + "--elevation-1" + ] + }, + { + "id": "9c7aa2137addeb2c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(97.72% 0.0000 89.88)", + "raw": "oklch(97.72% 0.0000 89.88)", + "spec": { + "space": "oklch", + "source": "oklch(97.72% 0.0000 89.88)" + }, + "occurrences": 6, + "files_count": 1, + "tokens": ["--neutral-30"] + }, + { + "id": "a4459902aa484e64", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(95.42% 0.0000 89.88)", + "raw": "oklch(95.42% 0.0000 89.88)", + "spec": { + "space": "oklch", + "source": "oklch(95.42% 0.0000 89.88)" + }, + "occurrences": 45, + "files_count": 1, + "tokens": [ + "--neutral-50", + "--primary-foreground", + "--secondary", + "--muted", + "--sidebar-primary-foreground" + ] + }, + { + "id": "f54491051e177ba0", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(93.12% 0.0000 89.88)", + "raw": "oklch(93.12% 0.0000 89.88)", + "spec": { + "space": "oklch", + "source": "oklch(93.12% 0.0000 89.88)" + }, + "occurrences": 5, + "files_count": 1, + "tokens": ["--neutral-80"] + }, + { + "id": "34a60b49927e4774", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(91.57% 0.0000 89.88)", + "raw": "oklch(91.57% 0.0000 89.88)", + "spec": { + "space": "oklch", + "source": "oklch(91.57% 0.0000 89.88)" + }, + "occurrences": 25, + "files_count": 2, + "tokens": ["--neutral-100", "--accent", "--border", "--sidebar-border"] + }, + { + "id": "71a43ada6ed73ef8", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(86.89% 0.0000 89.88)", + "raw": "oklch(86.89% 0.0000 89.88)", + "spec": { + "space": "oklch", + "source": "oklch(86.89% 0.0000 89.88)" + }, + "occurrences": 8, + "files_count": 1, + "tokens": ["--neutral-150", "--input"] + }, + { + "id": "d1cea32b16646749", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(82.94% 0.0000 89.88)", + "raw": "oklch(82.94% 0.0000 89.88)", + "spec": { + "space": "oklch", + "source": "oklch(82.94% 0.0000 89.88)" + }, + "occurrences": 3, + "files_count": 1, + "tokens": ["--neutral-200"] + }, + { + "id": "53bb2e46cdf7b120", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(78.13% 0.0000 89.88)", + "raw": "oklch(78.13% 0.0000 89.88)", + "spec": { + "space": "oklch", + "source": "oklch(78.13% 0.0000 89.88)" + }, + "occurrences": 4, + "files_count": 1, + "tokens": ["--neutral-250"] + }, + { + "id": "55712b5bff198d3e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(74.07% 0.0000 89.88)", + "raw": "oklch(74.07% 0.0000 89.88)", + "spec": { + "space": "oklch", + "source": "oklch(74.07% 0.0000 89.88)" + }, + "occurrences": 8, + "files_count": 1, + "tokens": ["--neutral-300", "--trend-neutral"] + }, + { + "id": "3ece958af380c94f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(69.96% 0.0000 89.88)", + "raw": "oklch(69.96% 0.0000 89.88)", + "spec": { + "space": "oklch", + "source": "oklch(69.96% 0.0000 89.88)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--neutral-350"] + }, + { + "id": "0133421e4eddf7c7", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(64.94% 0.0000 89.88)", + "raw": "oklch(64.94% 0.0000 89.88)", + "spec": { + "space": "oklch", + "source": "oklch(64.94% 0.0000 89.88)" + }, + "occurrences": 14, + "files_count": 1, + "tokens": ["--neutral-400", "--ring", "--sidebar-ring"] + }, + { + "id": "a97662698900d3dc", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(60.68% 0.0000 89.88)", + "raw": "oklch(60.68% 0.0000 89.88)", + "spec": { + "space": "oklch", + "source": "oklch(60.68% 0.0000 89.88)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--neutral-450"] + }, + { + "id": "6b6ae8fd46063afd", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(56.34% 0.0000 89.88)", + "raw": "oklch(56.34% 0.0000 89.88)", + "spec": { + "space": "oklch", + "source": "oklch(56.34% 0.0000 89.88)" + }, + "occurrences": 12, + "files_count": 1, + "tokens": ["--neutral-500", "--neutral-base", "--muted-foreground"] + }, + { + "id": "45711828e2289e24", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(51.92% 0.0000 89.88)", + "raw": "oklch(51.92% 0.0000 89.88)", + "spec": { + "space": "oklch", + "source": "oklch(51.92% 0.0000 89.88)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--neutral-550"] + }, + { + "id": "a95ce1c2a9a83f71", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(47.41% 0.0000 89.88)", + "raw": "oklch(47.41% 0.0000 89.88)", + "spec": { + "space": "oklch", + "source": "oklch(47.41% 0.0000 89.88)" + }, + "occurrences": 9, + "files_count": 1, + "tokens": ["--neutral-600", "--subtle-foreground"] + }, + { + "id": "43506f80fffaaa62", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(43.71% 0.0000 89.88)", + "raw": "oklch(43.71% 0.0000 89.88)", + "spec": { + "space": "oklch", + "source": "oklch(43.71% 0.0000 89.88)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--neutral-650"] + }, + { + "id": "211b2ebe966102bd", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(38.99% 0.0000 89.88)", + "raw": "oklch(38.99% 0.0000 89.88)", + "spec": { + "space": "oklch", + "source": "oklch(38.99% 0.0000 89.88)" + }, + "occurrences": 7, + "files_count": 1, + "tokens": ["--neutral-700", "--sidebar-foreground"] + }, + { + "id": "108b2d002f93e4de", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(35.10% 0.0000 89.88)", + "raw": "oklch(35.10% 0.0000 89.88)", + "spec": { + "space": "oklch", + "source": "oklch(35.10% 0.0000 89.88)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--neutral-750"] + }, + { + "id": "f7ebc419683eed58", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(30.08% 0.0000 89.88)", + "raw": "oklch(30.08% 0.0000 89.88)", + "spec": { + "space": "oklch", + "source": "oklch(30.08% 0.0000 89.88)" + }, + "occurrences": 3, + "files_count": 1, + "tokens": ["--neutral-800"] + }, + { + "id": "78f6bc0939b124c9", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(26.97% 0.0000 89.88)", + "raw": "oklch(26.97% 0.0000 89.88)", + "spec": { + "space": "oklch", + "source": "oklch(26.97% 0.0000 89.88)" + }, + "occurrences": 3, + "files_count": 1, + "tokens": ["--neutral-850"] + }, + { + "id": "fe22dfa5e9efb986", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(21.56% 0.0000 89.88)", + "raw": "oklch(21.56% 0.0000 89.88)", + "spec": { + "space": "oklch", + "source": "oklch(21.56% 0.0000 89.88)" + }, + "occurrences": 38, + "files_count": 2, + "tokens": [ + "--neutral-900", + "--primary", + "--secondary-foreground", + "--accent-foreground", + "--sidebar-primary" + ] + }, + { + "id": "5a79487be320dd95", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(16.98% 0.0000 89.88)", + "raw": "oklch(16.98% 0.0000 89.88)", + "spec": { + "space": "oklch", + "source": "oklch(16.98% 0.0000 89.88)" + }, + "occurrences": 33, + "files_count": 2, + "tokens": [ + "--neutral-950", + "--foreground", + "--card-foreground", + "--popover-foreground", + "--sidebar-accent-foreground", + "--panel-foreground" + ] + }, + { + "id": "68aded95e92f650b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(95.78% 0.0180 17.47)", + "raw": "oklch(95.78% 0.0180 17.47)", + "spec": { + "space": "oklch", + "source": "oklch(95.78% 0.0180 17.47)" + }, + "occurrences": 18, + "files_count": 1, + "tokens": ["--red-50", "--destructive-foreground", "--danger-foreground"] + }, + { + "id": "4aba5d3c6e86c5b4", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(91.50% 0.0387 17.88)", + "raw": "oklch(91.50% 0.0387 17.88)", + "spec": { + "space": "oklch", + "source": "oklch(91.50% 0.0387 17.88)" + }, + "occurrences": 3, + "files_count": 1, + "tokens": ["--red-100"] + }, + { + "id": "9e4a5c5420f01343", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(87.39% 0.0588 18.34)", + "raw": "oklch(87.39% 0.0588 18.34)", + "spec": { + "space": "oklch", + "source": "oklch(87.39% 0.0588 18.34)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--red-150"] + }, + { + "id": "6bb9d8994617bf1f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(83.31% 0.0812 18.93)", + "raw": "oklch(83.31% 0.0812 18.93)", + "spec": { + "space": "oklch", + "source": "oklch(83.31% 0.0812 18.93)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--red-200"] + }, + { + "id": "4af8754242ad6c3c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(79.44% 0.1024 19.59)", + "raw": "oklch(79.44% 0.1024 19.59)", + "spec": { + "space": "oklch", + "source": "oklch(79.44% 0.1024 19.59)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--red-250"] + }, + { + "id": "1ec63621a6b1b55c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(75.70% 0.1250 20.41)", + "raw": "oklch(75.70% 0.1250 20.41)", + "spec": { + "space": "oklch", + "source": "oklch(75.70% 0.1250 20.41)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--red-300"] + }, + { + "id": "b4b520fe9558ac2b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(71.34% 0.1532 21.68)", + "raw": "oklch(71.34% 0.1532 21.68)", + "spec": { + "space": "oklch", + "source": "oklch(71.34% 0.1532 21.68)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--red-350"] + }, + { + "id": "6b7d1eb7de4836c3", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(67.45% 0.1801 23.23)", + "raw": "oklch(67.45% 0.1801 23.23)", + "spec": { + "space": "oklch", + "source": "oklch(67.45% 0.1801 23.23)" + }, + "occurrences": 3, + "files_count": 1, + "tokens": ["--red-400"] + }, + { + "id": "f1f9062f893d3518", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(63.56% 0.2082 25.38)", + "raw": "oklch(63.56% 0.2082 25.38)", + "spec": { + "space": "oklch", + "source": "oklch(63.56% 0.2082 25.38)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--red-450"] + }, + { + "id": "f34a02438c06470c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(59.49% 0.2366 28.55)", + "raw": "oklch(59.49% 0.2366 28.55)", + "spec": { + "space": "oklch", + "source": "oklch(59.49% 0.2366 28.55)" + }, + "occurrences": 5, + "files_count": 1, + "tokens": ["--red-500", "--red-base"] + }, + { + "id": "37c39b8b0267ae16", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(55.00% 0.2181 28.49)", + "raw": "oklch(55.00% 0.2181 28.49)", + "spec": { + "space": "oklch", + "source": "oklch(55.00% 0.2181 28.49)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--red-550"] + }, + { + "id": "258826fe210237d4", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(50.42% 0.1991 28.41)", + "raw": "oklch(50.42% 0.1991 28.41)", + "spec": { + "space": "oklch", + "source": "oklch(50.42% 0.1991 28.41)" + }, + "occurrences": 22, + "files_count": 1, + "tokens": ["--red-600", "--destructive", "--danger"] + }, + { + "id": "d31b4759a74f470d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(45.74% 0.1797 28.31)", + "raw": "oklch(45.74% 0.1797 28.31)", + "spec": { + "space": "oklch", + "source": "oklch(45.74% 0.1797 28.31)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--red-650"] + }, + { + "id": "2ad78e947e49969a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(40.94% 0.1597 28.16)", + "raw": "oklch(40.94% 0.1597 28.16)", + "spec": { + "space": "oklch", + "source": "oklch(40.94% 0.1597 28.16)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--red-700"] + }, + { + "id": "fd43147f5fbb3f95", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(37.10% 0.1441 28.08)", + "raw": "oklch(37.10% 0.1441 28.08)", + "spec": { + "space": "oklch", + "source": "oklch(37.10% 0.1441 28.08)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--red-750"] + }, + { + "id": "697b99e93f1ac22c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(31.87% 0.1207 27.60)", + "raw": "oklch(31.87% 0.1207 27.60)", + "spec": { + "space": "oklch", + "source": "oklch(31.87% 0.1207 27.60)" + }, + "occurrences": 3, + "files_count": 1, + "tokens": ["--red-800"] + }, + { + "id": "822b539d9ecce96c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(27.75% 0.1037 27.37)", + "raw": "oklch(27.75% 0.1037 27.37)", + "spec": { + "space": "oklch", + "source": "oklch(27.75% 0.1037 27.37)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--red-850"] + }, + { + "id": "0fd00ab198d28eef", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(23.39% 0.0850 26.89)", + "raw": "oklch(23.39% 0.0850 26.89)", + "spec": { + "space": "oklch", + "source": "oklch(23.39% 0.0850 26.89)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--red-900"] + }, + { + "id": "5cec28c2938699dd", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(17.60% 0.0618 26.34)", + "raw": "oklch(17.60% 0.0618 26.34)", + "spec": { + "space": "oklch", + "source": "oklch(17.60% 0.0618 26.34)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--red-950"] + }, + { + "id": "8521ec3df0607e58", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(95.36% 0.0582 93.11)", + "raw": "oklch(95.36% 0.0582 93.11)", + "spec": { + "space": "oklch", + "source": "oklch(95.36% 0.0582 93.11)" + }, + "occurrences": 4, + "files_count": 1, + "tokens": ["--yellow-50"] + }, + { + "id": "5fa8461908a6d526", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(91.04% 0.1056 91.66)", + "raw": "oklch(91.04% 0.1056 91.66)", + "spec": { + "space": "oklch", + "source": "oklch(91.04% 0.1056 91.66)" + }, + "occurrences": 3, + "files_count": 1, + "tokens": ["--yellow-100"] + }, + { + "id": "cdb18f192269d593", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(86.57% 0.1493 90.18)", + "raw": "oklch(86.57% 0.1493 90.18)", + "spec": { + "space": "oklch", + "source": "oklch(86.57% 0.1493 90.18)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--yellow-150"] + }, + { + "id": "61eb8146522f97cd", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(82.83% 0.1680 85.60)", + "raw": "oklch(82.83% 0.1680 85.60)", + "spec": { + "space": "oklch", + "source": "oklch(82.83% 0.1680 85.60)" + }, + "occurrences": 12, + "files_count": 1, + "tokens": ["--yellow-200", "--warning"] + }, + { + "id": "d32e977f40631e7b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(78.58% 0.1598 85.31)", + "raw": "oklch(78.58% 0.1598 85.31)", + "spec": { + "space": "oklch", + "source": "oklch(78.58% 0.1598 85.31)" + }, + "occurrences": 5, + "files_count": 1, + "tokens": ["--yellow-250", "--yellow-base"] + }, + { + "id": "815d6667cf64f74d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(73.77% 0.1501 85.30)", + "raw": "oklch(73.77% 0.1501 85.30)", + "spec": { + "space": "oklch", + "source": "oklch(73.77% 0.1501 85.30)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--yellow-300"] + }, + { + "id": "a6aefbf376102dfe", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(69.72% 0.1415 85.57)", + "raw": "oklch(69.72% 0.1415 85.57)", + "spec": { + "space": "oklch", + "source": "oklch(69.72% 0.1415 85.57)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--yellow-350"] + }, + { + "id": "32c2c422bb1586c2", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(65.31% 0.1324 87.61)", + "raw": "oklch(65.31% 0.1324 87.61)", + "spec": { + "space": "oklch", + "source": "oklch(65.31% 0.1324 87.61)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--yellow-400"] + }, + { + "id": "86e34929c6ff678d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(60.56% 0.1226 85.92)", + "raw": "oklch(60.56% 0.1226 85.92)", + "spec": { + "space": "oklch", + "source": "oklch(60.56% 0.1226 85.92)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--yellow-450"] + }, + { + "id": "4945a64d22bf7ba8", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(56.53% 0.1142 86.12)", + "raw": "oklch(56.53% 0.1142 86.12)", + "spec": { + "space": "oklch", + "source": "oklch(56.53% 0.1142 86.12)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--yellow-500"] + }, + { + "id": "0705c985f8a854e8", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(52.43% 0.1058 86.35)", + "raw": "oklch(52.43% 0.1058 86.35)", + "spec": { + "space": "oklch", + "source": "oklch(52.43% 0.1058 86.35)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--yellow-550"] + }, + { + "id": "d6238900cc46fa32", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(48.67% 0.0979 88.54)", + "raw": "oklch(48.67% 0.0979 88.54)", + "spec": { + "space": "oklch", + "source": "oklch(48.67% 0.0979 88.54)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--yellow-600"] + }, + { + "id": "c6778635168260db", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(44.10% 0.0888 86.90)", + "raw": "oklch(44.10% 0.0888 86.90)", + "spec": { + "space": "oklch", + "source": "oklch(44.10% 0.0888 86.90)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--yellow-650"] + }, + { + "id": "a8536a51d45279bd", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(39.59% 0.0792 87.47)", + "raw": "oklch(39.59% 0.0792 87.47)", + "spec": { + "space": "oklch", + "source": "oklch(39.59% 0.0792 87.47)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--yellow-700"] + }, + { + "id": "2e30dac1dd755db4", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(34.99% 0.0694 88.16)", + "raw": "oklch(34.99% 0.0694 88.16)", + "spec": { + "space": "oklch", + "source": "oklch(34.99% 0.0694 88.16)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--yellow-750"] + }, + { + "id": "40b2be55c9c420ec", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(30.75% 0.0612 90.61)", + "raw": "oklch(30.75% 0.0612 90.61)", + "spec": { + "space": "oklch", + "source": "oklch(30.75% 0.0612 90.61)" + }, + "occurrences": 3, + "files_count": 1, + "tokens": ["--yellow-800"] + }, + { + "id": "d8526114b2f7f184", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(27.16% 0.0531 89.80)", + "raw": "oklch(27.16% 0.0531 89.80)", + "spec": { + "space": "oklch", + "source": "oklch(27.16% 0.0531 89.80)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--yellow-850"] + }, + { + "id": "2dc98f064f698dde", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(22.28% 0.0437 91.63)", + "raw": "oklch(22.28% 0.0437 91.63)", + "spec": { + "space": "oklch", + "source": "oklch(22.28% 0.0437 91.63)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--yellow-900"] + }, + { + "id": "32d7ba0f2ef816bb", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(16.77% 0.0313 93.87)", + "raw": "oklch(16.77% 0.0313 93.87)", + "spec": { + "space": "oklch", + "source": "oklch(16.77% 0.0313 93.87)" + }, + "occurrences": 8, + "files_count": 1, + "tokens": ["--yellow-950", "--warning-foreground"] + }, + { + "id": "c464096bc0ff7c72", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(94.82% 0.0422 169.29)", + "raw": "oklch(94.82% 0.0422 169.29)", + "spec": { + "space": "oklch", + "source": "oklch(94.82% 0.0422 169.29)" + }, + "occurrences": 11, + "files_count": 1, + "tokens": ["--green-50", "--success-foreground"] + }, + { + "id": "3225174b029111ec", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(89.95% 0.0807 168.66)", + "raw": "oklch(89.95% 0.0807 168.66)", + "spec": { + "space": "oklch", + "source": "oklch(89.95% 0.0807 168.66)" + }, + "occurrences": 3, + "files_count": 1, + "tokens": ["--green-100"] + }, + { + "id": "782424f435ebd2fe", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(85.35% 0.1197 167.35)", + "raw": "oklch(85.35% 0.1197 167.35)", + "spec": { + "space": "oklch", + "source": "oklch(85.35% 0.1197 167.35)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--green-150"] + }, + { + "id": "65a2f2e0d999ff5d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(80.58% 0.1574 162.70)", + "raw": "oklch(80.58% 0.1574 162.70)", + "spec": { + "space": "oklch", + "source": "oklch(80.58% 0.1574 162.70)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--green-200"] + }, + { + "id": "941053d2ab995f70", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(76.38% 0.1641 160.63)", + "raw": "oklch(76.38% 0.1641 160.63)", + "spec": { + "space": "oklch", + "source": "oklch(76.38% 0.1641 160.63)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--green-250"] + }, + { + "id": "ba07f98bcf7f101a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(72.79% 0.1560 160.70)", + "raw": "oklch(72.79% 0.1560 160.70)", + "spec": { + "space": "oklch", + "source": "oklch(72.79% 0.1560 160.70)" + }, + "occurrences": 11, + "files_count": 2, + "tokens": ["--green-300", "--green-base", "--chart-11"] + }, + { + "id": "511c4d1f4da93963", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(67.94% 0.1450 160.80)", + "raw": "oklch(67.94% 0.1450 160.80)", + "spec": { + "space": "oklch", + "source": "oklch(67.94% 0.1450 160.80)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--green-350"] + }, + { + "id": "7c9bc39ea568e50d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(64.24% 0.1366 160.89)", + "raw": "oklch(64.24% 0.1366 160.89)", + "spec": { + "space": "oklch", + "source": "oklch(64.24% 0.1366 160.89)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--green-400"] + }, + { + "id": "7880190aaf54af2b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(59.23% 0.1253 161.04)", + "raw": "oklch(59.23% 0.1253 161.04)", + "spec": { + "space": "oklch", + "source": "oklch(59.23% 0.1253 161.04)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--green-450"] + }, + { + "id": "e6463554b3144be6", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(55.40% 0.1166 161.17)", + "raw": "oklch(55.40% 0.1166 161.17)", + "spec": { + "space": "oklch", + "source": "oklch(55.40% 0.1166 161.17)" + }, + "occurrences": 3, + "files_count": 1, + "tokens": ["--green-500"] + }, + { + "id": "47231a2a6545ac5b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(50.19% 0.1047 161.37)", + "raw": "oklch(50.19% 0.1047 161.37)", + "spec": { + "space": "oklch", + "source": "oklch(50.19% 0.1047 161.37)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--green-550"] + }, + { + "id": "b0759b362f2cf729", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(47.54% 0.0986 161.50)", + "raw": "oklch(47.54% 0.0986 161.50)", + "spec": { + "space": "oklch", + "source": "oklch(47.54% 0.0986 161.50)" + }, + "occurrences": 17, + "files_count": 2, + "tokens": ["--green-600", "--success", "--chart-12"] + }, + { + "id": "13d95988089e5483", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(42.12% 0.0861 161.81)", + "raw": "oklch(42.12% 0.0861 161.81)", + "spec": { + "space": "oklch", + "source": "oklch(42.12% 0.0861 161.81)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--green-650"] + }, + { + "id": "79973e8508456219", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(38.09% 0.0761 163.02)", + "raw": "oklch(38.09% 0.0761 163.02)", + "spec": { + "space": "oklch", + "source": "oklch(38.09% 0.0761 163.02)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--green-700"] + }, + { + "id": "9f769138e4b537f0", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(33.77% 0.0661 163.42)", + "raw": "oklch(33.77% 0.0661 163.42)", + "spec": { + "space": "oklch", + "source": "oklch(33.77% 0.0661 163.42)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--green-750"] + }, + { + "id": "2676efd1bb32b54e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(30.79% 0.0600 162.74)", + "raw": "oklch(30.79% 0.0600 162.74)", + "spec": { + "space": "oklch", + "source": "oklch(30.79% 0.0600 162.74)" + }, + "occurrences": 3, + "files_count": 1, + "tokens": ["--green-800"] + }, + { + "id": "47d0776548d3823a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(26.22% 0.0492 163.52)", + "raw": "oklch(26.22% 0.0492 163.52)", + "spec": { + "space": "oklch", + "source": "oklch(26.22% 0.0492 163.52)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--green-850"] + }, + { + "id": "01d34fe8192ffe55", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(21.43% 0.0378 164.98)", + "raw": "oklch(21.43% 0.0378 164.98)", + "spec": { + "space": "oklch", + "source": "oklch(21.43% 0.0378 164.98)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--green-900"] + }, + { + "id": "afba960d28a3d1a3", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(16.38% 0.0261 169.50)", + "raw": "oklch(16.38% 0.0261 169.50)", + "spec": { + "space": "oklch", + "source": "oklch(16.38% 0.0261 169.50)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--green-950"] + }, + { + "id": "fc5ad6e00684cbff", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(96.09% 0.0180 286.06)", + "raw": "oklch(96.09% 0.0180 286.06)", + "spec": { + "space": "oklch", + "source": "oklch(96.09% 0.0180 286.06)" + }, + "occurrences": 4, + "files_count": 1, + "tokens": ["--blue-50"] + }, + { + "id": "b4a8dd91bd3e5737", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(91.95% 0.0390 285.61)", + "raw": "oklch(91.95% 0.0390 285.61)", + "spec": { + "space": "oklch", + "source": "oklch(91.95% 0.0390 285.61)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--blue-100"] + }, + { + "id": "d39245b1b2f63696", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(86.69% 0.0647 284.97)", + "raw": "oklch(86.69% 0.0647 284.97)", + "spec": { + "space": "oklch", + "source": "oklch(86.69% 0.0647 284.97)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--blue-150"] + }, + { + "id": "a0af092baab12706", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(82.77% 0.0843 284.41)", + "raw": "oklch(82.77% 0.0843 284.41)", + "spec": { + "space": "oklch", + "source": "oklch(82.77% 0.0843 284.41)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--blue-200"] + }, + { + "id": "a853a76db002e061", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(78.90% 0.1038 283.77)", + "raw": "oklch(78.90% 0.1038 283.77)", + "spec": { + "space": "oklch", + "source": "oklch(78.90% 0.1038 283.77)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--blue-250"] + }, + { + "id": "798bda46279f2204", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(74.76% 0.1273 282.88)", + "raw": "oklch(74.76% 0.1273 282.88)", + "spec": { + "space": "oklch", + "source": "oklch(74.76% 0.1273 282.88)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--blue-300"] + }, + { + "id": "0fcbffa978976846", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(70.96% 0.1471 281.99)", + "raw": "oklch(70.96% 0.1471 281.99)", + "spec": { + "space": "oklch", + "source": "oklch(70.96% 0.1471 281.99)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--blue-350"] + }, + { + "id": "293eed4b7c18c082", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(67.12% 0.1682 280.87)", + "raw": "oklch(67.12% 0.1682 280.87)", + "spec": { + "space": "oklch", + "source": "oklch(67.12% 0.1682 280.87)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--blue-400"] + }, + { + "id": "fc09dc1ef716e23c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(62.01% 0.1980 278.94)", + "raw": "oklch(62.01% 0.1980 278.94)", + "spec": { + "space": "oklch", + "source": "oklch(62.01% 0.1980 278.94)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--blue-450"] + }, + { + "id": "f43de5b07212d72e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(58.57% 0.2160 277.40)", + "raw": "oklch(58.57% 0.2160 277.40)", + "spec": { + "space": "oklch", + "source": "oklch(58.57% 0.2160 277.40)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--blue-500"] + }, + { + "id": "3e601d6895a0882b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(54.14% 0.2411 274.78)", + "raw": "oklch(54.14% 0.2411 274.78)", + "spec": { + "space": "oklch", + "source": "oklch(54.14% 0.2411 274.78)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--blue-550"] + }, + { + "id": "42b06fe80b47bb8c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(50.25% 0.2633 271.74)", + "raw": "oklch(50.25% 0.2633 271.74)", + "spec": { + "space": "oklch", + "source": "oklch(50.25% 0.2633 271.74)" + }, + "occurrences": 5, + "files_count": 1, + "tokens": ["--blue-600", "--blue-base"] + }, + { + "id": "8ee405402e64dd52", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(46.47% 0.2854 267.82)", + "raw": "oklch(46.47% 0.2854 267.82)", + "spec": { + "space": "oklch", + "source": "oklch(46.47% 0.2854 267.82)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--blue-650"] + }, + { + "id": "6311780e4ce35226", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(42.24% 0.2846 265.00)", + "raw": "oklch(42.24% 0.2846 265.00)", + "spec": { + "space": "oklch", + "source": "oklch(42.24% 0.2846 265.00)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--blue-700"] + }, + { + "id": "596fb03bbd2382a0", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(37.54% 0.2514 265.20)", + "raw": "oklch(37.54% 0.2514 265.20)", + "spec": { + "space": "oklch", + "source": "oklch(37.54% 0.2514 265.20)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--blue-750"] + }, + { + "id": "81e4fee3c878aeb7", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(33.39% 0.2220 265.44)", + "raw": "oklch(33.39% 0.2220 265.44)", + "spec": { + "space": "oklch", + "source": "oklch(33.39% 0.2220 265.44)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--blue-800"] + }, + { + "id": "c81d693a6be95d20", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(28.39% 0.1864 265.84)", + "raw": "oklch(28.39% 0.1864 265.84)", + "spec": { + "space": "oklch", + "source": "oklch(28.39% 0.1864 265.84)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--blue-850"] + }, + { + "id": "a985db7f1f898136", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(23.93% 0.1544 266.37)", + "raw": "oklch(23.93% 0.1544 266.37)", + "spec": { + "space": "oklch", + "source": "oklch(23.93% 0.1544 266.37)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--blue-900"] + }, + { + "id": "84a03878d6add137", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(18.40% 0.1160 267.07)", + "raw": "oklch(18.40% 0.1160 267.07)", + "spec": { + "space": "oklch", + "source": "oklch(18.40% 0.1160 267.07)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--blue-950"] + }, + { + "id": "b03ed0552b7bd175", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(95.51% 0.0262 309.51)", + "raw": "oklch(95.51% 0.0262 309.51)", + "spec": { + "space": "oklch", + "source": "oklch(95.51% 0.0262 309.51)" + }, + "occurrences": 4, + "files_count": 1, + "tokens": ["--purple-50"] + }, + { + "id": "5a1a0b29dd3e6b2e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(92.24% 0.0466 310.65)", + "raw": "oklch(92.24% 0.0466 310.65)", + "spec": { + "space": "oklch", + "source": "oklch(92.24% 0.0466 310.65)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--purple-100"] + }, + { + "id": "3dfacf80d7392b62", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(87.78% 0.0736 309.66)", + "raw": "oklch(87.78% 0.0736 309.66)", + "spec": { + "space": "oklch", + "source": "oklch(87.78% 0.0736 309.66)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--purple-150"] + }, + { + "id": "2a7f92563e29a1ef", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(83.40% 0.1012 309.30)", + "raw": "oklch(83.40% 0.1012 309.30)", + "spec": { + "space": "oklch", + "source": "oklch(83.40% 0.1012 309.30)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--purple-200"] + }, + { + "id": "84d6d5d67740faa5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(80.06% 0.1246 308.96)", + "raw": "oklch(80.06% 0.1246 308.96)", + "spec": { + "space": "oklch", + "source": "oklch(80.06% 0.1246 308.96)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--purple-250"] + }, + { + "id": "bdaeb3a246bedd6e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(75.82% 0.1525 308.47)", + "raw": "oklch(75.82% 0.1525 308.47)", + "spec": { + "space": "oklch", + "source": "oklch(75.82% 0.1525 308.47)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--purple-300"] + }, + { + "id": "57fef20aaf7d2b3b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(71.72% 0.1797 307.88)", + "raw": "oklch(71.72% 0.1797 307.88)", + "spec": { + "space": "oklch", + "source": "oklch(71.72% 0.1797 307.88)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--purple-350"] + }, + { + "id": "59013189186abff1", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(67.81% 0.2055 307.15)", + "raw": "oklch(67.81% 0.2055 307.15)", + "spec": { + "space": "oklch", + "source": "oklch(67.81% 0.2055 307.15)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--purple-400"] + }, + { + "id": "53408297e26eb9f6", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(63.28% 0.2344 305.98)", + "raw": "oklch(63.28% 0.2344 305.98)", + "spec": { + "space": "oklch", + "source": "oklch(63.28% 0.2344 305.98)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--purple-450"] + }, + { + "id": "619fae9a9e0e4b70", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(60.06% 0.2517 304.85)", + "raw": "oklch(60.06% 0.2517 304.85)", + "spec": { + "space": "oklch", + "source": "oklch(60.06% 0.2517 304.85)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--purple-500"] + }, + { + "id": "4c114409e68e2e57", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(55.95% 0.2723 302.42)", + "raw": "oklch(55.95% 0.2723 302.42)", + "spec": { + "space": "oklch", + "source": "oklch(55.95% 0.2723 302.42)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--purple-550"] + }, + { + "id": "75a854bdbed54b70", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(51.81% 0.2718 299.74)", + "raw": "oklch(51.81% 0.2718 299.74)", + "spec": { + "space": "oklch", + "source": "oklch(51.81% 0.2718 299.74)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--purple-600"] + }, + { + "id": "45ba828b8ecabaf0", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(47.08% 0.2467 299.80)", + "raw": "oklch(47.08% 0.2467 299.80)", + "spec": { + "space": "oklch", + "source": "oklch(47.08% 0.2467 299.80)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--purple-650"] + }, + { + "id": "6125eae9a025bd99", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(42.06% 0.2184 300.28)", + "raw": "oklch(42.06% 0.2184 300.28)", + "spec": { + "space": "oklch", + "source": "oklch(42.06% 0.2184 300.28)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--purple-700"] + }, + { + "id": "46c87933de8bf377", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(37.89% 0.1962 300.45)", + "raw": "oklch(37.89% 0.1962 300.45)", + "spec": { + "space": "oklch", + "source": "oklch(37.89% 0.1962 300.45)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--purple-750"] + }, + { + "id": "cf135070de6cc98d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(32.65% 0.1672 301.01)", + "raw": "oklch(32.65% 0.1672 301.01)", + "spec": { + "space": "oklch", + "source": "oklch(32.65% 0.1672 301.01)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--purple-800"] + }, + { + "id": "53a98545a860bcd4", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(28.09% 0.1411 301.82)", + "raw": "oklch(28.09% 0.1411 301.82)", + "spec": { + "space": "oklch", + "source": "oklch(28.09% 0.1411 301.82)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--purple-850"] + }, + { + "id": "2a96a4f94875afcb", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(23.39% 0.1148 302.86)", + "raw": "oklch(23.39% 0.1148 302.86)", + "spec": { + "space": "oklch", + "source": "oklch(23.39% 0.1148 302.86)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--purple-900"] + }, + { + "id": "ce15c19a17c2ee06", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(18.39% 0.0887 304.57)", + "raw": "oklch(18.39% 0.0887 304.57)", + "spec": { + "space": "oklch", + "source": "oklch(18.39% 0.0887 304.57)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--purple-950"] + }, + { + "id": "c5800e422ee4cf18", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(65.08% 0.2214 306.55)", + "raw": "oklch(65.08% 0.2214 306.55)", + "spec": { + "space": "oklch", + "source": "oklch(65.08% 0.2214 306.55)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--purple-base"] + }, + { + "id": "bbcb262d21c973f5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(97.5% 0.065 145.0)", + "raw": "oklch(97.5% 0.065 145.0)", + "spec": { + "space": "oklch", + "source": "oklch(97.5% 0.065 145.0)" + }, + "occurrences": 4, + "files_count": 1, + "tokens": ["--cashapp-50"] + }, + { + "id": "e140bd9f2ed1838d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(93.5% 0.215 143.5)", + "raw": "oklch(93.5% 0.215 143.5)", + "spec": { + "space": "oklch", + "source": "oklch(93.5% 0.215 143.5)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--cashapp-100"] + }, + { + "id": "1c1d2045f422f9c1", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(88.0% 0.315 142.5)", + "raw": "oklch(88.0% 0.315 142.5)", + "spec": { + "space": "oklch", + "source": "oklch(88.0% 0.315 142.5)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--cashapp-150"] + }, + { + "id": "1eb417c20070ba6f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(85.0% 0.302 142.3)", + "raw": "oklch(85.0% 0.302 142.3)", + "spec": { + "space": "oklch", + "source": "oklch(85.0% 0.302 142.3)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--cashapp-200"] + }, + { + "id": "c10e20a57ad95532", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(81.5% 0.287 142.1)", + "raw": "oklch(81.5% 0.287 142.1)", + "spec": { + "space": "oklch", + "source": "oklch(81.5% 0.287 142.1)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--cashapp-250"] + }, + { + "id": "78fe47534ae778e8", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(77.5% 0.269 141.9)", + "raw": "oklch(77.5% 0.269 141.9)", + "spec": { + "space": "oklch", + "source": "oklch(77.5% 0.269 141.9)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--cashapp-300"] + }, + { + "id": "47bf5d2813429212", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(73.0% 0.251 141.7)", + "raw": "oklch(73.0% 0.251 141.7)", + "spec": { + "space": "oklch", + "source": "oklch(73.0% 0.251 141.7)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--cashapp-350"] + }, + { + "id": "bcbe92513d2db51f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(69.0% 0.233 141.6)", + "raw": "oklch(69.0% 0.233 141.6)", + "spec": { + "space": "oklch", + "source": "oklch(69.0% 0.233 141.6)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--cashapp-400"] + }, + { + "id": "2725eef7c4705bf0", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(64.5% 0.214 141.4)", + "raw": "oklch(64.5% 0.214 141.4)", + "spec": { + "space": "oklch", + "source": "oklch(64.5% 0.214 141.4)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--cashapp-450"] + }, + { + "id": "cb51fa15223f7f3e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(60.0% 0.195 141.2)", + "raw": "oklch(60.0% 0.195 141.2)", + "spec": { + "space": "oklch", + "source": "oklch(60.0% 0.195 141.2)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--cashapp-500"] + }, + { + "id": "77c0a735e644e7b7", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(55.0% 0.176 141.1)", + "raw": "oklch(55.0% 0.176 141.1)", + "spec": { + "space": "oklch", + "source": "oklch(55.0% 0.176 141.1)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--cashapp-550"] + }, + { + "id": "20b7fd133152fa98", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(49.5% 0.156 140.9)", + "raw": "oklch(49.5% 0.156 140.9)", + "spec": { + "space": "oklch", + "source": "oklch(49.5% 0.156 140.9)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--cashapp-600"] + }, + { + "id": "86f6d3756b39eb64", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(44.5% 0.137 140.7)", + "raw": "oklch(44.5% 0.137 140.7)", + "spec": { + "space": "oklch", + "source": "oklch(44.5% 0.137 140.7)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--cashapp-650"] + }, + { + "id": "703544c7ebd83349", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(39.0% 0.117 140.5)", + "raw": "oklch(39.0% 0.117 140.5)", + "spec": { + "space": "oklch", + "source": "oklch(39.0% 0.117 140.5)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--cashapp-700"] + }, + { + "id": "9affb01b332574ee", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(34.0% 0.098 140.3)", + "raw": "oklch(34.0% 0.098 140.3)", + "spec": { + "space": "oklch", + "source": "oklch(34.0% 0.098 140.3)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--cashapp-750"] + }, + { + "id": "d157b5b3d2031444", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(28.5% 0.079 140.1)", + "raw": "oklch(28.5% 0.079 140.1)", + "spec": { + "space": "oklch", + "source": "oklch(28.5% 0.079 140.1)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--cashapp-800"] + }, + { + "id": "54927c2964ac689e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(23.5% 0.061 139.9)", + "raw": "oklch(23.5% 0.061 139.9)", + "spec": { + "space": "oklch", + "source": "oklch(23.5% 0.061 139.9)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--cashapp-850"] + }, + { + "id": "4c57f458eb7a5423", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(18.0% 0.044 139.7)", + "raw": "oklch(18.0% 0.044 139.7)", + "spec": { + "space": "oklch", + "source": "oklch(18.0% 0.044 139.7)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--cashapp-900"] + }, + { + "id": "7e75556788b196c5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(12.5% 0.028 139.5)", + "raw": "oklch(12.5% 0.028 139.5)", + "spec": { + "space": "oklch", + "source": "oklch(12.5% 0.028 139.5)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--cashapp-950"] + }, + { + "id": "c2e55072195672d8", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(96.0% 0.034 264.5)", + "raw": "oklch(96.0% 0.034 264.5)", + "spec": { + "space": "oklch", + "source": "oklch(96.0% 0.034 264.5)" + }, + "occurrences": 4, + "files_count": 1, + "tokens": ["--square-50"] + }, + { + "id": "47a14a8f7a1a5463", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(91.5% 0.060 264.3)", + "raw": "oklch(91.5% 0.060 264.3)", + "spec": { + "space": "oklch", + "source": "oklch(91.5% 0.060 264.3)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--square-100"] + }, + { + "id": "0b95338b93878481", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(87.0% 0.087 264.2)", + "raw": "oklch(87.0% 0.087 264.2)", + "spec": { + "space": "oklch", + "source": "oklch(87.0% 0.087 264.2)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--square-150"] + }, + { + "id": "ee1734b1bba21e11", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(82.5% 0.111 264.1)", + "raw": "oklch(82.5% 0.111 264.1)", + "spec": { + "space": "oklch", + "source": "oklch(82.5% 0.111 264.1)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--square-200"] + }, + { + "id": "b311fc7e98e93257", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(78.0% 0.133 264.0)", + "raw": "oklch(78.0% 0.133 264.0)", + "spec": { + "space": "oklch", + "source": "oklch(78.0% 0.133 264.0)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--square-250"] + }, + { + "id": "528da0e613d2ebf5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(73.5% 0.153 263.9)", + "raw": "oklch(73.5% 0.153 263.9)", + "spec": { + "space": "oklch", + "source": "oklch(73.5% 0.153 263.9)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--square-300"] + }, + { + "id": "0c405adb20b6fb5a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(69.0% 0.176 263.8)", + "raw": "oklch(69.0% 0.176 263.8)", + "spec": { + "space": "oklch", + "source": "oklch(69.0% 0.176 263.8)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--square-350"] + }, + { + "id": "63993ffdbc250943", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(65.0% 0.204 263.7)", + "raw": "oklch(65.0% 0.204 263.7)", + "spec": { + "space": "oklch", + "source": "oklch(65.0% 0.204 263.7)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--square-400"] + }, + { + "id": "3633a4ad833daa12", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(61.0% 0.234 263.6)", + "raw": "oklch(61.0% 0.234 263.6)", + "spec": { + "space": "oklch", + "source": "oklch(61.0% 0.234 263.6)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--square-450"] + }, + { + "id": "ab06d5caddceecc7", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(57.0% 0.263 263.5)", + "raw": "oklch(57.0% 0.263 263.5)", + "spec": { + "space": "oklch", + "source": "oklch(57.0% 0.263 263.5)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--square-500"] + }, + { + "id": "d79699bc2883b86f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(51.5% 0.250 263.4)", + "raw": "oklch(51.5% 0.250 263.4)", + "spec": { + "space": "oklch", + "source": "oklch(51.5% 0.250 263.4)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--square-550"] + }, + { + "id": "017f9ac229d29309", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(46.5% 0.236 263.3)", + "raw": "oklch(46.5% 0.236 263.3)", + "spec": { + "space": "oklch", + "source": "oklch(46.5% 0.236 263.3)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--square-600"] + }, + { + "id": "a79826b70a29cc0b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(41.0% 0.220 263.2)", + "raw": "oklch(41.0% 0.220 263.2)", + "spec": { + "space": "oklch", + "source": "oklch(41.0% 0.220 263.2)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--square-650"] + }, + { + "id": "6bd070403974576b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(35.5% 0.202 263.1)", + "raw": "oklch(35.5% 0.202 263.1)", + "spec": { + "space": "oklch", + "source": "oklch(35.5% 0.202 263.1)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--square-700"] + }, + { + "id": "f12732c2459430ab", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(30.5% 0.182 263.0)", + "raw": "oklch(30.5% 0.182 263.0)", + "spec": { + "space": "oklch", + "source": "oklch(30.5% 0.182 263.0)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--square-750"] + }, + { + "id": "8ffa2361bbdc4ae5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(25.0% 0.160 262.9)", + "raw": "oklch(25.0% 0.160 262.9)", + "spec": { + "space": "oklch", + "source": "oklch(25.0% 0.160 262.9)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--square-800"] + }, + { + "id": "a66170609ae80302", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(20.0% 0.126 262.8)", + "raw": "oklch(20.0% 0.126 262.8)", + "spec": { + "space": "oklch", + "source": "oklch(20.0% 0.126 262.8)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--square-850"] + }, + { + "id": "f198376c95d6afe8", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(14.5% 0.092 262.7)", + "raw": "oklch(14.5% 0.092 262.7)", + "spec": { + "space": "oklch", + "source": "oklch(14.5% 0.092 262.7)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--square-900"] + }, + { + "id": "088b385d5ec9c150", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(9.0% 0.057 262.6)", + "raw": "oklch(9.0% 0.057 262.6)", + "spec": { + "space": "oklch", + "source": "oklch(9.0% 0.057 262.6)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--square-950"] + }, + { + "id": "e3d38323750fc16e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(98.4% 0.003 247.858)", + "raw": "oklch(98.4% 0.003 247.858)", + "spec": { + "space": "oklch", + "source": "oklch(98.4% 0.003 247.858)" + }, + "occurrences": 4, + "files_count": 1, + "tokens": ["--slate-50"] + }, + { + "id": "b95ed4db24be32ba", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(96.8% 0.007 247.896)", + "raw": "oklch(96.8% 0.007 247.896)", + "spec": { + "space": "oklch", + "source": "oklch(96.8% 0.007 247.896)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--slate-100"] + }, + { + "id": "6ae94b3b176a6eb5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(92.9% 0.013 255.508)", + "raw": "oklch(92.9% 0.013 255.508)", + "spec": { + "space": "oklch", + "source": "oklch(92.9% 0.013 255.508)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--slate-200"] + }, + { + "id": "94e77d33e3a15ad0", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(86.9% 0.022 252.894)", + "raw": "oklch(86.9% 0.022 252.894)", + "spec": { + "space": "oklch", + "source": "oklch(86.9% 0.022 252.894)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--slate-300"] + }, + { + "id": "563a011c0ce28a73", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(70.4% 0.04 256.788)", + "raw": "oklch(70.4% 0.04 256.788)", + "spec": { + "space": "oklch", + "source": "oklch(70.4% 0.04 256.788)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--slate-400"] + }, + { + "id": "c49166f5511f23a9", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(55.4% 0.046 257.417)", + "raw": "oklch(55.4% 0.046 257.417)", + "spec": { + "space": "oklch", + "source": "oklch(55.4% 0.046 257.417)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--slate-500"] + }, + { + "id": "f39025711de0848d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(44.6% 0.043 257.281)", + "raw": "oklch(44.6% 0.043 257.281)", + "spec": { + "space": "oklch", + "source": "oklch(44.6% 0.043 257.281)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--slate-600"] + }, + { + "id": "e1f8676ed3002ea6", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(37.2% 0.044 257.287)", + "raw": "oklch(37.2% 0.044 257.287)", + "spec": { + "space": "oklch", + "source": "oklch(37.2% 0.044 257.287)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--slate-700"] + }, + { + "id": "4d79595693cf0bbb", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(27.9% 0.041 260.031)", + "raw": "oklch(27.9% 0.041 260.031)", + "spec": { + "space": "oklch", + "source": "oklch(27.9% 0.041 260.031)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--slate-800"] + }, + { + "id": "5b50567f43c127f3", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(20.8% 0.042 265.755)", + "raw": "oklch(20.8% 0.042 265.755)", + "spec": { + "space": "oklch", + "source": "oklch(20.8% 0.042 265.755)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--slate-900"] + }, + { + "id": "6acb14505e3e542a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(12.9% 0.042 264.695)", + "raw": "oklch(12.9% 0.042 264.695)", + "spec": { + "space": "oklch", + "source": "oklch(12.9% 0.042 264.695)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--slate-950"] + }, + { + "id": "6a776d932fa37810", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(98.5% 0.002 247.839)", + "raw": "oklch(98.5% 0.002 247.839)", + "spec": { + "space": "oklch", + "source": "oklch(98.5% 0.002 247.839)" + }, + "occurrences": 4, + "files_count": 1, + "tokens": ["--gray-50"] + }, + { + "id": "4d232d080f6658e7", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(96.7% 0.003 264.542)", + "raw": "oklch(96.7% 0.003 264.542)", + "spec": { + "space": "oklch", + "source": "oklch(96.7% 0.003 264.542)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--gray-100"] + }, + { + "id": "4fd8f2999838c976", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(92.8% 0.006 264.531)", + "raw": "oklch(92.8% 0.006 264.531)", + "spec": { + "space": "oklch", + "source": "oklch(92.8% 0.006 264.531)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--gray-200"] + }, + { + "id": "a34b7a99d0524260", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(87.2% 0.01 258.338)", + "raw": "oklch(87.2% 0.01 258.338)", + "spec": { + "space": "oklch", + "source": "oklch(87.2% 0.01 258.338)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--gray-300"] + }, + { + "id": "6004605f9b2158be", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(70.7% 0.022 261.325)", + "raw": "oklch(70.7% 0.022 261.325)", + "spec": { + "space": "oklch", + "source": "oklch(70.7% 0.022 261.325)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--gray-400"] + }, + { + "id": "0d7a8b3da271de3c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(55.1% 0.023 264.364)", + "raw": "oklch(55.1% 0.023 264.364)", + "spec": { + "space": "oklch", + "source": "oklch(55.1% 0.023 264.364)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--gray-500"] + }, + { + "id": "f7351ba6222d5f85", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(44.6% 0.02 264.533)", + "raw": "oklch(44.6% 0.02 264.533)", + "spec": { + "space": "oklch", + "source": "oklch(44.6% 0.02 264.533)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--gray-600"] + }, + { + "id": "d591ea6f61e711fc", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(37.3% 0.034 259.733)", + "raw": "oklch(37.3% 0.034 259.733)", + "spec": { + "space": "oklch", + "source": "oklch(37.3% 0.034 259.733)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--gray-700"] + }, + { + "id": "dee9f488fc7e7b84", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(27.8% 0.033 256.848)", + "raw": "oklch(27.8% 0.033 256.848)", + "spec": { + "space": "oklch", + "source": "oklch(27.8% 0.033 256.848)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--gray-800"] + }, + { + "id": "02304779b49264b5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(21.0% 0.034 264.665)", + "raw": "oklch(21.0% 0.034 264.665)", + "spec": { + "space": "oklch", + "source": "oklch(21.0% 0.034 264.665)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--gray-900"] + }, + { + "id": "7cd42cab7a9a4a37", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(13.0% 0.028 261.692)", + "raw": "oklch(13.0% 0.028 261.692)", + "spec": { + "space": "oklch", + "source": "oklch(13.0% 0.028 261.692)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--gray-950"] + }, + { + "id": "bad42323d8eecb3e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(98.5% 0 0)", + "raw": "oklch(98.5% 0 0)", + "spec": { + "space": "oklch", + "source": "oklch(98.5% 0 0)" + }, + "occurrences": 4, + "files_count": 1, + "tokens": ["--zinc-50"] + }, + { + "id": "4fec4c9e6af64907", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(96.7% 0.001 286.375)", + "raw": "oklch(96.7% 0.001 286.375)", + "spec": { + "space": "oklch", + "source": "oklch(96.7% 0.001 286.375)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--zinc-100"] + }, + { + "id": "a29c4408df379a95", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(92.0% 0.004 286.32)", + "raw": "oklch(92.0% 0.004 286.32)", + "spec": { + "space": "oklch", + "source": "oklch(92.0% 0.004 286.32)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--zinc-200"] + }, + { + "id": "ec9fa8d2af6ed0ab", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(87.1% 0.005 286.286)", + "raw": "oklch(87.1% 0.005 286.286)", + "spec": { + "space": "oklch", + "source": "oklch(87.1% 0.005 286.286)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--zinc-300"] + }, + { + "id": "4d9016df36eaeb81", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(70.5% 0.015 286.067)", + "raw": "oklch(70.5% 0.015 286.067)", + "spec": { + "space": "oklch", + "source": "oklch(70.5% 0.015 286.067)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--zinc-400"] + }, + { + "id": "a6a505a6fa3ef5ae", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(55.2% 0.016 285.786)", + "raw": "oklch(55.2% 0.016 285.786)", + "spec": { + "space": "oklch", + "source": "oklch(55.2% 0.016 285.786)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--zinc-500"] + }, + { + "id": "1affc5b45887f7ff", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(44.2% 0.013 285.786)", + "raw": "oklch(44.2% 0.013 285.786)", + "spec": { + "space": "oklch", + "source": "oklch(44.2% 0.013 285.786)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--zinc-600"] + }, + { + "id": "7b97598de4d4d310", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(37.0% 0.013 285.805)", + "raw": "oklch(37.0% 0.013 285.805)", + "spec": { + "space": "oklch", + "source": "oklch(37.0% 0.013 285.805)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--zinc-700"] + }, + { + "id": "f5d4abd03419f44b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(27.4% 0.006 286.618)", + "raw": "oklch(27.4% 0.006 286.618)", + "spec": { + "space": "oklch", + "source": "oklch(27.4% 0.006 286.618)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--zinc-800"] + }, + { + "id": "16ce92fdf65a732f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(21.0% 0 0)", + "raw": "oklch(21.0% 0 0)", + "spec": { + "space": "oklch", + "source": "oklch(21.0% 0 0)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--zinc-900"] + }, + { + "id": "adb175d82d6e7c8b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(14.5% 0 0)", + "raw": "oklch(14.5% 0 0)", + "spec": { + "space": "oklch", + "source": "oklch(14.5% 0 0)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--zinc-950"] + }, + { + "id": "7d62bdb28784c5dd", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(98.5% 0.001 106.423)", + "raw": "oklch(98.5% 0.001 106.423)", + "spec": { + "space": "oklch", + "source": "oklch(98.5% 0.001 106.423)" + }, + "occurrences": 4, + "files_count": 1, + "tokens": ["--stone-50"] + }, + { + "id": "c97bd08f4b6ac615", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(97.0% 0.001 106.424)", + "raw": "oklch(97.0% 0.001 106.424)", + "spec": { + "space": "oklch", + "source": "oklch(97.0% 0.001 106.424)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--stone-100"] + }, + { + "id": "85473c725321f3b9", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(92.3% 0.003 48.717)", + "raw": "oklch(92.3% 0.003 48.717)", + "spec": { + "space": "oklch", + "source": "oklch(92.3% 0.003 48.717)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--stone-200"] + }, + { + "id": "1ae03447a3e153ae", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(86.9% 0.005 56.366)", + "raw": "oklch(86.9% 0.005 56.366)", + "spec": { + "space": "oklch", + "source": "oklch(86.9% 0.005 56.366)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--stone-300"] + }, + { + "id": "601d8b166a6e4d3e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(70.9% 0.01 56.259)", + "raw": "oklch(70.9% 0.01 56.259)", + "spec": { + "space": "oklch", + "source": "oklch(70.9% 0.01 56.259)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--stone-400"] + }, + { + "id": "b7e9f2f1ea481a1e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(55.3% 0.014 58.071)", + "raw": "oklch(55.3% 0.014 58.071)", + "spec": { + "space": "oklch", + "source": "oklch(55.3% 0.014 58.071)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--stone-500"] + }, + { + "id": "4d9507cdc18179fa", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(44.4% 0.011 73.638)", + "raw": "oklch(44.4% 0.011 73.638)", + "spec": { + "space": "oklch", + "source": "oklch(44.4% 0.011 73.638)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--stone-600"] + }, + { + "id": "ef94607229feb0c8", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(37.4% 0.01 67.558)", + "raw": "oklch(37.4% 0.01 67.558)", + "spec": { + "space": "oklch", + "source": "oklch(37.4% 0.01 67.558)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--stone-700"] + }, + { + "id": "5fc45d068f2bea68", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(26.8% 0.007 34.298)", + "raw": "oklch(26.8% 0.007 34.298)", + "spec": { + "space": "oklch", + "source": "oklch(26.8% 0.007 34.298)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--stone-800"] + }, + { + "id": "242d220729fcb9f1", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(21.6% 0.006 56.044)", + "raw": "oklch(21.6% 0.006 56.044)", + "spec": { + "space": "oklch", + "source": "oklch(21.6% 0.006 56.044)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--stone-900"] + }, + { + "id": "bd27f693d6fd0b11", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(13.1% 0.004 285.938)", + "raw": "oklch(13.1% 0.004 285.938)", + "spec": { + "space": "oklch", + "source": "oklch(13.1% 0.004 285.938)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--stone-950"] + }, + { + "id": "2cc8a8bdfc819362", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(96.9% 0.015 12.422)", + "raw": "oklch(96.9% 0.015 12.422)", + "spec": { + "space": "oklch", + "source": "oklch(96.9% 0.015 12.422)" + }, + "occurrences": 4, + "files_count": 1, + "tokens": ["--rose-50"] + }, + { + "id": "f6adb867d707fc47", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(94.1% 0.03 12.58)", + "raw": "oklch(94.1% 0.03 12.58)", + "spec": { + "space": "oklch", + "source": "oklch(94.1% 0.03 12.58)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--rose-100"] + }, + { + "id": "462775f78bc3aa7c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(89.2% 0.058 10.001)", + "raw": "oklch(89.2% 0.058 10.001)", + "spec": { + "space": "oklch", + "source": "oklch(89.2% 0.058 10.001)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--rose-200"] + }, + { + "id": "1122111d03f79d05", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(81.0% 0.117 11.638)", + "raw": "oklch(81.0% 0.117 11.638)", + "spec": { + "space": "oklch", + "source": "oklch(81.0% 0.117 11.638)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--rose-300"] + }, + { + "id": "ba64c933c90d931f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(71.2% 0.194 13.428)", + "raw": "oklch(71.2% 0.194 13.428)", + "spec": { + "space": "oklch", + "source": "oklch(71.2% 0.194 13.428)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--rose-400"] + }, + { + "id": "a8b82557ecbf49df", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(64.5% 0.246 16.439)", + "raw": "oklch(64.5% 0.246 16.439)", + "spec": { + "space": "oklch", + "source": "oklch(64.5% 0.246 16.439)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--rose-500"] + }, + { + "id": "1d05ddc276a06d67", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(58.6% 0.253 17.585)", + "raw": "oklch(58.6% 0.253 17.585)", + "spec": { + "space": "oklch", + "source": "oklch(58.6% 0.253 17.585)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--rose-600"] + }, + { + "id": "908629c5a280e326", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(51.4% 0.222 16.935)", + "raw": "oklch(51.4% 0.222 16.935)", + "spec": { + "space": "oklch", + "source": "oklch(51.4% 0.222 16.935)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--rose-700"] + }, + { + "id": "b0e6850cf050ea23", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(43.6% 0.177 15.304)", + "raw": "oklch(43.6% 0.177 15.304)", + "spec": { + "space": "oklch", + "source": "oklch(43.6% 0.177 15.304)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--rose-800"] + }, + { + "id": "45912acc24c64b5b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(41.0% 0.159 10.272)", + "raw": "oklch(41.0% 0.159 10.272)", + "spec": { + "space": "oklch", + "source": "oklch(41.0% 0.159 10.272)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--rose-900"] + }, + { + "id": "b9ed2d32a2faae0b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(27.1% 0.105 12.094)", + "raw": "oklch(27.1% 0.105 12.094)", + "spec": { + "space": "oklch", + "source": "oklch(27.1% 0.105 12.094)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--rose-950"] + }, + { + "id": "74fcaaaa8707d629", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(98.0% 0.016 73.684)", + "raw": "oklch(98.0% 0.016 73.684)", + "spec": { + "space": "oklch", + "source": "oklch(98.0% 0.016 73.684)" + }, + "occurrences": 7, + "files_count": 1, + "tokens": ["--orange-50"] + }, + { + "id": "4059347497a76fea", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(95.4% 0.038 75.164)", + "raw": "oklch(95.4% 0.038 75.164)", + "spec": { + "space": "oklch", + "source": "oklch(95.4% 0.038 75.164)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--orange-100"] + }, + { + "id": "4ec4d52b3b26a49d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(90.0% 0.076 70.697)", + "raw": "oklch(90.0% 0.076 70.697)", + "spec": { + "space": "oklch", + "source": "oklch(90.0% 0.076 70.697)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--orange-200"] + }, + { + "id": "57fbfde70bbb725c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(83.7% 0.128 66.29)", + "raw": "oklch(83.7% 0.128 66.29)", + "spec": { + "space": "oklch", + "source": "oklch(83.7% 0.128 66.29)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--orange-300"] + }, + { + "id": "a15655551c556038", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(75.0% 0.183 55.934)", + "raw": "oklch(75.0% 0.183 55.934)", + "spec": { + "space": "oklch", + "source": "oklch(75.0% 0.183 55.934)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--orange-400"] + }, + { + "id": "51d69da79e613800", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(70.5% 0.213 47.604)", + "raw": "oklch(70.5% 0.213 47.604)", + "spec": { + "space": "oklch", + "source": "oklch(70.5% 0.213 47.604)" + }, + "occurrences": 9, + "files_count": 1, + "tokens": ["--orange-500", "--trend-down"] + }, + { + "id": "e2c6b8f5419d6f74", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(64.6% 0.222 41.116)", + "raw": "oklch(64.6% 0.222 41.116)", + "spec": { + "space": "oklch", + "source": "oklch(64.6% 0.222 41.116)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--orange-600"] + }, + { + "id": "e75846cad110877f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(55.8% 0.227 37.304)", + "raw": "oklch(55.8% 0.227 37.304)", + "spec": { + "space": "oklch", + "source": "oklch(55.8% 0.227 37.304)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--orange-700"] + }, + { + "id": "eb6be3963b5823d8", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(47.0% 0.157 37.304)", + "raw": "oklch(47.0% 0.157 37.304)", + "spec": { + "space": "oklch", + "source": "oklch(47.0% 0.157 37.304)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--orange-800"] + }, + { + "id": "006ba6f9a1cc839a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(37.7% 0.127 29.234)", + "raw": "oklch(37.7% 0.127 29.234)", + "spec": { + "space": "oklch", + "source": "oklch(37.7% 0.127 29.234)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--orange-900"] + }, + { + "id": "5e4a5d5706ee2506", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(26.6% 0.079 36.259)", + "raw": "oklch(26.6% 0.079 36.259)", + "spec": { + "space": "oklch", + "source": "oklch(26.6% 0.079 36.259)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--orange-950"] + }, + { + "id": "913664141a0fb587", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(98.7% 0.022 95.277)", + "raw": "oklch(98.7% 0.022 95.277)", + "spec": { + "space": "oklch", + "source": "oklch(98.7% 0.022 95.277)" + }, + "occurrences": 4, + "files_count": 1, + "tokens": ["--amber-50"] + }, + { + "id": "8ac3d4ed00ad0ae1", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(96.2% 0.059 95.617)", + "raw": "oklch(96.2% 0.059 95.617)", + "spec": { + "space": "oklch", + "source": "oklch(96.2% 0.059 95.617)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--amber-100"] + }, + { + "id": "0e4888275e81e81d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(92.5% 0.096 101.664)", + "raw": "oklch(92.5% 0.096 101.664)", + "spec": { + "space": "oklch", + "source": "oklch(92.5% 0.096 101.664)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--amber-200"] + }, + { + "id": "0a6476b9c727e644", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(87.9% 0.169 91.605)", + "raw": "oklch(87.9% 0.169 91.605)", + "spec": { + "space": "oklch", + "source": "oklch(87.9% 0.169 91.605)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--amber-300"] + }, + { + "id": "0523ff9f4b745ddc", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(82.8% 0.189 84.429)", + "raw": "oklch(82.8% 0.189 84.429)", + "spec": { + "space": "oklch", + "source": "oklch(82.8% 0.189 84.429)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--amber-400"] + }, + { + "id": "d8e975ba6246bba4", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(76.9% 0.188 70.08)", + "raw": "oklch(76.9% 0.188 70.08)", + "spec": { + "space": "oklch", + "source": "oklch(76.9% 0.188 70.08)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--amber-500"] + }, + { + "id": "e72b4a4554d5ee04", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(66.6% 0.179 58.318)", + "raw": "oklch(66.6% 0.179 58.318)", + "spec": { + "space": "oklch", + "source": "oklch(66.6% 0.179 58.318)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--amber-600"] + }, + { + "id": "828975cf6d27e9de", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(55.5% 0.163 48.998)", + "raw": "oklch(55.5% 0.163 48.998)", + "spec": { + "space": "oklch", + "source": "oklch(55.5% 0.163 48.998)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--amber-700"] + }, + { + "id": "08ee47cb29f73f72", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(47.3% 0.137 46.201)", + "raw": "oklch(47.3% 0.137 46.201)", + "spec": { + "space": "oklch", + "source": "oklch(47.3% 0.137 46.201)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--amber-800"] + }, + { + "id": "22f3ec49466213b8", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(41.4% 0.112 45.904)", + "raw": "oklch(41.4% 0.112 45.904)", + "spec": { + "space": "oklch", + "source": "oklch(41.4% 0.112 45.904)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--amber-900"] + }, + { + "id": "b2623ea4351acb5c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(27.9% 0.077 45.635)", + "raw": "oklch(27.9% 0.077 45.635)", + "spec": { + "space": "oklch", + "source": "oklch(27.9% 0.077 45.635)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--amber-950"] + }, + { + "id": "607be4f1dbe998ce", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(98.6% 0.031 120.757)", + "raw": "oklch(98.6% 0.031 120.757)", + "spec": { + "space": "oklch", + "source": "oklch(98.6% 0.031 120.757)" + }, + "occurrences": 6, + "files_count": 1, + "tokens": ["--lime-50"] + }, + { + "id": "98e160132bbbab46", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(96.7% 0.067 122.328)", + "raw": "oklch(96.7% 0.067 122.328)", + "spec": { + "space": "oklch", + "source": "oklch(96.7% 0.067 122.328)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--lime-100"] + }, + { + "id": "242de5fd1269bae7", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(93.8% 0.127 124.321)", + "raw": "oklch(93.8% 0.127 124.321)", + "spec": { + "space": "oklch", + "source": "oklch(93.8% 0.127 124.321)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--lime-200"] + }, + { + "id": "13ade9d08650d66d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(89.7% 0.196 126.665)", + "raw": "oklch(89.7% 0.196 126.665)", + "spec": { + "space": "oklch", + "source": "oklch(89.7% 0.196 126.665)" + }, + "occurrences": 9, + "files_count": 2, + "tokens": ["--lime-300", "--chart-5"] + }, + { + "id": "1e61e35b8365a901", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(84.1% 0.238 128.85)", + "raw": "oklch(84.1% 0.238 128.85)", + "spec": { + "space": "oklch", + "source": "oklch(84.1% 0.238 128.85)" + }, + "occurrences": 7, + "files_count": 1, + "tokens": ["--lime-400", "--chart-15"] + }, + { + "id": "e93b7b97ff6cae14", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(76.8% 0.233 130.85)", + "raw": "oklch(76.8% 0.233 130.85)", + "spec": { + "space": "oklch", + "source": "oklch(76.8% 0.233 130.85)" + }, + "occurrences": 4, + "files_count": 1, + "tokens": ["--lime-500"] + }, + { + "id": "0b8c501370aa8ad5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(64.8% 0.2 131.684)", + "raw": "oklch(64.8% 0.2 131.684)", + "spec": { + "space": "oklch", + "source": "oklch(64.8% 0.2 131.684)" + }, + "occurrences": 14, + "files_count": 2, + "tokens": ["--lime-600", "--trend-up", "--chart-8"] + }, + { + "id": "4f4fc207d2f33caa", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(53.2% 0.157 131.589)", + "raw": "oklch(53.2% 0.157 131.589)", + "spec": { + "space": "oklch", + "source": "oklch(53.2% 0.157 131.589)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--lime-700"] + }, + { + "id": "1c0e99ab0e7e9758", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(45.3% 0.124 130.933)", + "raw": "oklch(45.3% 0.124 130.933)", + "spec": { + "space": "oklch", + "source": "oklch(45.3% 0.124 130.933)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--lime-800"] + }, + { + "id": "1982cb386c7b45f6", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(40.5% 0.101 131.063)", + "raw": "oklch(40.5% 0.101 131.063)", + "spec": { + "space": "oklch", + "source": "oklch(40.5% 0.101 131.063)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--lime-900"] + }, + { + "id": "951e8eb064e44e0d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(27.4% 0.072 132.109)", + "raw": "oklch(27.4% 0.072 132.109)", + "spec": { + "space": "oklch", + "source": "oklch(27.4% 0.072 132.109)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--lime-950"] + }, + { + "id": "a3e794f0700023ab", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(97.9% 0.021 166.113)", + "raw": "oklch(97.9% 0.021 166.113)", + "spec": { + "space": "oklch", + "source": "oklch(97.9% 0.021 166.113)" + }, + "occurrences": 5, + "files_count": 1, + "tokens": ["--emerald-50"] + }, + { + "id": "453ca10969b0054e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(95.0% 0.052 163.051)", + "raw": "oklch(95.0% 0.052 163.051)", + "spec": { + "space": "oklch", + "source": "oklch(95.0% 0.052 163.051)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--emerald-100"] + }, + { + "id": "95cf07b21de73152", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(90.5% 0.093 164.15)", + "raw": "oklch(90.5% 0.093 164.15)", + "spec": { + "space": "oklch", + "source": "oklch(90.5% 0.093 164.15)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--emerald-200"] + }, + { + "id": "d77c89bf6b175f23", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(84.5% 0.143 164.978)", + "raw": "oklch(84.5% 0.143 164.978)", + "spec": { + "space": "oklch", + "source": "oklch(84.5% 0.143 164.978)" + }, + "occurrences": 8, + "files_count": 2, + "tokens": ["--emerald-300", "--chart-10"] + }, + { + "id": "b68601d4ae8521ff", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(76.5% 0.177 163.223)", + "raw": "oklch(76.5% 0.177 163.223)", + "spec": { + "space": "oklch", + "source": "oklch(76.5% 0.177 163.223)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--emerald-400"] + }, + { + "id": "4ecba3094105aa7f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(69.6% 0.17 162.48)", + "raw": "oklch(69.6% 0.17 162.48)", + "spec": { + "space": "oklch", + "source": "oklch(69.6% 0.17 162.48)" + }, + "occurrences": 3, + "files_count": 1, + "tokens": ["--emerald-500"] + }, + { + "id": "29d94b76d0e22a6b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(59.6% 0.145 163.225)", + "raw": "oklch(59.6% 0.145 163.225)", + "spec": { + "space": "oklch", + "source": "oklch(59.6% 0.145 163.225)" + }, + "occurrences": 8, + "files_count": 2, + "tokens": ["--emerald-600", "--chart-4"] + }, + { + "id": "fe6b3b1b7c2ca3d2", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(50.8% 0.127 165.612)", + "raw": "oklch(50.8% 0.127 165.612)", + "spec": { + "space": "oklch", + "source": "oklch(50.8% 0.127 165.612)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--emerald-700"] + }, + { + "id": "29e8555361f1bcfd", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(43.2% 0.095 166.913)", + "raw": "oklch(43.2% 0.095 166.913)", + "spec": { + "space": "oklch", + "source": "oklch(43.2% 0.095 166.913)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--emerald-800"] + }, + { + "id": "ca8752f086643cf2", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(37.8% 0.077 168.94)", + "raw": "oklch(37.8% 0.077 168.94)", + "spec": { + "space": "oklch", + "source": "oklch(37.8% 0.077 168.94)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--emerald-900"] + }, + { + "id": "d9e2f44483c434f4", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(26.2% 0.051 172.552)", + "raw": "oklch(26.2% 0.051 172.552)", + "spec": { + "space": "oklch", + "source": "oklch(26.2% 0.051 172.552)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--emerald-950"] + }, + { + "id": "0581037565964019", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(98.4% 0.014 180.72)", + "raw": "oklch(98.4% 0.014 180.72)", + "spec": { + "space": "oklch", + "source": "oklch(98.4% 0.014 180.72)" + }, + "occurrences": 5, + "files_count": 1, + "tokens": ["--teal-50"] + }, + { + "id": "01c7a4e21d9b68d9", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(95.3% 0.05 180.801)", + "raw": "oklch(95.3% 0.05 180.801)", + "spec": { + "space": "oklch", + "source": "oklch(95.3% 0.05 180.801)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--teal-100"] + }, + { + "id": "7ce89b18707ffa27", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(91.0% 0.096 180.426)", + "raw": "oklch(91.0% 0.096 180.426)", + "spec": { + "space": "oklch", + "source": "oklch(91.0% 0.096 180.426)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--teal-200"] + }, + { + "id": "099064e08617532b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(85.5% 0.138 181.071)", + "raw": "oklch(85.5% 0.138 181.071)", + "spec": { + "space": "oklch", + "source": "oklch(85.5% 0.138 181.071)" + }, + "occurrences": 9, + "files_count": 2, + "tokens": ["--teal-300", "--chart-7"] + }, + { + "id": "4feb7f13342c645a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(77.7% 0.152 181.912)", + "raw": "oklch(77.7% 0.152 181.912)", + "spec": { + "space": "oklch", + "source": "oklch(77.7% 0.152 181.912)" + }, + "occurrences": 7, + "files_count": 1, + "tokens": ["--teal-400", "--chart-14"] + }, + { + "id": "3c37ee8bd1ac0bd8", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(70.4% 0.14 182.503)", + "raw": "oklch(70.4% 0.14 182.503)", + "spec": { + "space": "oklch", + "source": "oklch(70.4% 0.14 182.503)" + }, + "occurrences": 3, + "files_count": 1, + "tokens": ["--teal-500"] + }, + { + "id": "6dde887ace15564a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(60.0% 0.118 184.704)", + "raw": "oklch(60.0% 0.118 184.704)", + "spec": { + "space": "oklch", + "source": "oklch(60.0% 0.118 184.704)" + }, + "occurrences": 8, + "files_count": 2, + "tokens": ["--teal-600", "--chart-2"] + }, + { + "id": "9aeee5692f2b4edd", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(51.1% 0.096 186.391)", + "raw": "oklch(51.1% 0.096 186.391)", + "spec": { + "space": "oklch", + "source": "oklch(51.1% 0.096 186.391)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--teal-700"] + }, + { + "id": "6af22d1a42f85d6f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(43.7% 0.078 188.216)", + "raw": "oklch(43.7% 0.078 188.216)", + "spec": { + "space": "oklch", + "source": "oklch(43.7% 0.078 188.216)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--teal-800"] + }, + { + "id": "1ddc1d88399c18f5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(39.4% 0.068 188.416)", + "raw": "oklch(39.4% 0.068 188.416)", + "spec": { + "space": "oklch", + "source": "oklch(39.4% 0.068 188.416)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--teal-900"] + }, + { + "id": "5ac86a95db86c07c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(27.7% 0.046 192.524)", + "raw": "oklch(27.7% 0.046 192.524)", + "spec": { + "space": "oklch", + "source": "oklch(27.7% 0.046 192.524)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--teal-950"] + }, + { + "id": "e9304acd0bb404c8", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(98.4% 0.019 200.873)", + "raw": "oklch(98.4% 0.019 200.873)", + "spec": { + "space": "oklch", + "source": "oklch(98.4% 0.019 200.873)" + }, + "occurrences": 5, + "files_count": 1, + "tokens": ["--cyan-50"] + }, + { + "id": "55677de872fadd8a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(95.6% 0.045 203.388)", + "raw": "oklch(95.6% 0.045 203.388)", + "spec": { + "space": "oklch", + "source": "oklch(95.6% 0.045 203.388)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--cyan-100"] + }, + { + "id": "2485d2f1c76e6274", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(91.7% 0.08 205.041)", + "raw": "oklch(91.7% 0.08 205.041)", + "spec": { + "space": "oklch", + "source": "oklch(91.7% 0.08 205.041)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--cyan-200"] + }, + { + "id": "5f023b88f63f182d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(86.5% 0.127 207.078)", + "raw": "oklch(86.5% 0.127 207.078)", + "spec": { + "space": "oklch", + "source": "oklch(86.5% 0.127 207.078)" + }, + "occurrences": 9, + "files_count": 2, + "tokens": ["--cyan-300", "--chart-3"] + }, + { + "id": "1386b8561558d29a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(78.9% 0.154 211.53)", + "raw": "oklch(78.9% 0.154 211.53)", + "spec": { + "space": "oklch", + "source": "oklch(78.9% 0.154 211.53)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--cyan-400"] + }, + { + "id": "33b1100acda30de5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(71.5% 0.143 215.221)", + "raw": "oklch(71.5% 0.143 215.221)", + "spec": { + "space": "oklch", + "source": "oklch(71.5% 0.143 215.221)" + }, + "occurrences": 3, + "files_count": 1, + "tokens": ["--cyan-500"] + }, + { + "id": "debcabe169c1744c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(60.9% 0.126 221.723)", + "raw": "oklch(60.9% 0.126 221.723)", + "spec": { + "space": "oklch", + "source": "oklch(60.9% 0.126 221.723)" + }, + "occurrences": 7, + "files_count": 2, + "tokens": ["--cyan-600", "--chart-9"] + }, + { + "id": "7a10c329f992bfae", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(52.0% 0.105 223.128)", + "raw": "oklch(52.0% 0.105 223.128)", + "spec": { + "space": "oklch", + "source": "oklch(52.0% 0.105 223.128)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--cyan-700"] + }, + { + "id": "eefbb0e78719f4cc", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(45.0% 0.085 224.283)", + "raw": "oklch(45.0% 0.085 224.283)", + "spec": { + "space": "oklch", + "source": "oklch(45.0% 0.085 224.283)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--cyan-800"] + }, + { + "id": "2b548e2622b12ad1", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(39.8% 0.07 227.392)", + "raw": "oklch(39.8% 0.07 227.392)", + "spec": { + "space": "oklch", + "source": "oklch(39.8% 0.07 227.392)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--cyan-900"] + }, + { + "id": "e1a8d6339f426959", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(30.2% 0.056 229.695)", + "raw": "oklch(30.2% 0.056 229.695)", + "spec": { + "space": "oklch", + "source": "oklch(30.2% 0.056 229.695)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--cyan-950"] + }, + { + "id": "55447e57c19175ca", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(97.7% 0.013 236.62)", + "raw": "oklch(97.7% 0.013 236.62)", + "spec": { + "space": "oklch", + "source": "oklch(97.7% 0.013 236.62)" + }, + "occurrences": 5, + "files_count": 1, + "tokens": ["--sky-50"] + }, + { + "id": "d41b5c4f3bc3e37d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(95.1% 0.026 236.824)", + "raw": "oklch(95.1% 0.026 236.824)", + "spec": { + "space": "oklch", + "source": "oklch(95.1% 0.026 236.824)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--sky-100"] + }, + { + "id": "359c02171228fdba", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(90.1% 0.058 230.902)", + "raw": "oklch(90.1% 0.058 230.902)", + "spec": { + "space": "oklch", + "source": "oklch(90.1% 0.058 230.902)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--sky-200"] + }, + { + "id": "870ea1753e44e7eb", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(82.8% 0.111 230.318)", + "raw": "oklch(82.8% 0.111 230.318)", + "spec": { + "space": "oklch", + "source": "oklch(82.8% 0.111 230.318)" + }, + "occurrences": 30, + "files_count": 2, + "tokens": ["--sky-300", "--chart-1"] + }, + { + "id": "5fdcb817d7911103", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(74.6% 0.16 232.661)", + "raw": "oklch(74.6% 0.16 232.661)", + "spec": { + "space": "oklch", + "source": "oklch(74.6% 0.16 232.661)" + }, + "occurrences": 7, + "files_count": 1, + "tokens": ["--sky-400", "--chart-13"] + }, + { + "id": "026ee604f7b27199", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(68.5% 0.169 237.323)", + "raw": "oklch(68.5% 0.169 237.323)", + "spec": { + "space": "oklch", + "source": "oklch(68.5% 0.169 237.323)" + }, + "occurrences": 3, + "files_count": 1, + "tokens": ["--sky-500"] + }, + { + "id": "047702026faefa94", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(58.8% 0.158 241.966)", + "raw": "oklch(58.8% 0.158 241.966)", + "spec": { + "space": "oklch", + "source": "oklch(58.8% 0.158 241.966)" + }, + "occurrences": 8, + "files_count": 2, + "tokens": ["--sky-600", "--chart-6"] + }, + { + "id": "da1525a170b205a6", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(50.0% 0.134 242.749)", + "raw": "oklch(50.0% 0.134 242.749)", + "spec": { + "space": "oklch", + "source": "oklch(50.0% 0.134 242.749)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--sky-700"] + }, + { + "id": "a090ba3430d647bf", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(44.3% 0.11 240.79)", + "raw": "oklch(44.3% 0.11 240.79)", + "spec": { + "space": "oklch", + "source": "oklch(44.3% 0.11 240.79)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--sky-800"] + }, + { + "id": "f0fceed355237ce9", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(39.1% 0.09 240.876)", + "raw": "oklch(39.1% 0.09 240.876)", + "spec": { + "space": "oklch", + "source": "oklch(39.1% 0.09 240.876)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--sky-900"] + }, + { + "id": "2a3458e96d61255a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(29.3% 0.066 243.157)", + "raw": "oklch(29.3% 0.066 243.157)", + "spec": { + "space": "oklch", + "source": "oklch(29.3% 0.066 243.157)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--sky-950"] + }, + { + "id": "db3e1a8a2e304939", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(96.2% 0.018 272.314)", + "raw": "oklch(96.2% 0.018 272.314)", + "spec": { + "space": "oklch", + "source": "oklch(96.2% 0.018 272.314)" + }, + "occurrences": 4, + "files_count": 1, + "tokens": ["--indigo-50"] + }, + { + "id": "175981dcc5d8d8d8", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(93.0% 0.034 272.788)", + "raw": "oklch(93.0% 0.034 272.788)", + "spec": { + "space": "oklch", + "source": "oklch(93.0% 0.034 272.788)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--indigo-100"] + }, + { + "id": "ad325768a635e1f0", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(87.0% 0.065 274.039)", + "raw": "oklch(87.0% 0.065 274.039)", + "spec": { + "space": "oklch", + "source": "oklch(87.0% 0.065 274.039)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--indigo-200"] + }, + { + "id": "943164c936b71c3e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(78.5% 0.115 274.713)", + "raw": "oklch(78.5% 0.115 274.713)", + "spec": { + "space": "oklch", + "source": "oklch(78.5% 0.115 274.713)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--indigo-300"] + }, + { + "id": "b7580b124a420185", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(67.3% 0.182 276.935)", + "raw": "oklch(67.3% 0.182 276.935)", + "spec": { + "space": "oklch", + "source": "oklch(67.3% 0.182 276.935)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--indigo-400"] + }, + { + "id": "2db04ed674800eda", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(58.5% 0.233 277.117)", + "raw": "oklch(58.5% 0.233 277.117)", + "spec": { + "space": "oklch", + "source": "oklch(58.5% 0.233 277.117)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--indigo-500"] + }, + { + "id": "d43875dd5003bf85", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(51.1% 0.262 276.966)", + "raw": "oklch(51.1% 0.262 276.966)", + "spec": { + "space": "oklch", + "source": "oklch(51.1% 0.262 276.966)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--indigo-600"] + }, + { + "id": "6f121e6f693d9b21", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(45.7% 0.24 277.023)", + "raw": "oklch(45.7% 0.24 277.023)", + "spec": { + "space": "oklch", + "source": "oklch(45.7% 0.24 277.023)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--indigo-700"] + }, + { + "id": "8a24b7757b34b9d2", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(39.8% 0.195 277.366)", + "raw": "oklch(39.8% 0.195 277.366)", + "spec": { + "space": "oklch", + "source": "oklch(39.8% 0.195 277.366)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--indigo-800"] + }, + { + "id": "c124f0c6c97d3259", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(35.9% 0.144 278.697)", + "raw": "oklch(35.9% 0.144 278.697)", + "spec": { + "space": "oklch", + "source": "oklch(35.9% 0.144 278.697)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--indigo-900"] + }, + { + "id": "8d3420047651ffab", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(25.7% 0.09 281.288)", + "raw": "oklch(25.7% 0.09 281.288)", + "spec": { + "space": "oklch", + "source": "oklch(25.7% 0.09 281.288)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--indigo-950"] + }, + { + "id": "26ceef0d9aeb144c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(96.9% 0.016 293.756)", + "raw": "oklch(96.9% 0.016 293.756)", + "spec": { + "space": "oklch", + "source": "oklch(96.9% 0.016 293.756)" + }, + "occurrences": 4, + "files_count": 1, + "tokens": ["--violet-50"] + }, + { + "id": "31ebcbdf023f6f7f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(94.3% 0.029 294.588)", + "raw": "oklch(94.3% 0.029 294.588)", + "spec": { + "space": "oklch", + "source": "oklch(94.3% 0.029 294.588)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--violet-100"] + }, + { + "id": "2b599eb1e94f4880", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(89.4% 0.057 293.283)", + "raw": "oklch(89.4% 0.057 293.283)", + "spec": { + "space": "oklch", + "source": "oklch(89.4% 0.057 293.283)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--violet-200"] + }, + { + "id": "9e12cade4758fd70", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(81.1% 0.111 293.571)", + "raw": "oklch(81.1% 0.111 293.571)", + "spec": { + "space": "oklch", + "source": "oklch(81.1% 0.111 293.571)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--violet-300"] + }, + { + "id": "368cd21c34f16c76", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(70.2% 0.183 293.541)", + "raw": "oklch(70.2% 0.183 293.541)", + "spec": { + "space": "oklch", + "source": "oklch(70.2% 0.183 293.541)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--violet-400"] + }, + { + "id": "2e7788601dc9bcf4", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(60.6% 0.25 292.717)", + "raw": "oklch(60.6% 0.25 292.717)", + "spec": { + "space": "oklch", + "source": "oklch(60.6% 0.25 292.717)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--violet-500"] + }, + { + "id": "8ffd0e518197f358", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(54.1% 0.281 293.009)", + "raw": "oklch(54.1% 0.281 293.009)", + "spec": { + "space": "oklch", + "source": "oklch(54.1% 0.281 293.009)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--violet-600"] + }, + { + "id": "ce11d97d3edf95cf", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(49.1% 0.27 292.581)", + "raw": "oklch(49.1% 0.27 292.581)", + "spec": { + "space": "oklch", + "source": "oklch(49.1% 0.27 292.581)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--violet-700"] + }, + { + "id": "4ab024991708bcfc", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(43.2% 0.232 292.759)", + "raw": "oklch(43.2% 0.232 292.759)", + "spec": { + "space": "oklch", + "source": "oklch(43.2% 0.232 292.759)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--violet-800"] + }, + { + "id": "593bd5616fbbc819", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(38.0% 0.189 293.745)", + "raw": "oklch(38.0% 0.189 293.745)", + "spec": { + "space": "oklch", + "source": "oklch(38.0% 0.189 293.745)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--violet-900"] + }, + { + "id": "8cd18076c02cd763", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(28.3% 0.141 291.089)", + "raw": "oklch(28.3% 0.141 291.089)", + "spec": { + "space": "oklch", + "source": "oklch(28.3% 0.141 291.089)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--violet-950"] + }, + { + "id": "8f081979687dfd67", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(97.7% 0.017 320.058)", + "raw": "oklch(97.7% 0.017 320.058)", + "spec": { + "space": "oklch", + "source": "oklch(97.7% 0.017 320.058)" + }, + "occurrences": 4, + "files_count": 1, + "tokens": ["--fuchsia-50"] + }, + { + "id": "8c050ceae0765818", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(95.2% 0.037 318.852)", + "raw": "oklch(95.2% 0.037 318.852)", + "spec": { + "space": "oklch", + "source": "oklch(95.2% 0.037 318.852)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--fuchsia-100"] + }, + { + "id": "cf386b141d0dda5a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(90.3% 0.076 319.562)", + "raw": "oklch(90.3% 0.076 319.562)", + "spec": { + "space": "oklch", + "source": "oklch(90.3% 0.076 319.562)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--fuchsia-200"] + }, + { + "id": "60c4b00c7ace5e6e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(83.3% 0.145 321.434)", + "raw": "oklch(83.3% 0.145 321.434)", + "spec": { + "space": "oklch", + "source": "oklch(83.3% 0.145 321.434)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--fuchsia-300"] + }, + { + "id": "4c699c145df38845", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(74.0% 0.238 322.16)", + "raw": "oklch(74.0% 0.238 322.16)", + "spec": { + "space": "oklch", + "source": "oklch(74.0% 0.238 322.16)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--fuchsia-400"] + }, + { + "id": "30cabb8d3fc7802f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(66.7% 0.295 322.15)", + "raw": "oklch(66.7% 0.295 322.15)", + "spec": { + "space": "oklch", + "source": "oklch(66.7% 0.295 322.15)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--fuchsia-500"] + }, + { + "id": "3c1c91167e6e8e5b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(59.1% 0.293 322.896)", + "raw": "oklch(59.1% 0.293 322.896)", + "spec": { + "space": "oklch", + "source": "oklch(59.1% 0.293 322.896)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--fuchsia-600"] + }, + { + "id": "46e18ea0944e7fc5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(52.4% 0.253 323.046)", + "raw": "oklch(52.4% 0.253 323.046)", + "spec": { + "space": "oklch", + "source": "oklch(52.4% 0.253 323.046)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--fuchsia-700"] + }, + { + "id": "431e5c11b9e5f243", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(45.2% 0.211 324.591)", + "raw": "oklch(45.2% 0.211 324.591)", + "spec": { + "space": "oklch", + "source": "oklch(45.2% 0.211 324.591)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--fuchsia-800"] + }, + { + "id": "deb865c56ab06b6e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(40.2% 0.159 325.612)", + "raw": "oklch(40.2% 0.159 325.612)", + "spec": { + "space": "oklch", + "source": "oklch(40.2% 0.159 325.612)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--fuchsia-900"] + }, + { + "id": "4cefa0777430edad", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(29.6% 0.113 325.612)", + "raw": "oklch(29.6% 0.113 325.612)", + "spec": { + "space": "oklch", + "source": "oklch(29.6% 0.113 325.612)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--fuchsia-950"] + }, + { + "id": "5e2d7a2bac675dd9", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(97.1% 0.014 343.198)", + "raw": "oklch(97.1% 0.014 343.198)", + "spec": { + "space": "oklch", + "source": "oklch(97.1% 0.014 343.198)" + }, + "occurrences": 4, + "files_count": 1, + "tokens": ["--pink-50"] + }, + { + "id": "59b7cece85944399", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(94.8% 0.028 342.258)", + "raw": "oklch(94.8% 0.028 342.258)", + "spec": { + "space": "oklch", + "source": "oklch(94.8% 0.028 342.258)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--pink-100"] + }, + { + "id": "15b572887abbcb5d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(90.9% 0.058 343.231)", + "raw": "oklch(90.9% 0.058 343.231)", + "spec": { + "space": "oklch", + "source": "oklch(90.9% 0.058 343.231)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--pink-200"] + }, + { + "id": "d0b05bc82a78c8c0", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(82.3% 0.12 346.018)", + "raw": "oklch(82.3% 0.12 346.018)", + "spec": { + "space": "oklch", + "source": "oklch(82.3% 0.12 346.018)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--pink-300"] + }, + { + "id": "39412dd56debb3ad", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(71.8% 0.202 349.761)", + "raw": "oklch(71.8% 0.202 349.761)", + "spec": { + "space": "oklch", + "source": "oklch(71.8% 0.202 349.761)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--pink-400"] + }, + { + "id": "404e9ce8376b2b45", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(65.6% 0.241 354.308)", + "raw": "oklch(65.6% 0.241 354.308)", + "spec": { + "space": "oklch", + "source": "oklch(65.6% 0.241 354.308)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--pink-500"] + }, + { + "id": "14306e05c6e66688", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(59.2% 0.249 0.584)", + "raw": "oklch(59.2% 0.249 0.584)", + "spec": { + "space": "oklch", + "source": "oklch(59.2% 0.249 0.584)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--pink-600"] + }, + { + "id": "d46d422039d1794b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(52.5% 0.223 3.958)", + "raw": "oklch(52.5% 0.223 3.958)", + "spec": { + "space": "oklch", + "source": "oklch(52.5% 0.223 3.958)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--pink-700"] + }, + { + "id": "d75f06a21c37e502", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(45.1% 0.187 3.815)", + "raw": "oklch(45.1% 0.187 3.815)", + "spec": { + "space": "oklch", + "source": "oklch(45.1% 0.187 3.815)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--pink-800"] + }, + { + "id": "3f240909856c2c25", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(40.8% 0.153 2.432)", + "raw": "oklch(40.8% 0.153 2.432)", + "spec": { + "space": "oklch", + "source": "oklch(40.8% 0.153 2.432)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--pink-900"] + }, + { + "id": "2b85eba8d4dc67aa", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(29.1% 0.109 3.907)", + "raw": "oklch(29.1% 0.109 3.907)", + "spec": { + "space": "oklch", + "source": "oklch(29.1% 0.109 3.907)" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--pink-950"] + }, + { + "id": "b79b5c74cc53288b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "spacing", + "value": "4px", + "raw": "4px", + "spec": { + "unit": "px", + "magnitude": 4 + }, + "occurrences": 5, + "files_count": 1, + "tokens": ["--page-x-padding"] + }, + { + "id": "1773d0764da20e63", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklab(97.913% 0 -0.00011)", + "raw": "oklab(97.913% 0 -0.00011)", + "spec": { + "space": "oklab", + "source": "oklab(97.913% 0 -0.00011)" + }, + "occurrences": 38, + "files_count": 3, + "tokens": ["--elevation-0", "--sidebar"] + }, + { + "id": "6c52e18315dfd098", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "oklch(95.815% 0.00011 271.152)", + "raw": "oklch(95.815% 0.00011 271.152)", + "spec": { + "space": "oklch", + "source": "oklch(95.815% 0.00011 271.152)" + }, + "occurrences": 3, + "files_count": 1, + "tokens": ["--elevation-2"] + }, + { + "id": "0bf379ac9680f5fb", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "rgba(0, 0, 0, 0.1)", + "raw": "rgba(0, 0, 0, 0.1)", + "spec": { + "space": "srgb", + "source": "rgba(0, 0, 0, 0.1)" + }, + "occurrences": 4, + "files_count": 1, + "tokens": ["--popover-border"] + }, + { + "id": "c50db46b1a5c00a5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "color-mix(", + "raw": "color-mix(", + "spec": { + "source": "color-mix(" + }, + "occurrences": 6, + "files_count": 1, + "tokens": ["--sidebar-accent"] + }, + { + "id": "f5bedf7585ecbb06", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "color", + "value": "#FCFCFC", + "raw": "#FCFCFC", + "spec": { + "space": "srgb", + "hex": "#FCFCFC" + }, + "occurrences": 7, + "files_count": 1, + "tokens": ["--panel"] + }, + { + "id": "7d0e8948a77875cc", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "spacing", + "value": "16px", + "raw": "16px", + "spec": { + "unit": "px", + "magnitude": 16 + }, + "occurrences": 2, + "files_count": 1, + "tokens": [ + "--core-icon-small-size-width", + "--core-icon-small-size-height" + ] + }, + { + "id": "ac9a4377afd1cf60", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "spacing", + "value": "24px", + "raw": "24px", + "spec": { + "unit": "px", + "magnitude": 24 + }, + "occurrences": 2, + "files_count": 1, + "tokens": [ + "--core-icon-medium-size-width", + "--core-icon-medium-size-height" + ] + }, + { + "id": "1583a343ef6aae28", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "spacing", + "value": "32px", + "raw": "32px", + "spec": { + "unit": "px", + "magnitude": 32 + }, + "occurrences": 2, + "files_count": 1, + "tokens": [ + "--core-icon-large-size-width", + "--core-icon-large-size-height" + ] + }, + { + "id": "30f6f736ed0b5eed", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "typography", + "value": "'Cash Sans', ui-sans-serif, system-ui, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\"", + "raw": "'Cash Sans', ui-sans-serif, system-ui, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\"", + "spec": { + "source": "'Cash Sans', ui-sans-serif, system-ui, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\"" + }, + "occurrences": 1, + "files_count": 1, + "tokens": ["--font-sans"] + }, + { + "id": "f1832ff0e38058dc", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "spacing", + "value": "0.5px", + "raw": "0.5px", + "spec": { + "unit": "px", + "magnitude": 0.5 + }, + "occurrences": 1, + "files_count": 1, + "tokens": ["--border-width-hairline"] + }, + { + "id": "f8dfe024ddda66a0", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "radius", + "value": "4px", + "raw": "4px", + "spec": { + "unit": "px", + "magnitude": 4 + }, + "occurrences": 1, + "files_count": 1, + "tokens": ["--radius-xs"] + }, + { + "id": "6e28656b4a9bb2c7", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "radius", + "value": "6px", + "raw": "6px", + "spec": { + "unit": "px", + "magnitude": 6 + }, + "occurrences": 1, + "files_count": 1, + "tokens": ["--radius-sm"] + }, + { + "id": "69c107b5d41411ea", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "radius", + "value": "8px", + "raw": "8px", + "spec": { + "unit": "px", + "magnitude": 8 + }, + "occurrences": 1, + "files_count": 1, + "tokens": ["--radius-md"] + }, + { + "id": "8bcb4feedf964381", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "radius", + "value": "12px", + "raw": "12px", + "spec": { + "unit": "px", + "magnitude": 12 + }, + "occurrences": 1, + "files_count": 1, + "tokens": ["--radius-lg"] + }, + { + "id": "8de605c3f31baf91", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "radius", + "value": "16px", + "raw": "16px", + "spec": { + "unit": "px", + "magnitude": 16 + }, + "occurrences": 1, + "files_count": 1, + "tokens": ["--radius-xl"] + }, + { + "id": "ddd001e4f4d962bd", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "radius", + "value": "20px", + "raw": "20px", + "spec": { + "unit": "px", + "magnitude": 20 + }, + "occurrences": 1, + "files_count": 1, + "tokens": ["--radius-2xl"] + }, + { + "id": "371fe39583809520", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "radius", + "value": "24px", + "raw": "24px", + "spec": { + "unit": "px", + "magnitude": 24 + }, + "occurrences": 1, + "files_count": 1, + "tokens": ["--radius-3xl"] + }, + { + "id": "d8bf0eb3fd2a2157", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "radius", + "value": "32px", + "raw": "32px", + "spec": { + "unit": "px", + "magnitude": 32 + }, + "occurrences": 1, + "files_count": 1, + "tokens": ["--radius-4xl"] + }, + { + "id": "4fb4b5a4cccb4309", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "spacing", + "value": "280px", + "raw": "280px", + "spec": { + "unit": "px", + "magnitude": 280 + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--width-sidebar"] + }, + { + "id": "e11afaf68fe452c1", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "spacing", + "value": "calc(100vw - var(--width-sidebar) - (var(--spacing) * 8))", + "raw": "calc(100vw - var(--width-sidebar) - (var(--spacing) * 8))", + "spec": { + "source": "calc(100vw - var(--width-sidebar) - (var(--spacing) * 8))" + }, + "occurrences": 1, + "files_count": 1, + "tokens": ["--width-main"] + }, + { + "id": "27600fb7bb028a76", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "spacing", + "value": "48rem", + "raw": "48rem", + "spec": { + "unit": "rem", + "magnitude": 48 + }, + "occurrences": 2, + "files_count": 2, + "tokens": ["--width-content-narrow"] + }, + { + "id": "a522a5f6c87bdd81", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "spacing", + "value": "82rem", + "raw": "82rem", + "spec": { + "unit": "rem", + "magnitude": 82 + }, + "occurrences": 2, + "files_count": 2, + "tokens": ["--width-content-wide"] + }, + { + "id": "752eca10441602ee", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "shadow", + "value": "0 1px 0 0 rgba(0, 0, 0, 0.05)", + "raw": "0 1px 0 0 rgba(0, 0, 0, 0.05)", + "spec": { + "source": "0 1px 0 0 rgba(0, 0, 0, 0.05)" + }, + "occurrences": 1, + "files_count": 1, + "tokens": ["--shadow-2xs"] + }, + { + "id": "2d167e25abfb50e4", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "shadow", + "value": "0 0 1px 0 rgba(0, 0, 0, 0.05)", + "raw": "0 0 1px 0 rgba(0, 0, 0, 0.05)", + "spec": { + "source": "0 0 1px 0 rgba(0, 0, 0, 0.05)" + }, + "occurrences": 1, + "files_count": 1, + "tokens": ["--shadow-xs"] + }, + { + "id": "3c775d31b6ab7a37", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "shadow", + "value": "0 0 2px 0 rgba(0, 0, 0, 0.1), 0 4px 8px -2px rgba(0, 0, 0, 0.06)", + "raw": "0 0 2px 0 rgba(0, 0, 0, 0.1), 0 4px 8px -2px rgba(0, 0, 0, 0.06)", + "spec": { + "source": "0 0 2px 0 rgba(0, 0, 0, 0.1), 0 4px 8px -2px rgba(0, 0, 0, 0.06)" + }, + "occurrences": 1, + "files_count": 1, + "tokens": ["--shadow-sm"] + }, + { + "id": "fe1f99f91b5a7565", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "shadow", + "value": "0 0 2px 0 rgba(0, 0, 0, 0.1), 0 8px 16px -4px rgba(0, 0, 0, 0.1)", + "raw": "0 0 2px 0 rgba(0, 0, 0, 0.1), 0 8px 16px -4px rgba(0, 0, 0, 0.1)", + "spec": { + "source": "0 0 2px 0 rgba(0, 0, 0, 0.1), 0 8px 16px -4px rgba(0, 0, 0, 0.1)" + }, + "occurrences": 1, + "files_count": 1, + "tokens": ["--shadow-md"] + }, + { + "id": "49ec969b537d60de", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "shadow", + "value": "0 0 2px 0 rgba(0, 0, 0, 0.1), 0 16px 32px -8px rgba(0, 0, 0, 0.1)", + "raw": "0 0 2px 0 rgba(0, 0, 0, 0.1), 0 16px 32px -8px rgba(0, 0, 0, 0.1)", + "spec": { + "source": "0 0 2px 0 rgba(0, 0, 0, 0.1), 0 16px 32px -8px rgba(0, 0, 0, 0.1)" + }, + "occurrences": 1, + "files_count": 1, + "tokens": ["--shadow-lg"] + }, + { + "id": "b19bf980c161c785", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "shadow", + "value": "0 0 2px 0 rgba(0, 0, 0, 0.1), 0 24px 48px -12px rgba(0, 0, 0, 0.1)", + "raw": "0 0 2px 0 rgba(0, 0, 0, 0.1), 0 24px 48px -12px rgba(0, 0, 0, 0.1)", + "spec": { + "source": "0 0 2px 0 rgba(0, 0, 0, 0.1), 0 24px 48px -12px rgba(0, 0, 0, 0.1)" + }, + "occurrences": 1, + "files_count": 1, + "tokens": ["--shadow-xl"] + }, + { + "id": "0db866f3a832f3a0", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "shadow", + "value": "0 25px 50px -12px rgba(0, 0, 0, 0.25)", + "raw": "0 25px 50px -12px rgba(0, 0, 0, 0.25)", + "spec": { + "source": "0 25px 50px -12px rgba(0, 0, 0, 0.25)" + }, + "occurrences": 1, + "files_count": 1, + "tokens": ["--shadow-2xl"] + }, + { + "id": "20d2fb0eb4dfdd76", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "shadow", + "value": "inset 0 0 1px 0 rgba(0, 0, 0, 0.40)", + "raw": "inset 0 0 1px 0 rgba(0, 0, 0, 0.40)", + "spec": { + "source": "inset 0 0 1px 0 rgba(0, 0, 0, 0.40)" + }, + "occurrences": 1, + "files_count": 1, + "tokens": ["--shadow-inner"] + }, + { + "id": "6299c2d33eae5bf1", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "typography", + "value": "0.8125rem", + "raw": "0.8125rem", + "spec": { + "unit": "rem", + "magnitude": 0.8125 + }, + "occurrences": 3, + "files_count": 1, + "tokens": ["--text-xs"] + }, + { + "id": "8fd81dd54adfceb9", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "typography", + "value": "1rem", + "raw": "1rem", + "spec": { + "unit": "rem", + "magnitude": 1 + }, + "occurrences": 4, + "files_count": 1, + "tokens": ["--text-xs--line-height", "--text-base"] + }, + { + "id": "e682a0a174b657bd", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "typography", + "value": "0", + "raw": "0", + "spec": { + "source": "0" + }, + "occurrences": 5, + "files_count": 1, + "tokens": [ + "--text-xs--letter-spacing", + "--text-sm--letter-spacing", + "--text-base--letter-spacing", + "--text-lg--letter-spacing", + "--text-xl--letter-spacing" + ] + }, + { + "id": "dc5540e0d1ec3bf9", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "typography", + "value": "0.875rem", + "raw": "0.875rem", + "spec": { + "unit": "rem", + "magnitude": 0.875 + }, + "occurrences": 3, + "files_count": 1, + "tokens": ["--text-sm"] + }, + { + "id": "da1b9f9a57611faf", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "typography", + "value": "1.25rem", + "raw": "1.25rem", + "spec": { + "unit": "rem", + "magnitude": 1.25 + }, + "occurrences": 4, + "files_count": 1, + "tokens": ["--text-sm--line-height", "--text-xl"] + }, + { + "id": "4aaeb1f214004e5e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "typography", + "value": "1.5rem", + "raw": "1.5rem", + "spec": { + "unit": "rem", + "magnitude": 1.5 + }, + "occurrences": 4, + "files_count": 1, + "tokens": ["--text-base--line-height", "--text-2xl"] + }, + { + "id": "b441edec93d13a1b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "typography", + "value": "1.125rem", + "raw": "1.125rem", + "spec": { + "unit": "rem", + "magnitude": 1.125 + }, + "occurrences": 3, + "files_count": 1, + "tokens": ["--text-lg"] + }, + { + "id": "23151acf48673b01", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "typography", + "value": "1.75rem", + "raw": "1.75rem", + "spec": { + "unit": "rem", + "magnitude": 1.75 + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--text-lg--line-height", "--text-xl--line-height"] + }, + { + "id": "cf80ed45ea07a562", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "typography", + "value": "2rem", + "raw": "2rem", + "spec": { + "unit": "rem", + "magnitude": 2 + }, + "occurrences": 4, + "files_count": 1, + "tokens": ["--text-2xl--line-height", "--text-3xl"] + }, + { + "id": "e5c6731a6a699be5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "typography", + "value": "-0.2px", + "raw": "-0.2px", + "spec": { + "source": "-0.2px" + }, + "occurrences": 1, + "files_count": 1, + "tokens": ["--text-2xl--letter-spacing"] + }, + { + "id": "bca9a921e242cb20", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "typography", + "value": "2.25rem", + "raw": "2.25rem", + "spec": { + "unit": "rem", + "magnitude": 2.25 + }, + "occurrences": 4, + "files_count": 1, + "tokens": ["--text-3xl--line-height", "--text-4xl"] + }, + { + "id": "df2d385e977290e5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "typography", + "value": "-0.4px", + "raw": "-0.4px", + "spec": { + "source": "-0.4px" + }, + "occurrences": 1, + "files_count": 1, + "tokens": ["--text-3xl--letter-spacing"] + }, + { + "id": "8fd0636bb67da791", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "typography", + "value": "2.5rem", + "raw": "2.5rem", + "spec": { + "unit": "rem", + "magnitude": 2.5 + }, + "occurrences": 1, + "files_count": 1, + "tokens": ["--text-4xl--line-height"] + }, + { + "id": "dea3925c9406c073", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "typography", + "value": "-0.5px", + "raw": "-0.5px", + "spec": { + "source": "-0.5px" + }, + "occurrences": 1, + "files_count": 1, + "tokens": ["--text-4xl--letter-spacing"] + }, + { + "id": "11549153bb292a4d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "typography", + "value": "2.75rem", + "raw": "2.75rem", + "spec": { + "unit": "rem", + "magnitude": 2.75 + }, + "occurrences": 4, + "files_count": 1, + "tokens": ["--text-5xl", "--text-5xl--line-height"] + }, + { + "id": "0a1e11861269971f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "typography", + "value": "-1.4px", + "raw": "-1.4px", + "spec": { + "source": "-1.4px" + }, + "occurrences": 1, + "files_count": 1, + "tokens": ["--text-5xl--letter-spacing"] + }, + { + "id": "2a7ba72de8f075a9", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "typography", + "value": "3.5rem", + "raw": "3.5rem", + "spec": { + "unit": "rem", + "magnitude": 3.5 + }, + "occurrences": 8, + "files_count": 1, + "tokens": [ + "--text-6xl", + "--text-6xl--line-height", + "--text-7xl", + "--text-7xl--line-height" + ] + }, + { + "id": "0f2ba1d999bb5c46", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "typography", + "value": "-2.2px", + "raw": "-2.2px", + "spec": { + "source": "-2.2px" + }, + "occurrences": 2, + "files_count": 1, + "tokens": ["--text-6xl--letter-spacing", "--text-7xl--letter-spacing"] + }, + { + "id": "8ff43ddb2b49268d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "motion", + "value": "highlights-tab-fade-in 150ms ease forwards", + "raw": "highlights-tab-fade-in 150ms ease forwards", + "spec": { + "source": "highlights-tab-fade-in 150ms ease forwards" + }, + "occurrences": 1, + "files_count": 1, + "tokens": ["--animate-highlights-tab-fade-in"] + }, + { + "id": "432b3855876b2d20", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "kind": "breakpoint", + "value": "768px", + "raw": "768px", + "spec": { + "unit": "px", + "magnitude": 768 + }, + "occurrences": 1, + "files_count": 1 + } + ], + "tokens": [ + { + "id": "f5ccdf8c9cba188d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--black", + "alias_chain": [], + "resolved_value": "oklch(0.00% 0.0000 0.00)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "1468a8bb4c5da60e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--white", + "alias_chain": [], + "resolved_value": "oklch(100.00% 0.0000 89.88)", + "occurrences": 10, + "files_count": 1, + "kind": "color" + }, + { + "id": "7fbff889599ec18e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--neutral-30", + "alias_chain": [], + "resolved_value": "oklch(97.72% 0.0000 89.88)", + "occurrences": 6, + "files_count": 1, + "kind": "color" + }, + { + "id": "36a4f08767f58a7d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--neutral-50", + "alias_chain": [], + "resolved_value": "oklch(95.42% 0.0000 89.88)", + "occurrences": 20, + "files_count": 1, + "kind": "color" + }, + { + "id": "925f0f800fc98c03", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--neutral-80", + "alias_chain": [], + "resolved_value": "oklch(93.12% 0.0000 89.88)", + "occurrences": 5, + "files_count": 1, + "kind": "color" + }, + { + "id": "d037da2fe0c32121", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--neutral-100", + "alias_chain": [], + "resolved_value": "oklch(91.57% 0.0000 89.88)", + "occurrences": 6, + "files_count": 1, + "kind": "color" + }, + { + "id": "0fc02ecdbfbb0fcb", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--neutral-150", + "alias_chain": [], + "resolved_value": "oklch(86.89% 0.0000 89.88)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "d70a72cd16fd531e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--neutral-200", + "alias_chain": [], + "resolved_value": "oklch(82.94% 0.0000 89.88)", + "occurrences": 3, + "files_count": 1, + "kind": "color" + }, + { + "id": "3ae79b6a93d65ec9", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--neutral-250", + "alias_chain": [], + "resolved_value": "oklch(78.13% 0.0000 89.88)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "35f28e7abb174af9", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--neutral-300", + "alias_chain": [], + "resolved_value": "oklch(74.07% 0.0000 89.88)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "bc1a546d09ca9a6b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--neutral-350", + "alias_chain": [], + "resolved_value": "oklch(69.96% 0.0000 89.88)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "7a96a5490935d1a6", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--neutral-400", + "alias_chain": [], + "resolved_value": "oklch(64.94% 0.0000 89.88)", + "occurrences": 5, + "files_count": 1, + "kind": "color" + }, + { + "id": "6704622adb5e87dc", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--neutral-450", + "alias_chain": [], + "resolved_value": "oklch(60.68% 0.0000 89.88)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "73594a2be2d78713", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--neutral-500", + "alias_chain": [], + "resolved_value": "oklch(56.34% 0.0000 89.88)", + "occurrences": 6, + "files_count": 1, + "kind": "color" + }, + { + "id": "fd9a3ff34a621050", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--neutral-550", + "alias_chain": [], + "resolved_value": "oklch(51.92% 0.0000 89.88)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "273345c69f3a25c9", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--neutral-600", + "alias_chain": [], + "resolved_value": "oklch(47.41% 0.0000 89.88)", + "occurrences": 5, + "files_count": 1, + "kind": "color" + }, + { + "id": "e7d596c593f8933a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--neutral-650", + "alias_chain": [], + "resolved_value": "oklch(43.71% 0.0000 89.88)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "6433682ea1902b8a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--neutral-700", + "alias_chain": [], + "resolved_value": "oklch(38.99% 0.0000 89.88)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "47854f319121313e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--neutral-750", + "alias_chain": [], + "resolved_value": "oklch(35.10% 0.0000 89.88)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "a4d0b93fb8397170", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--neutral-800", + "alias_chain": [], + "resolved_value": "oklch(30.08% 0.0000 89.88)", + "occurrences": 3, + "files_count": 1, + "kind": "color" + }, + { + "id": "e244114cbed1641a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--neutral-850", + "alias_chain": [], + "resolved_value": "oklch(26.97% 0.0000 89.88)", + "occurrences": 3, + "files_count": 1, + "kind": "color" + }, + { + "id": "f54975f8f7146885", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--neutral-900", + "alias_chain": [], + "resolved_value": "oklch(21.56% 0.0000 89.88)", + "occurrences": 11, + "files_count": 1, + "kind": "color" + }, + { + "id": "aac177c6d4e82c51", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--neutral-950", + "alias_chain": [], + "resolved_value": "oklch(16.98% 0.0000 89.88)", + "occurrences": 10, + "files_count": 1, + "kind": "color" + }, + { + "id": "92f65573f13d18aa", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--neutral-base", + "alias_chain": ["--neutral-base", "--neutral-500"], + "resolved_value": "oklch(56.34% 0.0000 89.88)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "d0b03360ac3fe8e6", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--red-50", + "alias_chain": [], + "resolved_value": "oklch(95.78% 0.0180 17.47)", + "occurrences": 10, + "files_count": 1, + "kind": "color" + }, + { + "id": "1b2ca9b4625874b6", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--red-100", + "alias_chain": [], + "resolved_value": "oklch(91.50% 0.0387 17.88)", + "occurrences": 3, + "files_count": 1, + "kind": "color" + }, + { + "id": "7ccab36d1f4a7d0d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--red-150", + "alias_chain": [], + "resolved_value": "oklch(87.39% 0.0588 18.34)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "b6a5f7e87724b13d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--red-200", + "alias_chain": [], + "resolved_value": "oklch(83.31% 0.0812 18.93)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "f8eba96d58ae0b0e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--red-250", + "alias_chain": [], + "resolved_value": "oklch(79.44% 0.1024 19.59)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "03fdb444c824c18d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--red-300", + "alias_chain": [], + "resolved_value": "oklch(75.70% 0.1250 20.41)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "9c79d3ca939a18d6", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--red-350", + "alias_chain": [], + "resolved_value": "oklch(71.34% 0.1532 21.68)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "88a7fee6755f4700", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--red-400", + "alias_chain": [], + "resolved_value": "oklch(67.45% 0.1801 23.23)", + "occurrences": 3, + "files_count": 1, + "kind": "color" + }, + { + "id": "77d58db17c203da2", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--red-450", + "alias_chain": [], + "resolved_value": "oklch(63.56% 0.2082 25.38)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "46bb2b3e286635c2", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--red-500", + "alias_chain": [], + "resolved_value": "oklch(59.49% 0.2366 28.55)", + "occurrences": 3, + "files_count": 1, + "kind": "color" + }, + { + "id": "c88204fc2d06eec8", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--red-550", + "alias_chain": [], + "resolved_value": "oklch(55.00% 0.2181 28.49)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "d9fdea0bee0aa827", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--red-600", + "alias_chain": [], + "resolved_value": "oklch(50.42% 0.1991 28.41)", + "occurrences": 6, + "files_count": 1, + "kind": "color" + }, + { + "id": "6477f79067c1bb70", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--red-650", + "alias_chain": [], + "resolved_value": "oklch(45.74% 0.1797 28.31)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "00e03180da0ed95d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--red-700", + "alias_chain": [], + "resolved_value": "oklch(40.94% 0.1597 28.16)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "a419a416f153c517", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--red-750", + "alias_chain": [], + "resolved_value": "oklch(37.10% 0.1441 28.08)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "20a316156043f538", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--red-800", + "alias_chain": [], + "resolved_value": "oklch(31.87% 0.1207 27.60)", + "occurrences": 3, + "files_count": 1, + "kind": "color" + }, + { + "id": "86382046d0108403", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--red-850", + "alias_chain": [], + "resolved_value": "oklch(27.75% 0.1037 27.37)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "a0bd490aa6dea7e1", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--red-900", + "alias_chain": [], + "resolved_value": "oklch(23.39% 0.0850 26.89)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "ab4be16d8cdd3978", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--red-950", + "alias_chain": [], + "resolved_value": "oklch(17.60% 0.0618 26.34)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "e77fee7614e23370", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--red-base", + "alias_chain": ["--red-base", "--red-500"], + "resolved_value": "oklch(59.49% 0.2366 28.55)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "4496de4610464cd9", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--yellow-50", + "alias_chain": [], + "resolved_value": "oklch(95.36% 0.0582 93.11)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "004ab7173e02a801", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--yellow-100", + "alias_chain": [], + "resolved_value": "oklch(91.04% 0.1056 91.66)", + "occurrences": 3, + "files_count": 1, + "kind": "color" + }, + { + "id": "cd55de854e88cf4d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--yellow-150", + "alias_chain": [], + "resolved_value": "oklch(86.57% 0.1493 90.18)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "9ea034350f5db591", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--yellow-200", + "alias_chain": [], + "resolved_value": "oklch(82.83% 0.1680 85.60)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "6b9bb0b6ef25070c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--yellow-250", + "alias_chain": [], + "resolved_value": "oklch(78.58% 0.1598 85.31)", + "occurrences": 3, + "files_count": 1, + "kind": "color" + }, + { + "id": "3b43d82b8b461b83", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--yellow-300", + "alias_chain": [], + "resolved_value": "oklch(73.77% 0.1501 85.30)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "39c686ef900c9e6f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--yellow-350", + "alias_chain": [], + "resolved_value": "oklch(69.72% 0.1415 85.57)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "4a7e28b7e324bf62", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--yellow-400", + "alias_chain": [], + "resolved_value": "oklch(65.31% 0.1324 87.61)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "aa7cb04dba1acfe5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--yellow-450", + "alias_chain": [], + "resolved_value": "oklch(60.56% 0.1226 85.92)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "fcbf75ea78501670", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--yellow-500", + "alias_chain": [], + "resolved_value": "oklch(56.53% 0.1142 86.12)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "0a18a799ba7fc98b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--yellow-550", + "alias_chain": [], + "resolved_value": "oklch(52.43% 0.1058 86.35)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "0753e7856709101c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--yellow-600", + "alias_chain": [], + "resolved_value": "oklch(48.67% 0.0979 88.54)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "390e26edf940086a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--yellow-650", + "alias_chain": [], + "resolved_value": "oklch(44.10% 0.0888 86.90)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "7f846fac15c29865", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--yellow-700", + "alias_chain": [], + "resolved_value": "oklch(39.59% 0.0792 87.47)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "088ca9d45da8fe32", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--yellow-750", + "alias_chain": [], + "resolved_value": "oklch(34.99% 0.0694 88.16)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "2e04dad9b1e2ea3a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--yellow-800", + "alias_chain": [], + "resolved_value": "oklch(30.75% 0.0612 90.61)", + "occurrences": 3, + "files_count": 1, + "kind": "color" + }, + { + "id": "c293ad1e19ec0600", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--yellow-850", + "alias_chain": [], + "resolved_value": "oklch(27.16% 0.0531 89.80)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "3a6e011aed7ab80c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--yellow-900", + "alias_chain": [], + "resolved_value": "oklch(22.28% 0.0437 91.63)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "99ab7e1b84a21207", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--yellow-950", + "alias_chain": [], + "resolved_value": "oklch(16.77% 0.0313 93.87)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "682100fc10b32dcd", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--yellow-base", + "alias_chain": ["--yellow-base", "--yellow-250"], + "resolved_value": "oklch(78.58% 0.1598 85.31)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "79afb66e74f97430", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--green-50", + "alias_chain": [], + "resolved_value": "oklch(94.82% 0.0422 169.29)", + "occurrences": 7, + "files_count": 1, + "kind": "color" + }, + { + "id": "db06f911224dbcbc", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--green-100", + "alias_chain": [], + "resolved_value": "oklch(89.95% 0.0807 168.66)", + "occurrences": 3, + "files_count": 1, + "kind": "color" + }, + { + "id": "b87d98032481e985", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--green-150", + "alias_chain": [], + "resolved_value": "oklch(85.35% 0.1197 167.35)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "d8488edc5b443ec4", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--green-200", + "alias_chain": [], + "resolved_value": "oklch(80.58% 0.1574 162.70)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "40b1f07a5b535fd4", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--green-250", + "alias_chain": [], + "resolved_value": "oklch(76.38% 0.1641 160.63)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "b585ebb2c3d74014", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--green-300", + "alias_chain": [], + "resolved_value": "oklch(72.79% 0.1560 160.70)", + "occurrences": 5, + "files_count": 1, + "kind": "color" + }, + { + "id": "3bc47dfe4a359769", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--green-350", + "alias_chain": [], + "resolved_value": "oklch(67.94% 0.1450 160.80)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "6fdeae956e6853b9", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--green-400", + "alias_chain": [], + "resolved_value": "oklch(64.24% 0.1366 160.89)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "de29af300398bfc7", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--green-450", + "alias_chain": [], + "resolved_value": "oklch(59.23% 0.1253 161.04)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "50c02cfdb9df228f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--green-500", + "alias_chain": [], + "resolved_value": "oklch(55.40% 0.1166 161.17)", + "occurrences": 3, + "files_count": 1, + "kind": "color" + }, + { + "id": "72bb47c9dec975e9", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--green-550", + "alias_chain": [], + "resolved_value": "oklch(50.19% 0.1047 161.37)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "500350346302a4fa", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--green-600", + "alias_chain": [], + "resolved_value": "oklch(47.54% 0.0986 161.50)", + "occurrences": 5, + "files_count": 1, + "kind": "color" + }, + { + "id": "71ca4c1660c5dcbf", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--green-650", + "alias_chain": [], + "resolved_value": "oklch(42.12% 0.0861 161.81)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "0855b0dca1ae5b1d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--green-700", + "alias_chain": [], + "resolved_value": "oklch(38.09% 0.0761 163.02)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "b0652df72ff2512f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--green-750", + "alias_chain": [], + "resolved_value": "oklch(33.77% 0.0661 163.42)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "e6b19c20ebbb1122", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--green-800", + "alias_chain": [], + "resolved_value": "oklch(30.79% 0.0600 162.74)", + "occurrences": 3, + "files_count": 1, + "kind": "color" + }, + { + "id": "599767eca1f0e256", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--green-850", + "alias_chain": [], + "resolved_value": "oklch(26.22% 0.0492 163.52)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "3ba2d6db8812744b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--green-900", + "alias_chain": [], + "resolved_value": "oklch(21.43% 0.0378 164.98)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "d47f89f143d66e62", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--green-950", + "alias_chain": [], + "resolved_value": "oklch(16.38% 0.0261 169.50)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "e591026474e41cc1", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--green-base", + "alias_chain": ["--green-base", "--green-300"], + "resolved_value": "oklch(72.79% 0.1560 160.70)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "50c27f29a7e67e36", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--blue-50", + "alias_chain": [], + "resolved_value": "oklch(96.09% 0.0180 286.06)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "867112d1ec16c98a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--blue-100", + "alias_chain": [], + "resolved_value": "oklch(91.95% 0.0390 285.61)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "85212d13f48c7c46", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--blue-150", + "alias_chain": [], + "resolved_value": "oklch(86.69% 0.0647 284.97)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "f72cabd719eaa35c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--blue-200", + "alias_chain": [], + "resolved_value": "oklch(82.77% 0.0843 284.41)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "a42e28318e656cbd", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--blue-250", + "alias_chain": [], + "resolved_value": "oklch(78.90% 0.1038 283.77)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "548cbced2843c37e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--blue-300", + "alias_chain": [], + "resolved_value": "oklch(74.76% 0.1273 282.88)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "8052f2fbf183faa9", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--blue-350", + "alias_chain": [], + "resolved_value": "oklch(70.96% 0.1471 281.99)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "99dd17f9f730ecdc", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--blue-400", + "alias_chain": [], + "resolved_value": "oklch(67.12% 0.1682 280.87)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "7483dbe588331e51", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--blue-450", + "alias_chain": [], + "resolved_value": "oklch(62.01% 0.1980 278.94)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "0bceace16b66f10f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--blue-500", + "alias_chain": [], + "resolved_value": "oklch(58.57% 0.2160 277.40)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "5846ef80c7adb896", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--blue-550", + "alias_chain": [], + "resolved_value": "oklch(54.14% 0.2411 274.78)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "42dd2679355b51be", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--blue-600", + "alias_chain": [], + "resolved_value": "oklch(50.25% 0.2633 271.74)", + "occurrences": 3, + "files_count": 1, + "kind": "color" + }, + { + "id": "0b625e74d328ca0c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--blue-650", + "alias_chain": [], + "resolved_value": "oklch(46.47% 0.2854 267.82)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "42b5a0553e1d7172", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--blue-700", + "alias_chain": [], + "resolved_value": "oklch(42.24% 0.2846 265.00)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "e6c89af29f461958", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--blue-750", + "alias_chain": [], + "resolved_value": "oklch(37.54% 0.2514 265.20)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "a4bdefd82ad9b235", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--blue-800", + "alias_chain": [], + "resolved_value": "oklch(33.39% 0.2220 265.44)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "3c0771d4d7f99feb", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--blue-850", + "alias_chain": [], + "resolved_value": "oklch(28.39% 0.1864 265.84)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "22253d353e1b944a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--blue-900", + "alias_chain": [], + "resolved_value": "oklch(23.93% 0.1544 266.37)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "b4f659fb902fcd97", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--blue-950", + "alias_chain": [], + "resolved_value": "oklch(18.40% 0.1160 267.07)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "736a1db8c64e67ab", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--blue-base", + "alias_chain": ["--blue-base", "--blue-600"], + "resolved_value": "oklch(50.25% 0.2633 271.74)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "b8f924861e1d6419", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--purple-50", + "alias_chain": [], + "resolved_value": "oklch(95.51% 0.0262 309.51)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "6290e1a182c9cabe", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--purple-100", + "alias_chain": [], + "resolved_value": "oklch(92.24% 0.0466 310.65)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "bc736f3e4e099910", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--purple-150", + "alias_chain": [], + "resolved_value": "oklch(87.78% 0.0736 309.66)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "86805cfa5b619e79", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--purple-200", + "alias_chain": [], + "resolved_value": "oklch(83.40% 0.1012 309.30)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "d04e37ed137c36d7", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--purple-250", + "alias_chain": [], + "resolved_value": "oklch(80.06% 0.1246 308.96)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "15bf7adfc2fb28cb", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--purple-300", + "alias_chain": [], + "resolved_value": "oklch(75.82% 0.1525 308.47)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "f6c041e8dc8f3875", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--purple-350", + "alias_chain": [], + "resolved_value": "oklch(71.72% 0.1797 307.88)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "38680b8bf42dbd0a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--purple-400", + "alias_chain": [], + "resolved_value": "oklch(67.81% 0.2055 307.15)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "009e924b64735a0c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--purple-450", + "alias_chain": [], + "resolved_value": "oklch(63.28% 0.2344 305.98)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "bec997db3a67f348", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--purple-500", + "alias_chain": [], + "resolved_value": "oklch(60.06% 0.2517 304.85)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "61d44f649bc4f215", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--purple-550", + "alias_chain": [], + "resolved_value": "oklch(55.95% 0.2723 302.42)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "87ce370f676de9e5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--purple-600", + "alias_chain": [], + "resolved_value": "oklch(51.81% 0.2718 299.74)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "40d576c8c90abe00", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--purple-650", + "alias_chain": [], + "resolved_value": "oklch(47.08% 0.2467 299.80)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "c1104869ddc52a0f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--purple-700", + "alias_chain": [], + "resolved_value": "oklch(42.06% 0.2184 300.28)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "6c1a4704ed1f5922", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--purple-750", + "alias_chain": [], + "resolved_value": "oklch(37.89% 0.1962 300.45)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "33177d22d328b905", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--purple-800", + "alias_chain": [], + "resolved_value": "oklch(32.65% 0.1672 301.01)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "798b59acf65f818c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--purple-850", + "alias_chain": [], + "resolved_value": "oklch(28.09% 0.1411 301.82)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "a702915f6668da2f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--purple-900", + "alias_chain": [], + "resolved_value": "oklch(23.39% 0.1148 302.86)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "cea9fcab7a6694b4", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--purple-950", + "alias_chain": [], + "resolved_value": "oklch(18.39% 0.0887 304.57)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "0ef918e4511dcdb4", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--purple-base", + "alias_chain": [], + "resolved_value": "oklch(65.08% 0.2214 306.55)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "b363db3bc6e3d614", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cashapp-50", + "alias_chain": [], + "resolved_value": "oklch(97.5% 0.065 145.0)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "46ade285a1457f23", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cashapp-100", + "alias_chain": [], + "resolved_value": "oklch(93.5% 0.215 143.5)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "0ca949fc522c5814", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cashapp-150", + "alias_chain": [], + "resolved_value": "oklch(88.0% 0.315 142.5)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "3b2b9d11b6ba8d02", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cashapp-200", + "alias_chain": [], + "resolved_value": "oklch(85.0% 0.302 142.3)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "55e9e68b2acd7f9c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cashapp-250", + "alias_chain": [], + "resolved_value": "oklch(81.5% 0.287 142.1)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "1b816005614fde6b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cashapp-300", + "alias_chain": [], + "resolved_value": "oklch(77.5% 0.269 141.9)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "0995398ff309e255", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cashapp-350", + "alias_chain": [], + "resolved_value": "oklch(73.0% 0.251 141.7)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "cbbac3c88957175d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cashapp-400", + "alias_chain": [], + "resolved_value": "oklch(69.0% 0.233 141.6)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "13c9b5117b004a24", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cashapp-450", + "alias_chain": [], + "resolved_value": "oklch(64.5% 0.214 141.4)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "bcf073f78db401e1", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cashapp-500", + "alias_chain": [], + "resolved_value": "oklch(60.0% 0.195 141.2)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "f39cd7a55eddd1ac", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cashapp-550", + "alias_chain": [], + "resolved_value": "oklch(55.0% 0.176 141.1)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "f1d505336e9d80d4", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cashapp-600", + "alias_chain": [], + "resolved_value": "oklch(49.5% 0.156 140.9)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "3a8781ae77fff5d3", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cashapp-650", + "alias_chain": [], + "resolved_value": "oklch(44.5% 0.137 140.7)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "c6462ea932664fa5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cashapp-700", + "alias_chain": [], + "resolved_value": "oklch(39.0% 0.117 140.5)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "bd8b3f167dae2caa", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cashapp-750", + "alias_chain": [], + "resolved_value": "oklch(34.0% 0.098 140.3)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "fbef2188e8173eb7", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cashapp-800", + "alias_chain": [], + "resolved_value": "oklch(28.5% 0.079 140.1)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "1b955967b8e44bf2", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cashapp-850", + "alias_chain": [], + "resolved_value": "oklch(23.5% 0.061 139.9)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "3f6608e5a27d1129", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cashapp-900", + "alias_chain": [], + "resolved_value": "oklch(18.0% 0.044 139.7)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "b768e518441fc578", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cashapp-950", + "alias_chain": [], + "resolved_value": "oklch(12.5% 0.028 139.5)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "348dd9d581a389f1", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--square-50", + "alias_chain": [], + "resolved_value": "oklch(96.0% 0.034 264.5)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "93b327da4262dd2e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--square-100", + "alias_chain": [], + "resolved_value": "oklch(91.5% 0.060 264.3)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "b990bc790e0ed1c3", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--square-150", + "alias_chain": [], + "resolved_value": "oklch(87.0% 0.087 264.2)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "f155dbe26820be97", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--square-200", + "alias_chain": [], + "resolved_value": "oklch(82.5% 0.111 264.1)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "03f937869ef0c08f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--square-250", + "alias_chain": [], + "resolved_value": "oklch(78.0% 0.133 264.0)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "4a52d68de338f672", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--square-300", + "alias_chain": [], + "resolved_value": "oklch(73.5% 0.153 263.9)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "208624625160f5fb", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--square-350", + "alias_chain": [], + "resolved_value": "oklch(69.0% 0.176 263.8)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "38970e13d3ba92cf", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--square-400", + "alias_chain": [], + "resolved_value": "oklch(65.0% 0.204 263.7)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "f55b80f3ea42602e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--square-450", + "alias_chain": [], + "resolved_value": "oklch(61.0% 0.234 263.6)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "d97da4a03e106145", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--square-500", + "alias_chain": [], + "resolved_value": "oklch(57.0% 0.263 263.5)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "c2a2c176b6b0bfd3", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--square-550", + "alias_chain": [], + "resolved_value": "oklch(51.5% 0.250 263.4)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "cc0a3393f1fc9d76", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--square-600", + "alias_chain": [], + "resolved_value": "oklch(46.5% 0.236 263.3)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "2cfbc944f833db2b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--square-650", + "alias_chain": [], + "resolved_value": "oklch(41.0% 0.220 263.2)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "fce5c2b090825417", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--square-700", + "alias_chain": [], + "resolved_value": "oklch(35.5% 0.202 263.1)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "0f890f479a438801", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--square-750", + "alias_chain": [], + "resolved_value": "oklch(30.5% 0.182 263.0)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "84748718d58e5fa3", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--square-800", + "alias_chain": [], + "resolved_value": "oklch(25.0% 0.160 262.9)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "b6ff4859e56bb9ee", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--square-850", + "alias_chain": [], + "resolved_value": "oklch(20.0% 0.126 262.8)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "8895bd773c618c22", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--square-900", + "alias_chain": [], + "resolved_value": "oklch(14.5% 0.092 262.7)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "59fd976f4adef5b2", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--square-950", + "alias_chain": [], + "resolved_value": "oklch(9.0% 0.057 262.6)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "de1bf3a6d7f72085", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--slate-50", + "alias_chain": [], + "resolved_value": "oklch(98.4% 0.003 247.858)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "2e74459c9ce24cc2", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--slate-100", + "alias_chain": [], + "resolved_value": "oklch(96.8% 0.007 247.896)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "91bb25836a9b5de9", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--slate-200", + "alias_chain": [], + "resolved_value": "oklch(92.9% 0.013 255.508)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "9719e0912b590fa1", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--slate-300", + "alias_chain": [], + "resolved_value": "oklch(86.9% 0.022 252.894)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "604b03f2bd82cd19", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--slate-400", + "alias_chain": [], + "resolved_value": "oklch(70.4% 0.04 256.788)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "45616545d30b8ffa", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--slate-500", + "alias_chain": [], + "resolved_value": "oklch(55.4% 0.046 257.417)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "7a2530a0fe26a7ed", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--slate-600", + "alias_chain": [], + "resolved_value": "oklch(44.6% 0.043 257.281)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "5d746c5677efa06c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--slate-700", + "alias_chain": [], + "resolved_value": "oklch(37.2% 0.044 257.287)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "d99cf2b2c13e1daf", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--slate-800", + "alias_chain": [], + "resolved_value": "oklch(27.9% 0.041 260.031)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "11b11ff074eb5128", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--slate-900", + "alias_chain": [], + "resolved_value": "oklch(20.8% 0.042 265.755)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "3b063c594efd0975", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--slate-950", + "alias_chain": [], + "resolved_value": "oklch(12.9% 0.042 264.695)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "9b6f5703b29a8956", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--gray-50", + "alias_chain": [], + "resolved_value": "oklch(98.5% 0.002 247.839)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "c8bbd043cc87b1fc", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--gray-100", + "alias_chain": [], + "resolved_value": "oklch(96.7% 0.003 264.542)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "b1d1c078805e4d8e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--gray-200", + "alias_chain": [], + "resolved_value": "oklch(92.8% 0.006 264.531)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "154aebb1481607bd", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--gray-300", + "alias_chain": [], + "resolved_value": "oklch(87.2% 0.01 258.338)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "e613ffc6e778c34a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--gray-400", + "alias_chain": [], + "resolved_value": "oklch(70.7% 0.022 261.325)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "cdcad96e2e44d397", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--gray-500", + "alias_chain": [], + "resolved_value": "oklch(55.1% 0.023 264.364)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "09f8cad3ca95c011", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--gray-600", + "alias_chain": [], + "resolved_value": "oklch(44.6% 0.02 264.533)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "9b6944507afb481f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--gray-700", + "alias_chain": [], + "resolved_value": "oklch(37.3% 0.034 259.733)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "2d77543ff853b978", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--gray-800", + "alias_chain": [], + "resolved_value": "oklch(27.8% 0.033 256.848)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "8266b3d843a91136", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--gray-900", + "alias_chain": [], + "resolved_value": "oklch(21.0% 0.034 264.665)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "2199b5c7a9b87a39", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--gray-950", + "alias_chain": [], + "resolved_value": "oklch(13.0% 0.028 261.692)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "423671fbdc0d8747", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--zinc-50", + "alias_chain": [], + "resolved_value": "oklch(98.5% 0 0)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "96f24bfd3058c8c7", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--zinc-100", + "alias_chain": [], + "resolved_value": "oklch(96.7% 0.001 286.375)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "7eea15f7c4b083a5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--zinc-200", + "alias_chain": [], + "resolved_value": "oklch(92.0% 0.004 286.32)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "7c65b5c89498d928", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--zinc-300", + "alias_chain": [], + "resolved_value": "oklch(87.1% 0.005 286.286)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "f6cb7f8e73d19a3a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--zinc-400", + "alias_chain": [], + "resolved_value": "oklch(70.5% 0.015 286.067)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "f41691ab3baab1bd", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--zinc-500", + "alias_chain": [], + "resolved_value": "oklch(55.2% 0.016 285.786)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "46c59283234d2192", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--zinc-600", + "alias_chain": [], + "resolved_value": "oklch(44.2% 0.013 285.786)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "df5e84a4d384a179", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--zinc-700", + "alias_chain": [], + "resolved_value": "oklch(37.0% 0.013 285.805)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "6d2304558b884a7d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--zinc-800", + "alias_chain": [], + "resolved_value": "oklch(27.4% 0.006 286.618)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "457a7b047e40982c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--zinc-900", + "alias_chain": [], + "resolved_value": "oklch(21.0% 0 0)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "d4754687c50ace9f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--zinc-950", + "alias_chain": [], + "resolved_value": "oklch(14.5% 0 0)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "8f515da2446b4746", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--stone-50", + "alias_chain": [], + "resolved_value": "oklch(98.5% 0.001 106.423)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "c236cd29462bf524", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--stone-100", + "alias_chain": [], + "resolved_value": "oklch(97.0% 0.001 106.424)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "23334ccba80e433d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--stone-200", + "alias_chain": [], + "resolved_value": "oklch(92.3% 0.003 48.717)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "4d0c967c9b48e9ca", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--stone-300", + "alias_chain": [], + "resolved_value": "oklch(86.9% 0.005 56.366)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "a7c3aca7e1e84717", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--stone-400", + "alias_chain": [], + "resolved_value": "oklch(70.9% 0.01 56.259)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "5549213a5c092c4d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--stone-500", + "alias_chain": [], + "resolved_value": "oklch(55.3% 0.014 58.071)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "695be851fcdd3bab", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--stone-600", + "alias_chain": [], + "resolved_value": "oklch(44.4% 0.011 73.638)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "aafffdaa9b4bf7d2", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--stone-700", + "alias_chain": [], + "resolved_value": "oklch(37.4% 0.01 67.558)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "bfec55cbfaa45246", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--stone-800", + "alias_chain": [], + "resolved_value": "oklch(26.8% 0.007 34.298)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "0434d7f49f860d47", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--stone-900", + "alias_chain": [], + "resolved_value": "oklch(21.6% 0.006 56.044)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "e71f7a881ea77d39", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--stone-950", + "alias_chain": [], + "resolved_value": "oklch(13.1% 0.004 285.938)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "f51c44ebb6be44a5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--rose-50", + "alias_chain": [], + "resolved_value": "oklch(96.9% 0.015 12.422)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "c3b1babc7c379016", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--rose-100", + "alias_chain": [], + "resolved_value": "oklch(94.1% 0.03 12.58)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "983755020ffdaa80", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--rose-200", + "alias_chain": [], + "resolved_value": "oklch(89.2% 0.058 10.001)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "1436c2a2c9fc40b1", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--rose-300", + "alias_chain": [], + "resolved_value": "oklch(81.0% 0.117 11.638)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "34798dee8609bd5e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--rose-400", + "alias_chain": [], + "resolved_value": "oklch(71.2% 0.194 13.428)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "02d76c5cc44826d4", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--rose-500", + "alias_chain": [], + "resolved_value": "oklch(64.5% 0.246 16.439)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "c41e3ce8fdee5624", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--rose-600", + "alias_chain": [], + "resolved_value": "oklch(58.6% 0.253 17.585)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "ba25bb41e18e9c1b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--rose-700", + "alias_chain": [], + "resolved_value": "oklch(51.4% 0.222 16.935)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "450eb397545cde46", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--rose-800", + "alias_chain": [], + "resolved_value": "oklch(43.6% 0.177 15.304)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "370db02f6a40b5e5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--rose-900", + "alias_chain": [], + "resolved_value": "oklch(41.0% 0.159 10.272)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "45b3cd2d5b17e824", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--rose-950", + "alias_chain": [], + "resolved_value": "oklch(27.1% 0.105 12.094)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "bd60a90c2d6604bd", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--orange-50", + "alias_chain": [], + "resolved_value": "oklch(98.0% 0.016 73.684)", + "occurrences": 7, + "files_count": 1, + "kind": "color" + }, + { + "id": "80fb39eeb6a6e979", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--orange-100", + "alias_chain": [], + "resolved_value": "oklch(95.4% 0.038 75.164)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "90133d37c6af6c74", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--orange-200", + "alias_chain": [], + "resolved_value": "oklch(90.0% 0.076 70.697)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "cfb86e0025ea7f8b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--orange-300", + "alias_chain": [], + "resolved_value": "oklch(83.7% 0.128 66.29)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "1d95035be7e3e034", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--orange-400", + "alias_chain": [], + "resolved_value": "oklch(75.0% 0.183 55.934)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "103b8bd7658ad1f6", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--orange-500", + "alias_chain": [], + "resolved_value": "oklch(70.5% 0.213 47.604)", + "occurrences": 5, + "files_count": 1, + "kind": "color" + }, + { + "id": "c562239e60bc3d32", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--orange-600", + "alias_chain": [], + "resolved_value": "oklch(64.6% 0.222 41.116)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "8f9bfae557b95642", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--orange-700", + "alias_chain": [], + "resolved_value": "oklch(55.8% 0.227 37.304)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "44d3a9a906944d61", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--orange-800", + "alias_chain": [], + "resolved_value": "oklch(47.0% 0.157 37.304)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "511925f52daedbda", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--orange-900", + "alias_chain": [], + "resolved_value": "oklch(37.7% 0.127 29.234)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "26cf24cb57c7cceb", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--orange-950", + "alias_chain": [], + "resolved_value": "oklch(26.6% 0.079 36.259)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "2a87cf2b9350e95d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--amber-50", + "alias_chain": [], + "resolved_value": "oklch(98.7% 0.022 95.277)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "83b1da6b929d23a2", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--amber-100", + "alias_chain": [], + "resolved_value": "oklch(96.2% 0.059 95.617)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "da48fa35b58ed4c4", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--amber-200", + "alias_chain": [], + "resolved_value": "oklch(92.5% 0.096 101.664)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "baafff471d9a29f8", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--amber-300", + "alias_chain": [], + "resolved_value": "oklch(87.9% 0.169 91.605)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "7c3d53fa188e5555", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--amber-400", + "alias_chain": [], + "resolved_value": "oklch(82.8% 0.189 84.429)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "af348c749a9a2e6a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--amber-500", + "alias_chain": [], + "resolved_value": "oklch(76.9% 0.188 70.08)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "914871f26bb6da12", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--amber-600", + "alias_chain": [], + "resolved_value": "oklch(66.6% 0.179 58.318)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "14641200b7537596", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--amber-700", + "alias_chain": [], + "resolved_value": "oklch(55.5% 0.163 48.998)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "cc8dad193af6acd0", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--amber-800", + "alias_chain": [], + "resolved_value": "oklch(47.3% 0.137 46.201)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "44679821f13b37c3", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--amber-900", + "alias_chain": [], + "resolved_value": "oklch(41.4% 0.112 45.904)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "5d1fba81106d4750", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--amber-950", + "alias_chain": [], + "resolved_value": "oklch(27.9% 0.077 45.635)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "8b498e3bb8e4f44f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--lime-50", + "alias_chain": [], + "resolved_value": "oklch(98.6% 0.031 120.757)", + "occurrences": 6, + "files_count": 1, + "kind": "color" + }, + { + "id": "316a729a02f9c23d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--lime-100", + "alias_chain": [], + "resolved_value": "oklch(96.7% 0.067 122.328)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "4e99c5d421616c80", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--lime-200", + "alias_chain": [], + "resolved_value": "oklch(93.8% 0.127 124.321)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "401f458462c71ad7", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--lime-300", + "alias_chain": [], + "resolved_value": "oklch(89.7% 0.196 126.665)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "e10419ea62b84572", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--lime-400", + "alias_chain": [], + "resolved_value": "oklch(84.1% 0.238 128.85)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "acc8387f002102e9", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--lime-500", + "alias_chain": [], + "resolved_value": "oklch(76.8% 0.233 130.85)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "09e67d9da196d90d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--lime-600", + "alias_chain": [], + "resolved_value": "oklch(64.8% 0.2 131.684)", + "occurrences": 5, + "files_count": 1, + "kind": "color" + }, + { + "id": "d34ce0af7d2231c6", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--lime-700", + "alias_chain": [], + "resolved_value": "oklch(53.2% 0.157 131.589)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "3d865c66b2cf6cb7", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--lime-800", + "alias_chain": [], + "resolved_value": "oklch(45.3% 0.124 130.933)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "9ec30a9265f16d58", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--lime-900", + "alias_chain": [], + "resolved_value": "oklch(40.5% 0.101 131.063)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "14a8de4f97ae2afe", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--lime-950", + "alias_chain": [], + "resolved_value": "oklch(27.4% 0.072 132.109)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "f45fe9f5132e04f9", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--emerald-50", + "alias_chain": [], + "resolved_value": "oklch(97.9% 0.021 166.113)", + "occurrences": 5, + "files_count": 1, + "kind": "color" + }, + { + "id": "7ecd342a18fbd164", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--emerald-100", + "alias_chain": [], + "resolved_value": "oklch(95.0% 0.052 163.051)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "b5bc0453276ad4a8", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--emerald-200", + "alias_chain": [], + "resolved_value": "oklch(90.5% 0.093 164.15)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "ae3adca761a89611", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--emerald-300", + "alias_chain": [], + "resolved_value": "oklch(84.5% 0.143 164.978)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "c8f32daba84c0965", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--emerald-400", + "alias_chain": [], + "resolved_value": "oklch(76.5% 0.177 163.223)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "75b26930f1186016", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--emerald-500", + "alias_chain": [], + "resolved_value": "oklch(69.6% 0.17 162.48)", + "occurrences": 3, + "files_count": 1, + "kind": "color" + }, + { + "id": "53b05567bb4964e5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--emerald-600", + "alias_chain": [], + "resolved_value": "oklch(59.6% 0.145 163.225)", + "occurrences": 3, + "files_count": 1, + "kind": "color" + }, + { + "id": "a5851c245fa727fb", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--emerald-700", + "alias_chain": [], + "resolved_value": "oklch(50.8% 0.127 165.612)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "5090e553c870a570", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--emerald-800", + "alias_chain": [], + "resolved_value": "oklch(43.2% 0.095 166.913)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "894a7ed01cbe8577", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--emerald-900", + "alias_chain": [], + "resolved_value": "oklch(37.8% 0.077 168.94)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "901374fbdb1ca3f3", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--emerald-950", + "alias_chain": [], + "resolved_value": "oklch(26.2% 0.051 172.552)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "470dc70cb8dc5402", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--teal-50", + "alias_chain": [], + "resolved_value": "oklch(98.4% 0.014 180.72)", + "occurrences": 5, + "files_count": 1, + "kind": "color" + }, + { + "id": "51b33752ef7fd3cf", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--teal-100", + "alias_chain": [], + "resolved_value": "oklch(95.3% 0.05 180.801)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "044d11624e879e75", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--teal-200", + "alias_chain": [], + "resolved_value": "oklch(91.0% 0.096 180.426)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "02f3406e2d03adfa", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--teal-300", + "alias_chain": [], + "resolved_value": "oklch(85.5% 0.138 181.071)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "14beafb7c1ee5f95", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--teal-400", + "alias_chain": [], + "resolved_value": "oklch(77.7% 0.152 181.912)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "938070bec55058a2", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--teal-500", + "alias_chain": [], + "resolved_value": "oklch(70.4% 0.14 182.503)", + "occurrences": 3, + "files_count": 1, + "kind": "color" + }, + { + "id": "69be77b77c79299c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--teal-600", + "alias_chain": [], + "resolved_value": "oklch(60.0% 0.118 184.704)", + "occurrences": 3, + "files_count": 1, + "kind": "color" + }, + { + "id": "5486ecef8326aca7", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--teal-700", + "alias_chain": [], + "resolved_value": "oklch(51.1% 0.096 186.391)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "2c19b4c477e7964e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--teal-800", + "alias_chain": [], + "resolved_value": "oklch(43.7% 0.078 188.216)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "b0519ef70457c543", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--teal-900", + "alias_chain": [], + "resolved_value": "oklch(39.4% 0.068 188.416)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "c6440937d77267e2", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--teal-950", + "alias_chain": [], + "resolved_value": "oklch(27.7% 0.046 192.524)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "1e012c28a03d0c7f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cyan-50", + "alias_chain": [], + "resolved_value": "oklch(98.4% 0.019 200.873)", + "occurrences": 5, + "files_count": 1, + "kind": "color" + }, + { + "id": "28827cbe7fbe1618", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cyan-100", + "alias_chain": [], + "resolved_value": "oklch(95.6% 0.045 203.388)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "c26f7f8f04feb8a4", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cyan-200", + "alias_chain": [], + "resolved_value": "oklch(91.7% 0.08 205.041)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "5f5daf5d64bdc1b9", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cyan-300", + "alias_chain": [], + "resolved_value": "oklch(86.5% 0.127 207.078)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "e6c4633dadad9f34", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cyan-400", + "alias_chain": [], + "resolved_value": "oklch(78.9% 0.154 211.53)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "bdcdca7f8efde8ca", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cyan-500", + "alias_chain": [], + "resolved_value": "oklch(71.5% 0.143 215.221)", + "occurrences": 3, + "files_count": 1, + "kind": "color" + }, + { + "id": "91b35e0d5ad31dd5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cyan-600", + "alias_chain": [], + "resolved_value": "oklch(60.9% 0.126 221.723)", + "occurrences": 3, + "files_count": 1, + "kind": "color" + }, + { + "id": "599f738e51bfe3ce", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cyan-700", + "alias_chain": [], + "resolved_value": "oklch(52.0% 0.105 223.128)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "627cbe33bc960a09", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cyan-800", + "alias_chain": [], + "resolved_value": "oklch(45.0% 0.085 224.283)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "7982e40a7bda08bb", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cyan-900", + "alias_chain": [], + "resolved_value": "oklch(39.8% 0.07 227.392)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "3ac58501f3bc8f13", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--cyan-950", + "alias_chain": [], + "resolved_value": "oklch(30.2% 0.056 229.695)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "4e4c272387b88949", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--sky-50", + "alias_chain": [], + "resolved_value": "oklch(97.7% 0.013 236.62)", + "occurrences": 5, + "files_count": 1, + "kind": "color" + }, + { + "id": "6e9d852962cdcc4c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--sky-100", + "alias_chain": [], + "resolved_value": "oklch(95.1% 0.026 236.824)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "fe802c3456a62891", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--sky-200", + "alias_chain": [], + "resolved_value": "oklch(90.1% 0.058 230.902)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "21b7adc1ed4d3f58", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--sky-300", + "alias_chain": [], + "resolved_value": "oklch(82.8% 0.111 230.318)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "8de1c7039c6a7450", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--sky-400", + "alias_chain": [], + "resolved_value": "oklch(74.6% 0.16 232.661)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "e8e0148a14a6e41e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--sky-500", + "alias_chain": [], + "resolved_value": "oklch(68.5% 0.169 237.323)", + "occurrences": 3, + "files_count": 1, + "kind": "color" + }, + { + "id": "4d2637acdd768cd2", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--sky-600", + "alias_chain": [], + "resolved_value": "oklch(58.8% 0.158 241.966)", + "occurrences": 3, + "files_count": 1, + "kind": "color" + }, + { + "id": "a7aa63c1b88c8bd8", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--sky-700", + "alias_chain": [], + "resolved_value": "oklch(50.0% 0.134 242.749)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "e00bbcb04caf4553", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--sky-800", + "alias_chain": [], + "resolved_value": "oklch(44.3% 0.11 240.79)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "c4ce7570f25df2f2", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--sky-900", + "alias_chain": [], + "resolved_value": "oklch(39.1% 0.09 240.876)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "46dcb4573bfccbc3", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--sky-950", + "alias_chain": [], + "resolved_value": "oklch(29.3% 0.066 243.157)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "88a4b24ff45ad790", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--indigo-50", + "alias_chain": [], + "resolved_value": "oklch(96.2% 0.018 272.314)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "262e4fa9a383c087", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--indigo-100", + "alias_chain": [], + "resolved_value": "oklch(93.0% 0.034 272.788)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "3c11dea43b7194d4", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--indigo-200", + "alias_chain": [], + "resolved_value": "oklch(87.0% 0.065 274.039)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "9db03f5fe8a975b4", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--indigo-300", + "alias_chain": [], + "resolved_value": "oklch(78.5% 0.115 274.713)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "4d06fba65438b467", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--indigo-400", + "alias_chain": [], + "resolved_value": "oklch(67.3% 0.182 276.935)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "2eb2e9fd0d7b6db8", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--indigo-500", + "alias_chain": [], + "resolved_value": "oklch(58.5% 0.233 277.117)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "b1738052e67bbe41", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--indigo-600", + "alias_chain": [], + "resolved_value": "oklch(51.1% 0.262 276.966)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "4319f3de31530588", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--indigo-700", + "alias_chain": [], + "resolved_value": "oklch(45.7% 0.24 277.023)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "9540190a948757dc", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--indigo-800", + "alias_chain": [], + "resolved_value": "oklch(39.8% 0.195 277.366)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "907f707730854737", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--indigo-900", + "alias_chain": [], + "resolved_value": "oklch(35.9% 0.144 278.697)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "cde2752e065e3410", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--indigo-950", + "alias_chain": [], + "resolved_value": "oklch(25.7% 0.09 281.288)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "68c2c907c9f2a73a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--violet-50", + "alias_chain": [], + "resolved_value": "oklch(96.9% 0.016 293.756)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "3607062f12ece357", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--violet-100", + "alias_chain": [], + "resolved_value": "oklch(94.3% 0.029 294.588)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "0bbc3207b1441cef", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--violet-200", + "alias_chain": [], + "resolved_value": "oklch(89.4% 0.057 293.283)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "8de754097d842c42", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--violet-300", + "alias_chain": [], + "resolved_value": "oklch(81.1% 0.111 293.571)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "8933bd0a5d0bce06", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--violet-400", + "alias_chain": [], + "resolved_value": "oklch(70.2% 0.183 293.541)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "405ddc2f7da5eade", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--violet-500", + "alias_chain": [], + "resolved_value": "oklch(60.6% 0.25 292.717)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "0a0f3cbccaa9b877", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--violet-600", + "alias_chain": [], + "resolved_value": "oklch(54.1% 0.281 293.009)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "92090a95856776a8", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--violet-700", + "alias_chain": [], + "resolved_value": "oklch(49.1% 0.27 292.581)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "9f42441dd9634e90", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--violet-800", + "alias_chain": [], + "resolved_value": "oklch(43.2% 0.232 292.759)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "6d875c20d9816766", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--violet-900", + "alias_chain": [], + "resolved_value": "oklch(38.0% 0.189 293.745)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "188e39beb1553348", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--violet-950", + "alias_chain": [], + "resolved_value": "oklch(28.3% 0.141 291.089)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "c27e94f8ed1013a6", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--fuchsia-50", + "alias_chain": [], + "resolved_value": "oklch(97.7% 0.017 320.058)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "edc866e426c5acb0", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--fuchsia-100", + "alias_chain": [], + "resolved_value": "oklch(95.2% 0.037 318.852)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "777814f597a25975", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--fuchsia-200", + "alias_chain": [], + "resolved_value": "oklch(90.3% 0.076 319.562)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "22f92c65c01ab5c5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--fuchsia-300", + "alias_chain": [], + "resolved_value": "oklch(83.3% 0.145 321.434)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "f6cb4faf98a4a58b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--fuchsia-400", + "alias_chain": [], + "resolved_value": "oklch(74.0% 0.238 322.16)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "c153c5c816f849c7", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--fuchsia-500", + "alias_chain": [], + "resolved_value": "oklch(66.7% 0.295 322.15)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "d7dd0c5674b2231e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--fuchsia-600", + "alias_chain": [], + "resolved_value": "oklch(59.1% 0.293 322.896)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "f01225b8352378b8", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--fuchsia-700", + "alias_chain": [], + "resolved_value": "oklch(52.4% 0.253 323.046)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "7f921415571a2fe3", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--fuchsia-800", + "alias_chain": [], + "resolved_value": "oklch(45.2% 0.211 324.591)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "410a6494566e5b55", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--fuchsia-900", + "alias_chain": [], + "resolved_value": "oklch(40.2% 0.159 325.612)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "76907713164f6752", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--fuchsia-950", + "alias_chain": [], + "resolved_value": "oklch(29.6% 0.113 325.612)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "7ca6c9d50428449a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--pink-50", + "alias_chain": [], + "resolved_value": "oklch(97.1% 0.014 343.198)", + "occurrences": 4, + "files_count": 1, + "kind": "color" + }, + { + "id": "1d3cb7ba850a83e9", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--pink-100", + "alias_chain": [], + "resolved_value": "oklch(94.8% 0.028 342.258)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "de45fdf0d6746336", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--pink-200", + "alias_chain": [], + "resolved_value": "oklch(90.9% 0.058 343.231)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "58f784e89449984a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--pink-300", + "alias_chain": [], + "resolved_value": "oklch(82.3% 0.12 346.018)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "915acbc02faeaa65", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--pink-400", + "alias_chain": [], + "resolved_value": "oklch(71.8% 0.202 349.761)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "1935a8853098a4f5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--pink-500", + "alias_chain": [], + "resolved_value": "oklch(65.6% 0.241 354.308)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "32b5b81e7f7bbc1d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--pink-600", + "alias_chain": [], + "resolved_value": "oklch(59.2% 0.249 0.584)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "a2d87744063661c7", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--pink-700", + "alias_chain": [], + "resolved_value": "oklch(52.5% 0.223 3.958)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "43b72026515448bb", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--pink-800", + "alias_chain": [], + "resolved_value": "oklch(45.1% 0.187 3.815)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "cdf1102a8260a55a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--pink-900", + "alias_chain": [], + "resolved_value": "oklch(40.8% 0.153 2.432)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "12735ec08c34c33a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--pink-950", + "alias_chain": [], + "resolved_value": "oklch(29.1% 0.109 3.907)", + "occurrences": 2, + "files_count": 1, + "kind": "color" + }, + { + "id": "a34870bb2614a794", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--page-x-padding", + "alias_chain": [], + "resolved_value": "4px", + "occurrences": 5, + "files_count": 1, + "kind": "spacing" + }, + { + "id": "de2d24ff4d745af5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--background", + "alias_chain": ["--background", "--white"], + "resolved_value": "oklch(100.00% 0.0000 89.88)", + "occurrences": 4, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--white)", + "dark": "var(--neutral-900)", + "force_light": "var(--white)" + } + }, + { + "id": "47d514eeee2e2771", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--foreground", + "alias_chain": ["--foreground", "--neutral-950"], + "resolved_value": "oklch(16.98% 0.0000 89.88)", + "occurrences": 8, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--neutral-950)", + "dark": "var(--neutral-50)", + "force_light": "var(--neutral-950)" + } + }, + { + "id": "fb3cbeafabbc06e1", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--card", + "alias_chain": ["--card", "--white"], + "resolved_value": "oklch(100.00% 0.0000 89.88)", + "occurrences": 8, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--white)", + "dark": "var(--neutral-900)", + "force_light": "var(--white)" + } + }, + { + "id": "86707a630fdb544c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--card-foreground", + "alias_chain": ["--card-foreground", "--neutral-950"], + "resolved_value": "oklch(16.98% 0.0000 89.88)", + "occurrences": 4, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--neutral-950)", + "dark": "var(--neutral-50)", + "force_light": "var(--neutral-950)" + } + }, + { + "id": "f2af2c3873fa232d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--popover", + "alias_chain": ["--popover", "--white"], + "resolved_value": "oklch(100.00% 0.0000 89.88)", + "occurrences": 14, + "files_count": 2, + "kind": "color", + "by_theme": { + "default": "var(--white)", + "dark": "rgba(36, 36, 36, 0.8)", + "force_light": "var(--white)" + } + }, + { + "id": "3a4ffa772ad42d07", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--popover-foreground", + "alias_chain": ["--popover-foreground", "--neutral-950"], + "resolved_value": "oklch(16.98% 0.0000 89.88)", + "occurrences": 5, + "files_count": 2, + "kind": "color", + "by_theme": { + "default": "var(--neutral-950)", + "dark": "var(--neutral-50)", + "force_light": "var(--neutral-950)" + } + }, + { + "id": "fa3d0b87af8ff383", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--primary", + "alias_chain": ["--primary", "--neutral-900"], + "resolved_value": "oklch(21.56% 0.0000 89.88)", + "occurrences": 13, + "files_count": 2, + "kind": "color", + "by_theme": { + "default": "var(--neutral-900)", + "dark": "var(--neutral-200)", + "force_light": "var(--neutral-900)" + } + }, + { + "id": "2e70d34ebb41265e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--primary-foreground", + "alias_chain": ["--primary-foreground", "--neutral-50"], + "resolved_value": "oklch(95.42% 0.0000 89.88)", + "occurrences": 6, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--neutral-50)", + "dark": "var(--neutral-900)", + "force_light": "var(--neutral-50)" + } + }, + { + "id": "15e3eb30eef6a01c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--secondary", + "alias_chain": ["--secondary", "--neutral-50"], + "resolved_value": "oklch(95.42% 0.0000 89.88)", + "occurrences": 8, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--neutral-50)", + "dark": "var(--neutral-800)", + "force_light": "var(--neutral-50)" + } + }, + { + "id": "d13fe38775627ad1", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--secondary-foreground", + "alias_chain": ["--secondary-foreground", "--neutral-900"], + "resolved_value": "oklch(21.56% 0.0000 89.88)", + "occurrences": 4, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--neutral-900)", + "dark": "var(--neutral-50)", + "force_light": "var(--neutral-900)" + } + }, + { + "id": "56f92bb792f1e473", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--muted", + "alias_chain": ["--muted", "--neutral-50"], + "resolved_value": "oklch(95.42% 0.0000 89.88)", + "occurrences": 8, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--neutral-50)", + "dark": "var(--neutral-850)", + "force_light": "var(--neutral-50)" + } + }, + { + "id": "6e10c3dec6f1d083", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--muted-foreground", + "alias_chain": ["--muted-foreground", "--neutral-500"], + "resolved_value": "oklch(56.34% 0.0000 89.88)", + "occurrences": 4, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--neutral-500)", + "dark": "var(--neutral-400)", + "force_light": "var(--neutral-500)" + } + }, + { + "id": "1b05d6308c65ea5b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--subtle-foreground", + "alias_chain": ["--subtle-foreground", "--neutral-600"], + "resolved_value": "oklch(47.41% 0.0000 89.88)", + "occurrences": 4, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--neutral-600)", + "dark": "var(--neutral-250)", + "force_light": "var(--neutral-600)" + } + }, + { + "id": "350ab7776578f75d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--elevation-0", + "alias_chain": [], + "resolved_value": "oklab(97.913% 0 -0.00011)", + "occurrences": 7, + "files_count": 2, + "kind": "color", + "by_theme": { + "default": "oklab(97.913% 0 -0.00011)", + "dark": "oklch(0% 0 0)", + "force_light": "#F8F8F8" + } + }, + { + "id": "d5dce84ff4329300", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--elevation-1", + "alias_chain": ["--elevation-1", "--white"], + "resolved_value": "oklch(100.00% 0.0000 89.88)", + "occurrences": 4, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--white)", + "dark": "oklch(19.125% 0.00002 271.152)", + "force_light": "var(--white)" + } + }, + { + "id": "4ec3c0c459c4e188", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--elevation-2", + "alias_chain": [], + "resolved_value": "oklch(95.815% 0.00011 271.152)", + "occurrences": 3, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "oklch(95.815% 0.00011 271.152)", + "dark": "oklch(28.094% 0.00003 271.152)" + } + }, + { + "id": "e5a7cdd394cd4889", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--accent", + "alias_chain": ["--accent", "--neutral-100"], + "resolved_value": "oklch(91.57% 0.0000 89.88)", + "occurrences": 8, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--neutral-100)", + "dark": "var(--neutral-700)", + "force_light": "var(--neutral-100)" + } + }, + { + "id": "1a8e9414134fee85", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--accent-foreground", + "alias_chain": ["--accent-foreground", "--neutral-900"], + "resolved_value": "oklch(21.56% 0.0000 89.88)", + "occurrences": 4, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--neutral-900)", + "dark": "var(--neutral-50)", + "force_light": "var(--neutral-900)" + } + }, + { + "id": "e68a70a412cb67da", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--destructive", + "alias_chain": ["--destructive", "--red-600"], + "resolved_value": "oklch(50.42% 0.1991 28.41)", + "occurrences": 8, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--red-600)", + "dark": "var(--red-400)", + "force_light": "var(--red-600)" + } + }, + { + "id": "af7db862c696635a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--destructive-foreground", + "alias_chain": ["--destructive-foreground", "--red-50"], + "resolved_value": "oklch(95.78% 0.0180 17.47)", + "occurrences": 4, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--red-50)", + "dark": "var(--red-50)", + "force_light": "var(--red-50)" + } + }, + { + "id": "20ba876fc3c707fb", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--danger", + "alias_chain": ["--danger", "--red-600"], + "resolved_value": "oklch(50.42% 0.1991 28.41)", + "occurrences": 8, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--red-600)", + "dark": "var(--red-800)", + "force_light": "var(--red-600)" + } + }, + { + "id": "ae20e44a4b3aed7a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--danger-foreground", + "alias_chain": ["--danger-foreground", "--red-50"], + "resolved_value": "oklch(95.78% 0.0180 17.47)", + "occurrences": 4, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--red-50)", + "dark": "var(--red-100)", + "force_light": "var(--red-50)" + } + }, + { + "id": "db3515ad800c40f3", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--success", + "alias_chain": ["--success", "--green-600"], + "resolved_value": "oklch(47.54% 0.0986 161.50)", + "occurrences": 8, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--green-600)", + "dark": "var(--green-800)", + "force_light": "var(--green-600)" + } + }, + { + "id": "422388f82bc59dd0", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--success-foreground", + "alias_chain": ["--success-foreground", "--green-50"], + "resolved_value": "oklch(94.82% 0.0422 169.29)", + "occurrences": 4, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--green-50)", + "dark": "var(--green-100)", + "force_light": "var(--green-50)" + } + }, + { + "id": "c9cb2fb665a383f2", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--warning", + "alias_chain": ["--warning", "--yellow-200"], + "resolved_value": "oklch(82.83% 0.1680 85.60)", + "occurrences": 8, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--yellow-200)", + "dark": "var(--yellow-800)", + "force_light": "var(--yellow-200)" + } + }, + { + "id": "384ef531cbb88292", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--warning-foreground", + "alias_chain": ["--warning-foreground", "--yellow-950"], + "resolved_value": "oklch(16.77% 0.0313 93.87)", + "occurrences": 4, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--yellow-950)", + "dark": "var(--yellow-100)", + "force_light": "var(--yellow-950)" + } + }, + { + "id": "ae5e9bc83af6f67a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--trend-up", + "alias_chain": ["--trend-up", "--lime-600"], + "resolved_value": "oklch(64.8% 0.2 131.684)", + "occurrences": 4, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--lime-600)", + "dark": "var(--lime-500)", + "force_light": "var(--lime-600)" + } + }, + { + "id": "3677a387203ab484", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--trend-down", + "alias_chain": ["--trend-down", "--orange-500"], + "resolved_value": "oklch(70.5% 0.213 47.604)", + "occurrences": 4, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--orange-500)", + "dark": "var(--orange-500)", + "force_light": "var(--orange-500)" + } + }, + { + "id": "f2c766ae580d13b0", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--trend-neutral", + "alias_chain": ["--trend-neutral", "--neutral-300"], + "resolved_value": "oklch(74.07% 0.0000 89.88)", + "occurrences": 4, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--neutral-300)", + "dark": "var(--neutral-600)", + "force_light": "var(--neutral-300)" + } + }, + { + "id": "d904ebd364df48f0", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--border", + "alias_chain": ["--border", "--neutral-100"], + "resolved_value": "oklch(91.57% 0.0000 89.88)", + "occurrences": 8, + "files_count": 2, + "kind": "color", + "by_theme": { + "default": "var(--neutral-100)", + "dark": "rgba(255, 255, 255, 0.1)", + "force_light": "var(--neutral-100)" + } + }, + { + "id": "accf32a332cdbfb8", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--popover-border", + "alias_chain": [], + "resolved_value": "rgba(0, 0, 0, 0.1)", + "occurrences": 4, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "rgba(0, 0, 0, 0.1)", + "dark": "rgba(255, 255, 255, 0.03)", + "force_light": "rgba(0, 0, 0, 0.1)" + } + }, + { + "id": "a7e7e14aa7b3b49e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--input", + "alias_chain": ["--input", "--neutral-150"], + "resolved_value": "oklch(86.89% 0.0000 89.88)", + "occurrences": 4, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--neutral-150)", + "dark": "rgba(255, 255, 255, 0.12)", + "force_light": "var(--neutral-150)" + } + }, + { + "id": "e76acfcc223852d9", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--ring", + "alias_chain": ["--ring", "--neutral-400"], + "resolved_value": "oklch(64.94% 0.0000 89.88)", + "occurrences": 6, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--neutral-400)", + "dark": "var(--neutral-500)", + "force_light": "var(--neutral-400)" + } + }, + { + "id": "964ee69ed356a043", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--chart-1", + "alias_chain": ["--chart-1", "--sky-300"], + "resolved_value": "oklch(82.8% 0.111 230.318)", + "occurrences": 26, + "files_count": 2, + "kind": "color", + "by_theme": { + "default": "var(--sky-300)", + "dark": "var(--sky-300)" + } + }, + { + "id": "2d2e01ee1098d6b6", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--chart-2", + "alias_chain": ["--chart-2", "--teal-600"], + "resolved_value": "oklch(60.0% 0.118 184.704)", + "occurrences": 5, + "files_count": 2, + "kind": "color", + "by_theme": { + "default": "var(--teal-600)", + "dark": "var(--teal-500)" + } + }, + { + "id": "e0b90c6f567e9ff5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--chart-3", + "alias_chain": ["--chart-3", "--cyan-300"], + "resolved_value": "oklch(86.5% 0.127 207.078)", + "occurrences": 5, + "files_count": 2, + "kind": "color", + "by_theme": { + "default": "var(--cyan-300)", + "dark": "var(--cyan-300)" + } + }, + { + "id": "77f061fdaba66c0b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--chart-4", + "alias_chain": ["--chart-4", "--emerald-600"], + "resolved_value": "oklch(59.6% 0.145 163.225)", + "occurrences": 5, + "files_count": 2, + "kind": "color", + "by_theme": { + "default": "var(--emerald-600)", + "dark": "var(--emerald-500)" + } + }, + { + "id": "d05c285ab7246fd7", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--chart-5", + "alias_chain": ["--chart-5", "--lime-300"], + "resolved_value": "oklch(89.7% 0.196 126.665)", + "occurrences": 5, + "files_count": 2, + "kind": "color", + "by_theme": { + "default": "var(--lime-300)", + "dark": "var(--lime-300)" + } + }, + { + "id": "510c19f799e68c41", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--chart-6", + "alias_chain": ["--chart-6", "--sky-600"], + "resolved_value": "oklch(58.8% 0.158 241.966)", + "occurrences": 5, + "files_count": 2, + "kind": "color", + "by_theme": { + "default": "var(--sky-600)", + "dark": "var(--sky-500)" + } + }, + { + "id": "5b37c429a381b3c1", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--chart-7", + "alias_chain": ["--chart-7", "--teal-300"], + "resolved_value": "oklch(85.5% 0.138 181.071)", + "occurrences": 5, + "files_count": 2, + "kind": "color", + "by_theme": { + "default": "var(--teal-300)", + "dark": "var(--teal-300)" + } + }, + { + "id": "f44e447ff51981ff", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--chart-8", + "alias_chain": ["--chart-8", "--lime-600"], + "resolved_value": "oklch(64.8% 0.2 131.684)", + "occurrences": 5, + "files_count": 2, + "kind": "color", + "by_theme": { + "default": "var(--lime-600)", + "dark": "var(--lime-500)" + } + }, + { + "id": "6dfe9d0b4238c1ee", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--chart-9", + "alias_chain": ["--chart-9", "--cyan-600"], + "resolved_value": "oklch(60.9% 0.126 221.723)", + "occurrences": 4, + "files_count": 2, + "kind": "color", + "by_theme": { + "default": "var(--cyan-600)", + "dark": "var(--cyan-500)" + } + }, + { + "id": "22d35d9291612930", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--chart-10", + "alias_chain": ["--chart-10", "--emerald-300"], + "resolved_value": "oklch(84.5% 0.143 164.978)", + "occurrences": 4, + "files_count": 2, + "kind": "color", + "by_theme": { + "default": "var(--emerald-300)", + "dark": "var(--emerald-300)" + } + }, + { + "id": "1e0e1876c0e6c3ac", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--chart-11", + "alias_chain": ["--chart-11", "--green-300"], + "resolved_value": "oklch(72.79% 0.1560 160.70)", + "occurrences": 4, + "files_count": 2, + "kind": "color", + "by_theme": { + "default": "var(--green-300)", + "dark": "var(--green-300)" + } + }, + { + "id": "1eb7c5846ddb2ade", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--chart-12", + "alias_chain": ["--chart-12", "--green-600"], + "resolved_value": "oklch(47.54% 0.0986 161.50)", + "occurrences": 4, + "files_count": 2, + "kind": "color", + "by_theme": { + "default": "var(--green-600)", + "dark": "var(--green-500)" + } + }, + { + "id": "ab896e3d407a2b91", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--chart-13", + "alias_chain": ["--chart-13", "--sky-400"], + "resolved_value": "oklch(74.6% 0.16 232.661)", + "occurrences": 3, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--sky-400)", + "dark": "var(--sky-400)" + } + }, + { + "id": "6516c07b13e65fba", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--chart-14", + "alias_chain": ["--chart-14", "--teal-400"], + "resolved_value": "oklch(77.7% 0.152 181.912)", + "occurrences": 3, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--teal-400)", + "dark": "var(--teal-400)" + } + }, + { + "id": "4f29699b594e2cb0", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--chart-15", + "alias_chain": ["--chart-15", "--lime-400"], + "resolved_value": "oklch(84.1% 0.238 128.85)", + "occurrences": 3, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--lime-400)", + "dark": "var(--lime-400)" + } + }, + { + "id": "0c4c7f475128717f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--sidebar", + "alias_chain": ["--sidebar", "--elevation-0"], + "resolved_value": "oklab(97.913% 0 -0.00011)", + "occurrences": 31, + "files_count": 3, + "kind": "color", + "by_theme": { + "default": "var(--elevation-0)", + "dark": "var(--elevation-0)" + } + }, + { + "id": "351b8296e09335ca", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--sidebar-foreground", + "alias_chain": ["--sidebar-foreground", "--neutral-700"], + "resolved_value": "oklch(38.99% 0.0000 89.88)", + "occurrences": 3, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--neutral-700)", + "dark": "var(--neutral-250)" + } + }, + { + "id": "450ee957345457b4", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--sidebar-primary", + "alias_chain": ["--sidebar-primary", "--primary"], + "resolved_value": "oklch(21.56% 0.0000 89.88)", + "occurrences": 6, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--primary)", + "dark": "var(--primary)" + } + }, + { + "id": "1cf3cbccf3e8ddba", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--sidebar-primary-foreground", + "alias_chain": ["--sidebar-primary-foreground", "--primary-foreground"], + "resolved_value": "oklch(95.42% 0.0000 89.88)", + "occurrences": 3, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--primary-foreground)", + "dark": "var(--primary-foreground)" + } + }, + { + "id": "9f7d148c660b8de4", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--sidebar-accent", + "alias_chain": [], + "resolved_value": "color-mix(", + "occurrences": 6, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "color-mix(", + "dark": "color-mix(" + } + }, + { + "id": "4db0a2d990f87123", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--sidebar-accent-foreground", + "alias_chain": ["--sidebar-accent-foreground", "--foreground"], + "resolved_value": "oklch(16.98% 0.0000 89.88)", + "occurrences": 3, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--foreground)", + "dark": "var(--foreground)" + } + }, + { + "id": "7406625c0cc1ffeb", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--sidebar-border", + "alias_chain": ["--sidebar-border", "--border"], + "resolved_value": "oklch(91.57% 0.0000 89.88)", + "occurrences": 3, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--border)", + "dark": "var(--border)" + } + }, + { + "id": "854cfa9b7c632efc", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--sidebar-ring", + "alias_chain": ["--sidebar-ring", "--ring"], + "resolved_value": "oklch(64.94% 0.0000 89.88)", + "occurrences": 3, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--ring)", + "dark": "var(--ring)" + } + }, + { + "id": "a0da6147a8273831", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--panel", + "alias_chain": [], + "resolved_value": "#FCFCFC", + "occurrences": 7, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "#FCFCFC", + "dark": "#181818", + "force_light": "#FCFCFC" + } + }, + { + "id": "db3535114854e4ff", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--panel-foreground", + "alias_chain": ["--panel-foreground", "--neutral-950"], + "resolved_value": "oklch(16.98% 0.0000 89.88)", + "occurrences": 3, + "files_count": 1, + "kind": "color", + "by_theme": { + "default": "var(--neutral-950)", + "dark": "var(--neutral-50)", + "force_light": "var(--neutral-950)" + } + }, + { + "id": "8688d41e121f3643", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--core-icon-small-size-width", + "alias_chain": [], + "resolved_value": "16px", + "occurrences": 1, + "files_count": 1, + "kind": "spacing" + }, + { + "id": "6d3f2af415ece6da", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--core-icon-small-size-height", + "alias_chain": [], + "resolved_value": "16px", + "occurrences": 1, + "files_count": 1, + "kind": "spacing" + }, + { + "id": "acb31d08d03e0ebf", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--core-icon-medium-size-width", + "alias_chain": [], + "resolved_value": "24px", + "occurrences": 1, + "files_count": 1, + "kind": "spacing" + }, + { + "id": "6776c00b1458fed9", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--core-icon-medium-size-height", + "alias_chain": [], + "resolved_value": "24px", + "occurrences": 1, + "files_count": 1, + "kind": "spacing" + }, + { + "id": "efa633fa9991b1fc", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--core-icon-large-size-width", + "alias_chain": [], + "resolved_value": "32px", + "occurrences": 1, + "files_count": 1, + "kind": "spacing" + }, + { + "id": "1c469cdb077e8d32", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--core-icon-large-size-height", + "alias_chain": [], + "resolved_value": "32px", + "occurrences": 1, + "files_count": 1, + "kind": "spacing" + }, + { + "id": "5616afce4261b3a9", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--font-sans", + "alias_chain": [], + "resolved_value": "'Cash Sans', ui-sans-serif, system-ui, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\"", + "occurrences": 1, + "files_count": 1, + "kind": "typography" + }, + { + "id": "091627f0a2761f3a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--border-width-hairline", + "alias_chain": [], + "resolved_value": "0.5px", + "occurrences": 1, + "files_count": 1, + "kind": "spacing" + }, + { + "id": "383ae9c2e0fb9663", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--radius-xs", + "alias_chain": [], + "resolved_value": "4px", + "occurrences": 1, + "files_count": 1, + "kind": "radius" + }, + { + "id": "493b57b34a6f842f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--radius-sm", + "alias_chain": [], + "resolved_value": "6px", + "occurrences": 1, + "files_count": 1, + "kind": "radius" + }, + { + "id": "d9e5230e1edccc5a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--radius-md", + "alias_chain": [], + "resolved_value": "8px", + "occurrences": 1, + "files_count": 1, + "kind": "radius" + }, + { + "id": "9c4fd7bc6674c987", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--radius-lg", + "alias_chain": [], + "resolved_value": "12px", + "occurrences": 1, + "files_count": 1, + "kind": "radius" + }, + { + "id": "659969929bd0e9a5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--radius-xl", + "alias_chain": [], + "resolved_value": "16px", + "occurrences": 1, + "files_count": 1, + "kind": "radius" + }, + { + "id": "0cb1be099db346a4", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--radius-2xl", + "alias_chain": [], + "resolved_value": "20px", + "occurrences": 1, + "files_count": 1, + "kind": "radius" + }, + { + "id": "ea667b566658658a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--radius-3xl", + "alias_chain": [], + "resolved_value": "24px", + "occurrences": 1, + "files_count": 1, + "kind": "radius" + }, + { + "id": "67bc508cd0808ba7", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--radius-4xl", + "alias_chain": [], + "resolved_value": "32px", + "occurrences": 1, + "files_count": 1, + "kind": "radius" + }, + { + "id": "4bc365610f1ab9d5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--width-sidebar", + "alias_chain": [], + "resolved_value": "280px", + "occurrences": 2, + "files_count": 1, + "kind": "spacing" + }, + { + "id": "6f68fe17ce27046d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--width-main", + "alias_chain": [], + "resolved_value": "calc(100vw - var(--width-sidebar) - (var(--spacing) * 8))", + "occurrences": 1, + "files_count": 1, + "kind": "spacing" + }, + { + "id": "a6ef2e8ea9698aae", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--width-content-narrow", + "alias_chain": [], + "resolved_value": "48rem", + "occurrences": 2, + "files_count": 2, + "kind": "spacing" + }, + { + "id": "437c29f67516adbe", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--width-content-wide", + "alias_chain": [], + "resolved_value": "82rem", + "occurrences": 2, + "files_count": 2, + "kind": "spacing" + }, + { + "id": "ec13cb0e8c013973", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--shadow-2xs", + "alias_chain": [], + "resolved_value": "0 1px 0 0 rgba(0, 0, 0, 0.05)", + "occurrences": 1, + "files_count": 1, + "kind": "shadow" + }, + { + "id": "71303335c949aca6", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--shadow-xs", + "alias_chain": [], + "resolved_value": "0 0 1px 0 rgba(0, 0, 0, 0.05)", + "occurrences": 1, + "files_count": 1, + "kind": "shadow" + }, + { + "id": "cd18761c55f2e57e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--shadow-sm", + "alias_chain": [], + "resolved_value": "0 0 2px 0 rgba(0, 0, 0, 0.1), 0 4px 8px -2px rgba(0, 0, 0, 0.06)", + "occurrences": 1, + "files_count": 1, + "kind": "shadow" + }, + { + "id": "d3c0c75c2ac47a2f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--shadow-md", + "alias_chain": [], + "resolved_value": "0 0 2px 0 rgba(0, 0, 0, 0.1), 0 8px 16px -4px rgba(0, 0, 0, 0.1)", + "occurrences": 1, + "files_count": 1, + "kind": "shadow" + }, + { + "id": "734ed908c1061136", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--shadow-lg", + "alias_chain": [], + "resolved_value": "0 0 2px 0 rgba(0, 0, 0, 0.1), 0 16px 32px -8px rgba(0, 0, 0, 0.1)", + "occurrences": 1, + "files_count": 1, + "kind": "shadow" + }, + { + "id": "8fa02d3885706efe", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--shadow-xl", + "alias_chain": [], + "resolved_value": "0 0 2px 0 rgba(0, 0, 0, 0.1), 0 24px 48px -12px rgba(0, 0, 0, 0.1)", + "occurrences": 1, + "files_count": 1, + "kind": "shadow" + }, + { + "id": "e9764dfe2fa67722", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--shadow-2xl", + "alias_chain": [], + "resolved_value": "0 25px 50px -12px rgba(0, 0, 0, 0.25)", + "occurrences": 1, + "files_count": 1, + "kind": "shadow" + }, + { + "id": "2e2a632a661812b0", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--shadow-inner", + "alias_chain": [], + "resolved_value": "inset 0 0 1px 0 rgba(0, 0, 0, 0.40)", + "occurrences": 1, + "files_count": 1, + "kind": "shadow" + }, + { + "id": "88055973ba24c050", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-xs", + "alias_chain": [], + "resolved_value": "0.8125rem", + "occurrences": 3, + "files_count": 1, + "kind": "typography" + }, + { + "id": "62b3750357a1b8ec", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-xs--line-height", + "alias_chain": [], + "resolved_value": "1rem", + "occurrences": 1, + "files_count": 1, + "kind": "typography" + }, + { + "id": "78a41c2ffadf356e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-xs--letter-spacing", + "alias_chain": [], + "resolved_value": "0", + "occurrences": 1, + "files_count": 1, + "kind": "typography" + }, + { + "id": "49195a1995aa0d40", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-sm", + "alias_chain": [], + "resolved_value": "0.875rem", + "occurrences": 3, + "files_count": 1, + "kind": "typography" + }, + { + "id": "c9bebea6aaaca4fc", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-sm--line-height", + "alias_chain": [], + "resolved_value": "1.25rem", + "occurrences": 1, + "files_count": 1, + "kind": "typography" + }, + { + "id": "455710a4cf1c40c9", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-sm--letter-spacing", + "alias_chain": [], + "resolved_value": "0", + "occurrences": 1, + "files_count": 1, + "kind": "typography" + }, + { + "id": "ad4a867c2a1cacea", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-base", + "alias_chain": [], + "resolved_value": "1rem", + "occurrences": 3, + "files_count": 1, + "kind": "typography" + }, + { + "id": "2b7f475969d97aa3", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-base--line-height", + "alias_chain": [], + "resolved_value": "1.5rem", + "occurrences": 1, + "files_count": 1, + "kind": "typography" + }, + { + "id": "77f63a03b0fcf37c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-base--letter-spacing", + "alias_chain": [], + "resolved_value": "0", + "occurrences": 1, + "files_count": 1, + "kind": "typography" + }, + { + "id": "23d17b09cee74343", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-lg", + "alias_chain": [], + "resolved_value": "1.125rem", + "occurrences": 3, + "files_count": 1, + "kind": "typography" + }, + { + "id": "d50ea6bd50a0c6f3", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-lg--line-height", + "alias_chain": [], + "resolved_value": "1.75rem", + "occurrences": 1, + "files_count": 1, + "kind": "typography" + }, + { + "id": "84f2b433ce58987d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-lg--letter-spacing", + "alias_chain": [], + "resolved_value": "0", + "occurrences": 1, + "files_count": 1, + "kind": "typography" + }, + { + "id": "f65451504f474251", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-xl", + "alias_chain": [], + "resolved_value": "1.25rem", + "occurrences": 3, + "files_count": 1, + "kind": "typography" + }, + { + "id": "b208befa4c76d78a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-xl--line-height", + "alias_chain": [], + "resolved_value": "1.75rem", + "occurrences": 1, + "files_count": 1, + "kind": "typography" + }, + { + "id": "b7c7a2f416393067", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-xl--letter-spacing", + "alias_chain": [], + "resolved_value": "0", + "occurrences": 1, + "files_count": 1, + "kind": "typography" + }, + { + "id": "dec620d956c1d8fe", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-2xl", + "alias_chain": [], + "resolved_value": "1.5rem", + "occurrences": 3, + "files_count": 1, + "kind": "typography" + }, + { + "id": "bb5d84509de198cf", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-2xl--line-height", + "alias_chain": [], + "resolved_value": "2rem", + "occurrences": 1, + "files_count": 1, + "kind": "typography" + }, + { + "id": "baa44931ae91a20b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-2xl--letter-spacing", + "alias_chain": [], + "resolved_value": "-0.2px", + "occurrences": 1, + "files_count": 1, + "kind": "typography" + }, + { + "id": "b1e9e6d1bb83c490", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-3xl", + "alias_chain": [], + "resolved_value": "2rem", + "occurrences": 3, + "files_count": 1, + "kind": "typography" + }, + { + "id": "f63ccfcae011a6ca", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-3xl--line-height", + "alias_chain": [], + "resolved_value": "2.25rem", + "occurrences": 1, + "files_count": 1, + "kind": "typography" + }, + { + "id": "01b207a320403def", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-3xl--letter-spacing", + "alias_chain": [], + "resolved_value": "-0.4px", + "occurrences": 1, + "files_count": 1, + "kind": "typography" + }, + { + "id": "66cfd51212a124d0", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-4xl", + "alias_chain": [], + "resolved_value": "2.25rem", + "occurrences": 3, + "files_count": 1, + "kind": "typography" + }, + { + "id": "af199e904cc3c7f8", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-4xl--line-height", + "alias_chain": [], + "resolved_value": "2.5rem", + "occurrences": 1, + "files_count": 1, + "kind": "typography" + }, + { + "id": "ae5359fa4f36ab5d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-4xl--letter-spacing", + "alias_chain": [], + "resolved_value": "-0.5px", + "occurrences": 1, + "files_count": 1, + "kind": "typography" + }, + { + "id": "2b3c41212d28e352", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-5xl", + "alias_chain": [], + "resolved_value": "2.75rem", + "occurrences": 3, + "files_count": 1, + "kind": "typography" + }, + { + "id": "81d9742482cbbd78", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-5xl--line-height", + "alias_chain": [], + "resolved_value": "2.75rem", + "occurrences": 1, + "files_count": 1, + "kind": "typography" + }, + { + "id": "643080d5196c97b3", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-5xl--letter-spacing", + "alias_chain": [], + "resolved_value": "-1.4px", + "occurrences": 1, + "files_count": 1, + "kind": "typography" + }, + { + "id": "65ad7e34ba400549", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-6xl", + "alias_chain": [], + "resolved_value": "3.5rem", + "occurrences": 3, + "files_count": 1, + "kind": "typography" + }, + { + "id": "2d1b55d271985cdc", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-6xl--line-height", + "alias_chain": [], + "resolved_value": "3.5rem", + "occurrences": 1, + "files_count": 1, + "kind": "typography" + }, + { + "id": "b032804d9158abdb", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-6xl--letter-spacing", + "alias_chain": [], + "resolved_value": "-2.2px", + "occurrences": 1, + "files_count": 1, + "kind": "typography" + }, + { + "id": "54093e556a947024", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-7xl", + "alias_chain": [], + "resolved_value": "3.5rem", + "occurrences": 3, + "files_count": 1, + "kind": "typography" + }, + { + "id": "9d571183331023e2", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-7xl--line-height", + "alias_chain": [], + "resolved_value": "3.5rem", + "occurrences": 1, + "files_count": 1, + "kind": "typography" + }, + { + "id": "3dabe1d2b167e13b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--text-7xl--letter-spacing", + "alias_chain": [], + "resolved_value": "-2.2px", + "occurrences": 1, + "files_count": 1, + "kind": "typography" + }, + { + "id": "4145e1e438d4e65d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "--animate-highlights-tab-fade-in", + "alias_chain": [], + "resolved_value": "highlights-tab-fade-in 150ms ease forwards", + "occurrences": 1, + "files_count": 1, + "kind": "motion" + } + ], + "components": [ + { + "id": "6a1a01e7f6ec586e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Accordion", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/accordion.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "14955945c82acfef", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Actions", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/actions.tsx", + "group": "ai-elements", + "exports": ["Actions", "Action"], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "2410e70f0da033f6", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Artifact", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/artifact.tsx", + "group": "ai-elements", + "exports": [ + "Artifact", + "ArtifactHeader", + "ArtifactClose", + "ArtifactTitle", + "ArtifactDescription", + "ArtifactActions", + "ArtifactAction", + "ArtifactContent" + ], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "94513a9186b2e799", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Branch", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/branch.tsx", + "group": "ai-elements", + "exports": [ + "Branch", + "BranchMessages", + "BranchSelector", + "BranchPrevious", + "BranchNext", + "BranchPage" + ], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "b54ea27fdaec8ff9", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Canvas", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/canvas.tsx", + "group": "ai-elements", + "exports": ["Canvas"], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "4f1396e9e417fc81", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "ChainOfThought", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/chain-of-thought.tsx", + "group": "ai-elements", + "exports": [ + "ChainOfThought", + "ChainOfThoughtHeader", + "ChainOfThoughtStep", + "ChainOfThoughtSearchResults", + "ChainOfThoughtSearchResult", + "ChainOfThoughtContent", + "ChainOfThoughtImage" + ], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "fde5ad62b2ea11de", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "CodeBlockDownloadButton", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/code-block-download-button.tsx", + "group": "ai-elements", + "exports": ["CodeBlockDownloadButton"], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "b6a187ceffa88b5f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "CodeBlock", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/code-block.tsx", + "group": "ai-elements", + "exports": ["CodeBlock", "CodeBlockCopyButton"], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "08ab2dab32f8175d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Confirmation", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/confirmation.tsx", + "group": "ai-elements", + "exports": [ + "Confirmation", + "ConfirmationTitle", + "ConfirmationRequest", + "ConfirmationAccepted", + "ConfirmationRejected", + "ConfirmationActions", + "ConfirmationAction" + ], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "6fcdd290ebc7d925", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Connection", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/connection.tsx", + "group": "ai-elements", + "exports": ["Connection"], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "d17a7e3e51ce9aa3", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Context", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/context.tsx", + "group": "ai-elements", + "exports": [ + "Context", + "ContextTrigger", + "ContextContent", + "ContextContentHeader", + "ContextContentBody", + "ContextContentFooter", + "ContextInputUsage", + "ContextOutputUsage", + "ContextReasoningUsage", + "ContextCacheUsage" + ], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "1cce606e4e9496ac", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Controls", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/controls.tsx", + "group": "ai-elements", + "exports": ["Controls"], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "372d5d2170e6acf7", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Conversation", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/conversation.tsx", + "group": "ai-elements", + "exports": [ + "Conversation", + "ConversationContent", + "ConversationEmptyState", + "ScrollToBottomButton" + ], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "7111fedc002a8faf", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Edge", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/edge.tsx", + "group": "ai-elements", + "exports": ["Edge"], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "863a32ddc98ee519", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Image", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/image.tsx", + "group": "ai-elements", + "exports": ["Image"], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "ba7996efd49abbe5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "InlineCitation", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/inline-citation.tsx", + "group": "ai-elements", + "exports": [ + "InlineCitation", + "InlineCitationText", + "InlineCitationCard", + "InlineCitationCardTrigger", + "InlineCitationCardBody", + "InlineCitationCarousel", + "InlineCitationCarouselContent", + "InlineCitationCarouselItem", + "InlineCitationCarouselHeader", + "InlineCitationCarouselIndex", + "InlineCitationCarouselPrev", + "InlineCitationCarouselNext", + "InlineCitationSource", + "InlineCitationQuote" + ], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "c6c33fdbd3a11811", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "InputChip", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/input-chip.tsx", + "group": "ai-elements", + "exports": ["InputChip"], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "3d4cce081c76a977", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Loader", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/loader.tsx", + "group": "ai-elements", + "exports": ["Loader"], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "fd53eb95ef798888", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "ai-elements/Message", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/message.tsx", + "group": "ai-elements", + "exports": [ + "Message", + "MessageContent", + "MessageTitle", + "MessageSeparator", + "MessageAction", + "MessageGroup" + ], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "7baccda99f763288", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Node", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/node.tsx", + "group": "ai-elements", + "exports": [ + "Node", + "NodeHeader", + "NodeTitle", + "NodeDescription", + "NodeAction", + "NodeContent", + "NodeFooter" + ], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "796a5300126a7cc5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "OpenInChat", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/open-in-chat.tsx", + "group": "ai-elements", + "exports": [ + "OpenIn", + "OpenInContent", + "OpenInItem", + "OpenInLabel", + "OpenInSeparator", + "OpenInTrigger", + "OpenInChatGPT", + "OpenInClaude", + "OpenInT3", + "OpenInScira", + "OpenInv0", + "OpenInCursor" + ], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "56ef38180410804b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Panel", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/panel.tsx", + "group": "ai-elements", + "exports": ["Panel"], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "6cf913a2c9bd02e8", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Plan", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/plan.tsx", + "group": "ai-elements", + "exports": [ + "Plan", + "PlanHeader", + "PlanTitle", + "PlanDescription", + "PlanAction", + "PlanContent", + "PlanFooter", + "PlanTrigger" + ], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "9600e1000348dc1c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "PromptInput", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/prompt-input.tsx", + "group": "ai-elements", + "exports": [ + "PromptInputProvider", + "PromptInputAttachment", + "PromptInputAttachments", + "PromptInputActionAddAttachments", + "PromptInput", + "PromptInputBody", + "PromptInputTextarea", + "PromptInputHeader", + "PromptInputFooter", + "PromptInputTools", + "PromptInputButton", + "PromptInputActionMenu", + "PromptInputActionMenuTrigger", + "PromptInputActionMenuContent", + "PromptInputActionMenuItem", + "PromptInputSubmit", + "PromptInputSpeechButton", + "PromptInputModelSelect", + "PromptInputModelSelectTrigger", + "PromptInputModelSelectContent", + "PromptInputModelSelectItem", + "PromptInputModelSelectValue", + "PromptInputHoverCard", + "PromptInputHoverCardTrigger", + "PromptInputHoverCardContent", + "PromptInputTabsList", + "PromptInputTab", + "PromptInputTabLabel", + "PromptInputTabBody", + "PromptInputTabItem", + "PromptInputCommand", + "PromptInputCommandInput", + "PromptInputCommandList", + "PromptInputCommandEmpty", + "PromptInputCommandGroup", + "PromptInputCommandItem", + "PromptInputCommandSeparator" + ], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "8558a602a5a32548", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Queue", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/queue.tsx", + "group": "ai-elements", + "exports": [ + "QueueItem", + "QueueItemIndicator", + "QueueItemContent", + "QueueItemDescription", + "QueueItemActions", + "QueueItemAction", + "QueueItemAttachment", + "QueueItemImage", + "QueueItemFile", + "QueueList", + "QueueSection", + "QueueSectionTrigger", + "QueueSectionLabel", + "QueueSectionContent", + "Queue" + ], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "d48ba14cde698e33", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Reasoning", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/reasoning.tsx", + "group": "ai-elements", + "exports": ["Reasoning", "ReasoningTrigger", "ReasoningContent"], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "e40abc06d0a36499", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Response", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/response.tsx", + "group": "ai-elements", + "exports": ["Response"], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "3755277012607b2c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "ScrollStrategy", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/scroll-strategy.tsx", + "group": "ai-elements", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "4365f5479a62a1f9", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Shimmer", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/shimmer.tsx", + "group": "ai-elements", + "exports": ["Shimmer"], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "9dbf3b6d75f62e43", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Sources", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/sources.tsx", + "group": "ai-elements", + "exports": ["Sources", "SourcesTrigger", "SourcesContent", "Source"], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "6e83f1c6383921c8", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Suggestion", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/suggestion.tsx", + "group": "ai-elements", + "exports": ["Suggestions", "Suggestion"], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "8edc89aff3d686db", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Task", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/task.tsx", + "group": "ai-elements", + "exports": [ + "TaskItemFile", + "TaskItem", + "Task", + "TaskTrigger", + "TaskContent" + ], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "5aa0a812d7396669", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Tool", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/tool.tsx", + "group": "ai-elements", + "exports": [ + "Tool", + "ToolHeader", + "ToolContent", + "ToolInput", + "ToolOutput" + ], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "ffbbe4d01b695db0", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Toolbar", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/toolbar.tsx", + "group": "ai-elements", + "exports": ["Toolbar"], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "d5d1c1ecd6227d2c", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "WebPreview", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/ai-elements/web-preview.tsx", + "group": "ai-elements", + "exports": [ + "WebPreview", + "WebPreviewNavigation", + "WebPreviewNavigationButton", + "WebPreviewUrl", + "WebPreviewBody", + "WebPreviewConsole" + ], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "059495e1bcb82184", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "AlertDialog", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/alert-dialog.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "7a27ca5573663388", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Alert", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/alert.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "2e1cce6741f9f941", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "AspectRatio", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/aspect-ratio.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "3fb91941fa285209", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Avatar", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/avatar.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "85c2d6750543a0be", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Badge", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/badge.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "cb83fb8642b8fd41", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Breadcrumb", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/breadcrumb.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "eb0ce0277fcac93a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "ButtonGroup", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/button-group.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "c84e559e20755423", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Button", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/button.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "4c6467ddc24e1144", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Calendar", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/calendar.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "23e6e361e19f9786", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Card", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/card.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "5fcdd76183886b65", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Carousel", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/carousel.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "e350fb982fd22829", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Checkbox", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/checkbox.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "bfd9b8117de00312", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Collapsible", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/collapsible.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "57ac2345ae919b2a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Combobox", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/combobox.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "a39b731c4f814c77", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "CommandPalette", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/command-palette.tsx", + "group": "primitive", + "exports": ["CommandPalette"], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "d5e4ebc708ede4e2", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Command", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/command.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "6fa0a8f82cd55e53", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Conditional", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/conditional.tsx", + "group": "primitive", + "exports": ["Conditional"], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "57a7513a071c884e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "ContextMenu", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/context-menu.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "e2a71600a79d8b37", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "conversation/Message", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/conversation/message.tsx", + "group": "conversation", + "exports": [ + "MessageList", + "Message", + "MessageOrnament", + "MessageContent", + "MessageAvatar", + "CollapsibleMessage", + "CollapsibleMessageTrigger", + "CollapsibleMessageOrnament", + "CollapsibleMessageContent", + "HumanMessage", + "AgentMessage" + ], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "6dd315037432a14e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "DateRangePicker", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/date-range-picker.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "741eca4f1c6526b7", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Dialog", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/dialog.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "e11ffe74cc150eee", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Drawer", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/drawer.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "0cc1230353804703", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "DropdownMenu", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/dropdown-menu.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "ea92960938ae8b61", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Empty", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/empty.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "85772ab48d15871d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Field", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/field.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "cded33f01f610f53", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Form", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/form.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "3a5b4ec2b9315a15", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "HoverCard", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/hover-card.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "3d4514ab68ddb33d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "IdDisplay", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/id-display.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "3304e9e338270b16", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "InputGroup", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/input-group.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "0b558c6f121cba75", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "InputOtp", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/input-otp.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "cd9a13379a3ec519", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Input", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/input.tsx", + "group": "primitive", + "exports": ["INPUT_BASE_CLASSES"], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "984390194501243a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "InsightsCard", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/insights-card.tsx", + "group": "primitive", + "exports": [ + "InsightCard", + "InsightsCardRow", + "InsightsCardTitle", + "InsightsCardDescription", + "InsightsCardAction", + "InsightsCardPrimaryAction", + "InsightsCardDismiss", + "ActiveSessionRing", + "CardShimmer" + ], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "b142bc064e972cc5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Item", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/item.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "85962df749c34287", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Kbd", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/kbd.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "3c0cb93f74add9c2", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Label", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/label.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "84d70b59e8374b51", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "ManagerbotIcon", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/managerbot-icon.tsx", + "group": "primitive", + "exports": ["ManagerbotIcon"], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "bb0c0ce697d5ca0f", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Menubar", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/menubar.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "956d5766c6b95d8e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "NavigationMenu", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/navigation-menu.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "cc0b8284645b26ce", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Pagination", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/pagination.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "5daab12c552467f8", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Popover", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/popover.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "0faa76b649e96bb8", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Progress", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/progress.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "955a15f624484ddb", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "RadioGroup", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/radio-group.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "d6af07690ea10505", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Resizable", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/resizable.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "d106e7cfe90f2f06", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "ScrollArea", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/scroll-area.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "9334b09349af4b3e", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Select", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/select.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "1e0080df30c84b07", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Separator", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/separator.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "74cad9e6383e2e18", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Sheet", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/sheet.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "bcb7bc04c26606b3", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Shell", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/shell.tsx", + "group": "primitive", + "exports": ["Root", "Header", "Content"], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "dc7064c72aca10cc", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Sidebar", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/sidebar.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "81ba312e9047d52d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Skeleton", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/skeleton.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "a0846e3e4f2eaed6", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Slider", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/slider.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "166fe6910c2fd0a6", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Slot", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/slot.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "9568a5e55087a889", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Sonner", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/sonner.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "d6fe0358c97eebc4", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Spinner", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/spinner.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "a8c0ff16c2974568", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Switch", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/switch.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "dd75dfba8d8deeb9", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Table", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/table.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "475a7d9dbca5090d", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Tabs", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/tabs.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "a3a6227644870b81", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "TasksBadge", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/tasks-popover/tasks-badge.tsx", + "group": "tasks-popover", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "412d6d6756f1a591", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "TasksPopoverPanels", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/tasks-popover/tasks-popover-panels.tsx", + "group": "tasks-popover", + "exports": ["TasksPopoverRootContext"], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "b2dc017eefecc8f8", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "TasksPopover", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/tasks-popover/tasks-popover.tsx", + "group": "tasks-popover", + "exports": [ + "TasksPopoverHeader", + "TasksPopoverOrnament", + "TasksPopoverTitle", + "TasksPopoverActions" + ], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "734a6781de43b380", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Textarea", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/textarea.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "ad263c57d070955a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "TheManager", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/the-manager.tsx", + "group": "primitive", + "exports": ["TheManager"], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "d833ac5e300c6e9a", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "ToggleGroup", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/toggle-group.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "2972cdc426c635d0", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Toggle", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/toggle.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "5513848463906087", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Tooltip", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/tooltip.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "fa902c5cfd32ff6b", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "Trend", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/trend.tsx", + "group": "primitive", + "exports": [], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "51b49ea283793b28", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "TypingText", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/typing-text.tsx", + "group": "primitive", + "exports": ["TypingText"], + "variants": [], + "sizes": [], + "occurrences": 1 + }, + { + "id": "877092b9193aacd5", + "source": { + "target": "squareup/square-web/apps/managerbot", + "scanned_at": "2026-04-30T00:00:00Z" + }, + "name": "View", + "discovered_via": "heuristic", + "file": "libs/managerbot-ui/src/components/view.tsx", + "group": "primitive", + "exports": [ + "Root", + "Nav", + "NavTitle", + "Content", + "Footer", + "Section", + "SectionHeader", + "SectionTitle", + "SectionStatusIcon", + "SectionDescription", + "Header", + "HeaderTitle", + "HeaderDescription", + "HeaderOrnament", + "Seperator" + ], + "variants": [], + "sizes": [], + "occurrences": 1 + } + ] +} diff --git a/dogfood/managerbot/expression.md b/dogfood/managerbot/expression.md new file mode 100644 index 0000000..17db6a2 --- /dev/null +++ b/dogfood/managerbot/expression.md @@ -0,0 +1,333 @@ +--- +id: managerbot +source: llm +timestamp: 2026-04-30T00:00:00Z +sources: + - squareup/square-web/apps/managerbot +observation: + personality: + - utilitarian + - polished + - dual-themed + - ai-native + - dense + - chromatic-reserved + resembles: + - shadcn-new-york + - vercel-ai-elements +decisions: + - dimension: color-strategy + - dimension: surface-hierarchy + - dimension: shape-language + - dimension: typography-voice + - dimension: elevation + - dimension: theming-architecture + - dimension: token-architecture + - dimension: motion + - dimension: font-sourcing +rules: + - id: monochrome-semantic-default + canonical: color-strategy + kind: color + summary: Semantic color tokens must resolve to the neutral ramp by default + rationale: >- + The default theme is achromatic — `--primary`, `--secondary`, `--accent`, + `--muted`, `--card`, `--popover` all chain to `--neutral-*`. Hue is + reserved for state (destructive, success, warning), trend (trend-up/down), + and chart data. Wiring a brand hue (`cashapp-*`, `square-*`, + `--blue-*`) directly into a base surface token is drift. + pattern: '--(primary|secondary|accent|muted|card|popover|background|foreground|sidebar|panel)\b[^:]*:\s*var\(--(?!neutral|white|black|elevation)' + enforce_at: [css_var] + support: 0.97 + - id: tokens-not-literals + canonical: token-architecture + kind: color + summary: Component CSS must reference tokens, not raw oklch/hex/rgba literals + rationale: >- + The token layer (`theme.css` + `@theme inline`) carries all 459 named + tokens — components consume them via `var(--foo)` or Tailwind class + atoms. An oklch/hex literal anywhere outside `theme.css` bypasses the + light/dark cascade and the `force-light` utility. + pattern: '\boklch\(|\b#[0-9a-fA-F]{3,8}\b|\brgba?\(' + enforce_at: [css_var, inline_style] + support: 0.96 + - id: type-on-ramp + canonical: typography-voice + kind: type-size + summary: Font sizes must come from the `--text-*` ramp + rationale: >- + Eight discrete sizes (`xs` 13px → `7xl` 56px) carry matching + line-heights and letter-spacing. Tailwind v4 emits `text-xs..text-7xl` + utilities from `@theme inline` — arbitrary `text-[Npx]` values escape + the rhythm. + pattern: 'text-\[\d+(\.\d+)?(px|rem|em)\]|font-size:\s*\d' + enforce_at: [className, inline_style] + support: 0.94 + - id: radius-on-scale + canonical: shape-language + kind: radius + summary: Corner radii must come from the `--radius-*` scale + rationale: >- + Eight tiers: xs 4 / sm 6 / md 8 / lg 12 / xl 16 / 2xl 20 / 3xl 24 / + 4xl 32 px. No pill (999) and no zero-radius — interactives and + containers share the same vocabulary at different magnitudes. Arbitrary + `rounded-[Npx]` values fork the shape language. + pattern: 'rounded-\[\d+px\]|border-radius:\s*\d+px' + enforce_at: [className, inline_style] + support: 0.93 + - id: cash-sans-only + canonical: font-sourcing + kind: type-family + summary: Do not bundle fonts beyond Cash Sans + rationale: >- + `--font-sans` resolves to `'Cash Sans', ui-sans-serif, system-ui, + sans-serif, ...emoji-stack`. Cash Sans is loaded from + `cash-f.squarecdn.com` at three weights (400/500/600). Adding a new + `@font-face` or a webfont import crosses the font-sourcing decision — + the Square brand face is the only bundled type. + pattern: '@font-face|@import\s+url\([^)]*fonts' + enforce_at: [css_var] + presence_floor: 0 + support: 1.0 + - id: shadow-on-scale + canonical: elevation + kind: shadow + summary: Shadows must come from the `--shadow-*` scale + rationale: >- + Eight tiers (2xs / xs / sm / md / lg / xl / 2xl / inner). Every level + shares the same 2px ambient + a directional offset that doubles each + step. Inline `box-shadow` literals or `shadow-[...]` arbitrary values + break the depth vocabulary. + pattern: 'shadow-\[[^]]+\]|box-shadow:\s*[^v]' + enforce_at: [className, inline_style] + support: 0.95 + - id: no-decorative-motion + canonical: motion + kind: motion + summary: Motion is reserved for state transitions, not decoration + rationale: >- + The repo declares exactly one named animation token + (`--animate-highlights-tab-fade-in: 150ms ease`) plus three keyframes + tied to a single feature (highlights tabs/progress). No + hover-bounce, no decorative loops, no `transition: all`. Adding + decorative motion crosses the restraint baseline. + pattern: 'transition:\s*all\b|animate-(?!none|highlights-)\w' + enforce_at: [className, css_var] + presence_floor: 4 + support: 0.92 +palette: + dominant: + - { role: primary, value: "oklch(21.56% 0.0000 89.88)", token: "--neutral-900" } + - { role: primary-foreground, value: "oklch(95.42% 0.0000 89.88)", token: "--neutral-50" } + - { role: background, value: "oklch(100.00% 0.0000 89.88)", token: "--white" } + - { role: foreground, value: "oklch(16.98% 0.0000 89.88)", token: "--neutral-950" } + neutrals: + steps: + - "oklch(100.00% 0.0000 89.88)" + - "oklch(95.42% 0.0000 89.88)" + - "oklch(56.34% 0.0000 89.88)" + - "oklch(21.56% 0.0000 89.88)" + - "oklch(16.98% 0.0000 89.88)" + - "oklch(0.00% 0.0000 0.00)" + count: 6 + semantic: + - { role: destructive, value: "oklch(50.42% 0.1991 28.41)", token: "--red-600" } + - { role: success, value: "oklch(47.54% 0.0986 161.50)", token: "--green-600" } + - { role: warning, value: "oklch(82.83% 0.1680 85.60)", token: "--yellow-200" } + - { role: trend-up, value: "oklch(64.8% 0.2 131.684)", token: "--lime-600" } + - { role: trend-down, value: "oklch(70.5% 0.213 47.604)", token: "--orange-500" } + - { role: panel-light, value: "#FCFCFC", token: "--panel" } + - { role: panel-dark, value: "#181818", token: "--panel" } + saturationProfile: muted + contrast: high +spacing: + scale: [0.5, 4, 16, 20, 24, 32, 280, 768, 1312] + regularity: 0.4 + baseUnit: 4 +typography: + families: + - "Cash Sans" + sizeRamp: [13, 14, 16, 18, 20, 24, 32, 36, 44, 56] + weightDistribution: + "400": 1 + "500": 1 + "600": 1 + lineHeightPattern: normal +surfaces: + borderRadii: [4, 6, 8, 12, 16, 20, 24, 32] + shadowComplexity: layered + borderUsage: minimal +metadata: + bucket_id: managerbot-2026-04-30 + ui_library: "@squareup/managerbot-ui" + component_count: 103 + token_count: 459 + ai_elements_count: 34 +--- + +# Character + +Managerbot's design language is shadcn at heart — new-york style, lucide +icons, neutral baseColor — wearing Square's house font and dressed for +a long sit-down with an LLM. The default theme is monochrome (every +semantic surface chains to a `--neutral-*` step), so when chromatic +color does appear it carries meaning: red for destructive, green for +success, yellow for warning, lime/orange for trend, and a 15-stop chart +ramp for data. The system is dual-themed first, with `:root` and `.dark` +expressing the same vocabulary at inverted lightness, plus a +`force-light` utility for surfaces that must stay bright in dark mode. +What sets it apart from a vanilla shadcn build is the AI-elements +sub-tree — 34 components for conversation, prompt input, reasoning, +artifact, canvas, branch, plan, and tool — wired into the same token +cascade as the primitive shell. + +# Decisions + +### color-strategy + +Treat hue as opt-in semantics, not ambient identity. Every surface +token (`--background`, `--card`, `--popover`, `--primary`, `--secondary`, +`--muted`, `--accent`, `--sidebar`, `--panel`) chains to a step on the +neutral ramp; chromatic ramps exist only to express state, trend, and +chart data. Square's brand colors (`cashapp-*`, `square-*`) are present +in the token layer but unwired — available for marketing surfaces, not +the operating UI. + +**Evidence:** +- `--primary: var(--neutral-900)` resolves to `oklch(21.56% 0.0000 89.88)` (light) / `--neutral-200 → oklch(82.94% 0.0000 89.88)` (dark) — theme.css:384,473 +- `--secondary: var(--neutral-50)` resolves to `oklch(95.42% 0.0000 89.88)`; `--background: var(--white)` resolves to `oklch(100.00% 0.0000 89.88)` +- `--foreground: var(--neutral-950)` resolves to `oklch(16.98% 0.0000 89.88)`; `--neutral-500: oklch(56.34% 0.0000 89.88)` is the mid-ramp anchor; `--black: oklch(0.00% 0.0000 0.00)` is reserved +- `--destructive: var(--red-600)` resolves to `oklch(50.42% 0.1991 28.41)`; `--success: var(--green-600) → oklch(47.54% 0.0986 161.50)`; `--warning: var(--yellow-200) → oklch(82.83% 0.1680 85.60)` +- `--trend-up: var(--lime-600) → oklch(64.8% 0.2 131.684)`; `--trend-down: var(--orange-500) → oklch(70.5% 0.213 47.604)` +- `cashapp-*` and `square-*` ramps declared (20 stops each) but never referenced from a semantic token +- 396 color tokens / 338 distinct color values in the bucket + +### surface-hierarchy + +Name surfaces by intent at the semantic layer (`--background`, `--card`, +`--popover`, `--sidebar`, `--panel`) and add an explicit numeric elevation +scale (`--elevation-0/1/2`) for stacking context. The sidebar gets its +own slot family (`--sidebar`, `--sidebar-foreground`, `--sidebar-accent`, +`--sidebar-border`, `--sidebar-ring`, `--sidebar-primary`) so sidebar +restyling never bleeds into the main surface. + +**Evidence:** +- `--elevation-0: oklab(97.913% 0 -0.00011)` / `oklch(0% 0 0)` — outer chrome +- `--elevation-1: var(--white)` / `oklch(19.125% ...)` — primary surface +- `--elevation-2: oklch(95.815% ...)` / `oklch(28.094% ...)` — raised surface +- `--sidebar-accent: color-mix(in oklab, var(--foreground) 5%, transparent)` (light) / `10%` (dark) — semantic, not a fixed shade +- `--panel: #FCFCFC` (light) / `#181818` (dark) — the only hex literals at the semantic layer; chrome surface stays distinct from `--background`/`--card` +- 9 spacing-kind tokens including `--width-sidebar: 280px`, `--width-content-narrow: 48rem`, `--width-content-wide: 82rem` + +### shape-language + +Adopt a moderate-roundness vocabulary — every interactive and structural +surface picks a stop from the same 8-tier `--radius-*` scale (4 / 6 / 8 / +12 / 16 / 20 / 24 / 32 px). No pill, no sharp corner, no per-component +override token. The shape distinction between Button and Card is *which* +radius they pick, not different families. + +**Evidence:** +- `--radius-xs: 4px; --radius-sm: 6px; --radius-md: 8px; --radius-lg: 12px; --radius-xl: 16px; --radius-2xl: 20px; --radius-3xl: 24px; --radius-4xl: 32px` (theme.css:988-995) +- 8 radius tokens, 8 distinct radius values in the bucket — no aliases, no per-component radius +- `--border-width-hairline: 0.5px` and `@utility border-hairline` for sub-pixel dividers + +### typography-voice + +Use a 10-stop type ramp (`xs` 13 → `7xl` 56 px) with line-height and +letter-spacing baked into the token. Bodies sit at zero tracking; only +display sizes (`2xl` and up) carry negative letter-spacing, escalating +from `-0.2px` at `2xl` to `-2.2px` at `6xl`/`7xl`. Cash Sans is the only +bundled face, with three weight loads (Regular 400, Medium 500, Medium +again at 600) — there is no separate semibold cut, by design. + +**Evidence:** +- `--text-xs: 0.8125rem` (13px) → `--text-7xl: 3.5rem` (56px); 10 sizes total (theme.css:1011-1053) +- `--text-2xl--letter-spacing: -0.2px`, `3xl: -0.4px`, `4xl: -0.5px`, `5xl: -1.4px`, `6xl/7xl: -2.2px` +- `--text-xs/sm/base/lg/xl--letter-spacing: 0` — body sizes intentionally zero +- `--font-sans: 'Cash Sans', ui-sans-serif, system-ui, sans-serif, ...` (theme.css:547) +- `@font-face` declarations load CashSans-Regular (400), CashSans-Medium (500), CashSans-Medium again (600) from `cash-f.squarecdn.com` (globals.css:5-33) + +### elevation + +Build depth as a continuous numeric ramp, not as named roles. Eight +shadow tokens (2xs / xs / sm / md / lg / xl / 2xl / inner) compose a +fixed 2px ambient layer with a directional offset that scales 0→4→8→16→24 +→48 px. Dark mode does not redefine shadows — the ambient layer is faint +enough on dark surfaces and the directional layer carries the read. + +**Evidence:** +- `--shadow-2xs: 0 1px 0 0 rgba(0,0,0,0.05)` — flat highlight +- `--shadow-xs: 0 0 1px 0 rgba(0,0,0,0.05)` +- `--shadow-sm/md/lg/xl: 0 0 2px 0 rgba(0,0,0,0.1), 0 [4|8|16|24]px [8|16|32|48]px [-2|-4|-8|-12]px rgba(0,0,0,0.06–0.1)` — composed pairs (theme.css:1004-1007) +- `--shadow-2xl: 0 25px 50px -12px rgba(0,0,0,0.25)` — outlier, only one declared +- `--shadow-inner: inset 0 0 1px 0 rgba(0,0,0,0.40)` +- `.dark` block omits all `--shadow-*` declarations — single source for both themes + +### theming-architecture + +Cascade two layers: a raw color-ramp + scalar layer in `:root`, then a +semantic alias layer also in `:root` and re-declared under `.dark` (and +the `force-light` `@utility`). Tailwind's `@theme inline` block at the +end re-exports both layers as `--color-*` / `--text-*` / `--radius-*` / +`--shadow-*` so utility classes can resolve. Components read the +semantic tokens or the Tailwind utilities — never raw oklch. + +**Evidence:** +- `:root` carries 396 color tokens (raw ramps) + ~30 semantic aliases (`--background`, `--primary`, ...) +- `.dark` re-declares only the ~30 semantic aliases — raw ramps inherit +- `@utility force-light` re-declares the same semantic set against light values, used to pin chrome to a light register inside dark surfaces +- `@theme inline { --color-*: var(--*); }` re-exports every token for Tailwind v4 class-atom generation (theme.css:544-984) +- `@source "../**/*.{ts,tsx}"; @source "../**/*.stories.{ts,tsx}";` (globals.css:35-36) bounds Tailwind's content scan + +### token-architecture + +Hold every literal in the token layer; let alias chains carry semantics. +Of 459 declared tokens, ~30 semantic surface tokens (`--primary`, +`--card`, `--accent`, ...) chain via `var(--neutral-*)` to a raw step; +chart tokens (`--chart-1`..`--chart-15`) chain to chosen ramp stops +across sky/teal/cyan/emerald/lime/green; raw ramp tokens are leaves +(`oklch(...)`). The `@theme inline` re-exports add a second alias layer +for Tailwind class atoms but do not change semantics — they exist purely +so `bg-primary` resolves. + +**Evidence:** +- 459 canonical tokens (excluding `@theme inline` mechanical re-exports) +- 396 color tokens; 12 spacing/layout; 34 typography (10 size triples + font-sans + 3 line-height defaults); 8 radius; 8 shadow; 1 motion +- Alias chains observed in semantic surface tokens, semantic state tokens, chart slots, and sidebar slots +- Raw color ramps (e.g. `--neutral-50: oklch(95.42% 0.0000 89.88)`) are leaves — no chains +- The Tailwind palette extension (`--slate-*`, `--gray-*`, `--zinc-*`, `--stone-*`, `--rose-*`, `--orange-*`, `--amber-*`, `--lime-*`, `--emerald-*`, `--teal-*`, `--cyan-*`, `--sky-*`, `--indigo-*`, `--violet-*`, `--fuchsia-*`, `--pink-*`) is included for chart use only — present but not wired into semantic tokens + +### motion + +Motion is reserved for functional state transitions; no decorative +animation. The system declares exactly one motion token +(`--animate-highlights-tab-fade-in: 150ms ease forwards`) and three +keyframes (`highlights-progress-fill`, `highlights-progress-drain`, +`highlights-tab-fade-in`) that serve a single feature surface (tabbed +highlights with progress). Component code uses Radix's data-state +attributes and `motion`/`framer-motion` for entry/exit; no +`transition: all`, no hover-only animations. + +**Evidence:** +- `--animate-highlights-tab-fade-in: highlights-tab-fade-in 150ms ease forwards` (theme.css:1055) +- `@keyframes highlights-progress-fill / -drain / -tab-fade-in` (globals.css:44-72) +- `tw-animate-css` imported in globals.css:3 — provides `animate-in/out` utilities for Radix data-state +- `motion` (catalog dep) and `framer-motion` (catalog dep) used in `managerbot-web` for entry transitions +- 1 motion-kind token in the bucket — comparable repos with decorative motion register 5–15 + +### font-sourcing + +The Square brand face is the only bundled type. Cash Sans is loaded +from `cash-f.squarecdn.com` at Regular (400), Medium (500), and Medium +again at 600 (intentional — Square's typeface ships no Semibold cut). +The fallback chain is `ui-sans-serif, system-ui, sans-serif, "Apple +Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"`, +so brand absence falls back to system-native rather than a generic +serif. There is no `--font-mono` or `--font-serif` token in the system. + +**Evidence:** +- `@font-face` x3 for Cash Sans (Regular/Medium/Medium-as-600), `font-display: swap` (globals.css:5-33) +- Source: `https://cash-f.squarecdn.com/static/fonts/cashsans/woff2/CashSans-{Regular,Medium}.woff2` +- `--font-sans: 'Cash Sans', ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"` (theme.css:547) +- No `--font-mono` or `--font-serif` declared in the bucket — code blocks rely on UA defaults via Tailwind's `font-mono` class diff --git a/dogfood/managerbot/map.md b/dogfood/managerbot/map.md new file mode 100644 index 0000000..3c8e240 --- /dev/null +++ b/dogfood/managerbot/map.md @@ -0,0 +1,174 @@ +--- +schema: ghost.map/v1 +id: managerbot +repo: squareup/square-web/apps/managerbot +mapped_at: 2026-04-30 +platform: web +languages: + - { name: typescript, files: 3614, share: 0.92 } + - { name: json, files: 190, share: 0.05 } + - { name: yaml, files: 59, share: 0.015 } + - { name: markdown, files: 51, share: 0.013 } + - { name: javascript, files: 10, share: 0.003 } + - { name: css, files: 4, share: 0.001 } + - { name: shell, files: 4, share: 0.001 } +build_system: [nx, vite, pnpm] +package_manifests: + - libs/ai-sdk-kgoose-provider/package.json + - libs/managerbot-ai-server-tools/package.json + - libs/managerbot-arazzo-engine/package.json + - libs/managerbot-bff/package.json + - libs/managerbot-kgoose/package.json + - libs/managerbot-schemas/package.json + - libs/managerbot-types-protos/package.json + - libs/managerbot-ui/package.json +composition: + frameworks: + - { name: react, version: "19" } + - { name: react-router, version: "7" } + - { name: vite } + - { name: tailwindcss, version: "4" } + - { name: base-ui-react } + - { name: radix-ui } + - { name: motion } + - { name: vitest } + rendering: react-router-ssr + styling: + - tailwindcss + - css-vars + navigation: react-router +design_system: + paths: + - libs/managerbot-ui/src/components + - libs/managerbot-ui/src/components/ai-elements + - libs/managerbot-ui/src/styles + - libs/managerbot-ui/src/hooks + - libs/managerbot-ui/src/utils + entry_files: + - libs/managerbot-ui/components.json + - libs/managerbot-ui/src/styles/globals.css + - libs/managerbot-ui/src/styles/theme.css + - libs/managerbot-ui/package.json + token_source: inline + status: active +ui_surface: + include: + - libs/managerbot-ui/src/components/** + - libs/managerbot-ui/src/styles/** + - libs/managerbot-ui/src/hooks/** + - managerbot-web/src/components/** + - managerbot-web/src/routes/** + - managerbot-storybook/** + exclude: + - "**/node_modules/**" + - "**/dist/**" + - "**/*.spec.ts" + - "**/*.spec.tsx" + - "**/*.stories.tsx" + - libs/managerbot-ui/src/components/visx-charts/** + - libs/managerbot-ui/src/components/rjsf/** + - managerbot-e2e/** + - managerbot-eval/** +feature_areas: + - name: ui-primitives + paths: + - libs/managerbot-ui/src/components + sub_areas: + - shadcn-shell + - form-controls + - overlays + - layout + - name: ai-elements + paths: + - libs/managerbot-ui/src/components/ai-elements + sub_areas: + - conversation + - prompt-input + - reasoning + - artifact + - canvas + - tool + - name: charts + paths: + - libs/managerbot-ui/src/components/visx-charts + - name: web-app + paths: + - managerbot-web/src/routes + - managerbot-web/src/components + sub_areas: + - chat-conversation + - tasks + - pulse + - insights + - settings + - name: storybook + paths: + - managerbot-storybook + - name: design-tokens + paths: + - libs/managerbot-ui/src/styles +orientation_files: + - README.md + - CLAUDE.md + - libs/managerbot-ui/components.json + - libs/managerbot-ui/src/styles/theme.css + - libs/managerbot-ui/package.json +--- + +## Identity + +Managerbot is the TypeScript codebase behind go/managerbot — Square's +AI-powered seller assistant. It is one app within the larger `square-web` +Nx + pnpm monorepo, co-locating a React Router web app, an MCP server, a +shared UI library, evals, e2e tests, and a Storybook into a single +deploy unit. Sellers interact with it as a chat surface that drives +LLM-mediated tools against Square's APIs. + +## Topology + +The design system lives in `libs/managerbot-ui` — a workspace package +(`@squareup/managerbot-ui`) consumed by `managerbot-web`. It is a +shadcn-flavored library: 130-plus primitive components plus an +`ai-elements/` sub-tree of LLM chat surfaces (conversation, prompt-input, +reasoning, artifact, canvas, tool) sourced from the +`registry.ai-sdk.dev` registry. Tokens are inline in +`src/styles/theme.css`, layered under `globals.css`. Charts use visx, +forms use react-hook-form + rjsf, motion is `motion`/framer-motion. + +Customer-facing UI lives in two places. The library is the catalogue +(also showcased in `managerbot-storybook`), and `managerbot-web/src` is +the consuming app — its `routes/` tree (insights, tasks, pulse, session, +settings, labs, panels) and `components/` tree (chat-conversation, tools, +tasks-popover, pinned-widgets, pages) wire primitives into product +surfaces. + +Excludes follow the usual monorepo noise (`node_modules`, `dist`) plus +test artifacts (`*.spec.tsx`, `*.stories.tsx`, `managerbot-e2e`, +`managerbot-eval`) and library wrappers that aren't load-bearing for the +expression (`visx-charts`, `rjsf`). + +## Conventions + +The repo uses pnpm catalog protocol (`catalog:managerbot`) to pin shared +dependencies across the monorepo, and Nx targets to orchestrate builds +(`nx run managerbot:dev`, `:build`, `:test:e2e`). The UI library exposes +its surface via package `exports` rather than a shadcn `registry.json` +— `components.json` configures style "new-york", `baseColor: neutral`, +Lucide icons, and a single external registry alias (`@ai-elements`). + +Theme tokens follow Tailwind 4's `@theme inline` pattern: raw color +ramps in `:root`, a monochrome semantic layer +(`--background`/`--foreground`/`--primary`/...) defined twice for light +and `.dark`, and a `force-light` utility for forced color modes. The +font stack is "Cash Sans" (loaded from `cash-f.squarecdn.com`) over the +system sans fallback. Color ramps are oklch and span 18 stops from 50 +to 950 in custom +50 increments (`30/50/80/100/150/200/...`). Brand +hooks (`cashapp-*`, `square-*`) sit alongside the neutral/red/yellow/ +green/blue/purple core families, and Tailwind's full extended palette +(slate/gray/zinc/stone/rose/orange/amber/lime/emerald/teal/cyan/sky/ +indigo/violet/fuchsia/pink) is preserved for chart use. + +Orientation reading order is `README.md` (architecture overview) → +`CLAUDE.md` (agent context) → `components.json` (shadcn config) → +`libs/managerbot-ui/src/styles/theme.css` (the canonical token layer) +→ `libs/managerbot-ui/package.json` (dependency footprint). diff --git a/install/install.sh b/install/install.sh new file mode 100755 index 0000000..4c50b1d --- /dev/null +++ b/install/install.sh @@ -0,0 +1,197 @@ +#!/bin/sh +# Ghost — install the design-language scan + emit skill bundle. +# +# Usage: +# curl -fsSL https://raw.githubusercontent.com/block/ghost/main/install/install.sh | sh +# +# Flags (set via env or args): +# GHOST_AGENT=claude|cursor|codex|opencode Target agent (default: auto-detect) +# GHOST_DEST= Override install path +# GHOST_REF= Source ref (default: main) +# GHOST_SOURCE= Override fetch base URL (for testing +# with file:// or a local mirror) +# +# What gets installed: +# /ghost/ +# SKILL.md +# references/scan.md, map.md, survey.md, profile.md, schema.md +# assets/expression.template.md +# +# Exit codes: +# 0 installed +# 1 unsupported platform / no detect +# 2 download failed +# 3 destination already populated; pass --force to overwrite + +set -eu + +GHOST_REF="${GHOST_REF:-main}" +GHOST_SOURCE="${GHOST_SOURCE:-https://raw.githubusercontent.com/block/ghost/${GHOST_REF}}" +GHOST_PACKAGE_PATH="packages/ghost-expression/src/skill-bundle" +GHOST_BUNDLE_NAME="ghost" +FORCE=0 + +# --- arg parsing ------------------------------------------------------- + +while [ $# -gt 0 ]; do + case "$1" in + --force) FORCE=1 ;; + --agent) shift; GHOST_AGENT="${1:-}" ;; + --agent=*) GHOST_AGENT="${1#--agent=}" ;; + --dest) shift; GHOST_DEST="${1:-}" ;; + --dest=*) GHOST_DEST="${1#--dest=}" ;; + --ref) shift; GHOST_REF="${1:-}"; GHOST_SOURCE="https://raw.githubusercontent.com/block/ghost/${GHOST_REF}" ;; + --ref=*) GHOST_REF="${1#--ref=}"; GHOST_SOURCE="https://raw.githubusercontent.com/block/ghost/${GHOST_REF}" ;; + --source) shift; GHOST_SOURCE="${1:-}" ;; + --source=*) GHOST_SOURCE="${1#--source=}" ;; + -h|--help) + sed -n '2,/^$/p' "$0" | sed 's/^# \{0,1\}//' | sed '$d' + exit 0 ;; + *) + echo "ghost install: unknown flag '$1' (try --help)" >&2 + exit 1 ;; + esac + shift +done + +GHOST_AGENT="${GHOST_AGENT:-}" +GHOST_DEST="${GHOST_DEST:-}" + +# --- agent detection --------------------------------------------------- + +detect_agent() { + # Look at HOME-relative skill dirs in priority order. The first that + # exists wins. If none exist, default to claude — it's the most common + # and creating ~/.claude/skills/ is benign. + if [ -d "$HOME/.claude/skills" ] || [ -d "$HOME/.claude" ]; then + echo "claude"; return + fi + if [ -d "$HOME/.cursor/skills" ] || [ -d "$HOME/.cursor" ]; then + echo "cursor"; return + fi + if [ -d "$HOME/.codex/skills" ] || [ -d "$HOME/.config/codex" ]; then + echo "codex"; return + fi + if [ -d "$HOME/.opencode/skills" ] || [ -d "$HOME/.opencode" ]; then + echo "opencode"; return + fi + echo "claude" +} + +agent_dest_dir() { + case "$1" in + claude) echo "$HOME/.claude/skills" ;; + cursor) echo "$HOME/.cursor/skills" ;; + codex) echo "$HOME/.codex/skills" ;; + opencode) echo "$HOME/.opencode/skills" ;; + *) return 1 ;; + esac +} + +if [ -z "$GHOST_AGENT" ]; then + GHOST_AGENT="$(detect_agent)" +fi + +# Validate agent up-front. `exit` inside $() doesn't propagate, so we +# can't rely on agent_dest_dir to fail the script. +case "$GHOST_AGENT" in + claude|cursor|codex|opencode) ;; + *) + echo "ghost install: unsupported agent '$GHOST_AGENT' (claude|cursor|codex|opencode)" >&2 + exit 1 ;; +esac + +if [ -z "$GHOST_DEST" ]; then + GHOST_DEST="$(agent_dest_dir "$GHOST_AGENT")/$GHOST_BUNDLE_NAME" +fi + +# --- pre-flight -------------------------------------------------------- + +# Idempotency: if the dest already has SKILL.md, refuse unless --force. +if [ -e "$GHOST_DEST/SKILL.md" ] && [ "$FORCE" -ne 1 ]; then + printf '%s already populated.\n' "$GHOST_DEST" >&2 + printf 'Pass --force to reinstall.\n' >&2 + exit 3 +fi + +# Pick a downloader. curl preferred, wget fallback. +if command -v curl >/dev/null 2>&1; then + DL_CMD="curl -fsSL" +elif command -v wget >/dev/null 2>&1; then + DL_CMD="wget -qO-" +else + echo "ghost install: need curl or wget on PATH" >&2 + exit 1 +fi + +# --- fetch the manifest, then each file -------------------------------- + +# The manifest lists every file the bundle ships. We let it drive the +# install so the bundle can grow without script changes. +MANIFEST_URL="${GHOST_SOURCE}/install/manifest.json" + +fetch() { + url="$1" + # shellcheck disable=SC2086 + $DL_CMD "$url" +} + +printf 'Ghost — installing %s skill bundle\n' "$GHOST_AGENT" +printf ' source: %s\n' "$GHOST_SOURCE" +printf ' dest: %s\n' "$GHOST_DEST" +printf '\n' + +# Download manifest. We don't take a hard dependency on jq — parse the +# small file list with grep + sed since the schema is fixed. +MANIFEST="$(fetch "$MANIFEST_URL")" || { + printf 'Could not fetch manifest from %s\n' "$MANIFEST_URL" >&2 + exit 2 +} + +# Extract `package` (used to construct each file's source URL). +PACKAGE_PATH="$(printf '%s' "$MANIFEST" | sed -n 's/.*"package"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1)" +if [ -z "$PACKAGE_PATH" ]; then + PACKAGE_PATH="$GHOST_PACKAGE_PATH" +fi + +# Extract each file path under "files": [...]. The manifest is hand- +# authored, so a simple line-oriented extraction works without jq. +FILES="$(printf '%s' "$MANIFEST" \ + | awk ' + /"files"[[:space:]]*:/ { in_files = 1; next } + in_files && /\]/ { in_files = 0; next } + in_files { + gsub(/[",[:space:]]/, "") + if (length($0) > 0) print $0 + } + ')" + +if [ -z "$FILES" ]; then + echo "ghost install: manifest contained no files; nothing to install" >&2 + exit 2 +fi + +mkdir -p "$GHOST_DEST" + +count=0 +for rel in $FILES; do + src="${GHOST_SOURCE}/${PACKAGE_PATH}/${rel}" + dst="${GHOST_DEST}/${rel}" + mkdir -p "$(dirname "$dst")" + if ! fetch "$src" > "$dst"; then + printf ' ✗ failed: %s\n' "$rel" >&2 + rm -f "$dst" + exit 2 + fi + printf ' ✓ %s\n' "$rel" + count=$((count + 1)) +done + +printf '\nInstalled %d files to %s\n' "$count" "$GHOST_DEST" +printf '\n' +printf 'Next:\n' +printf ' cd \n' +printf ' Tell your agent: "Scan this project with ghost"\n' +printf '\n' +printf 'The agent will produce map.md → bucket.json → expression.md, then\n' +printf 'emit a /design-review slash command tuned to your design language.\n' diff --git a/install/manifest.json b/install/manifest.json new file mode 100644 index 0000000..9b17178 --- /dev/null +++ b/install/manifest.json @@ -0,0 +1,18 @@ +{ + "$schema": "ghost.install-manifest/v1", + "name": "ghost-expression", + "description": "Ghost design-language scan + emit recipes — author expression.md, validate it, emit a per-project /design-review slash command.", + "version": "0.1.0", + "source": { + "package": "packages/ghost-expression/src/skill-bundle" + }, + "files": [ + "SKILL.md", + "references/scan.md", + "references/map.md", + "references/survey.md", + "references/profile.md", + "references/schema.md", + "assets/expression.template.md" + ] +} diff --git a/packages/ghost-core/package.json b/packages/ghost-core/package.json index 0bf3f07..7ff7627 100644 --- a/packages/ghost-core/package.json +++ b/packages/ghost-core/package.json @@ -2,7 +2,7 @@ "name": "@ghost/core", "version": "0.0.0", "private": true, - "description": "Internal shared primitives for the Ghost toolchain — types, embedding math, target resolution, skill-bundle loader. Consumed by ghost-drift and the upcoming ghost-expression / ghost-fleet / ghost-map packages. Not published.", + "description": "Internal shared primitives for the Ghost toolchain — types, embedding math, target resolution, skill-bundle loader, ghost.bucket/v1 schema, ghost.map/v1 schema. Consumed by every ghost-* tool. Not published.", "license": "Apache-2.0", "type": "module", "main": "./dist/index.js", @@ -18,5 +18,8 @@ ], "scripts": { "build": "rm -rf dist && tsc --build --force" + }, + "dependencies": { + "zod": "^4.3.6" } } diff --git a/packages/ghost-core/src/bucket/fix-ids.ts b/packages/ghost-core/src/bucket/fix-ids.ts new file mode 100644 index 0000000..ce22d28 --- /dev/null +++ b/packages/ghost-core/src/bucket/fix-ids.ts @@ -0,0 +1,38 @@ +import { componentRowId, tokenRowId, valueRowId } from "./id.js"; +import type { Bucket, ComponentRow, TokenRow, ValueRow } from "./types.js"; + +/** + * Recompute every row's `id` from its content fields, producing a new + * bucket with deterministic IDs. + * + * Authoring flow: an agent writes bucket rows with `id: ""` (or any + * placeholder), then calls `recomputeBucketIds` to populate them, then + * runs `lintBucket` to validate. This avoids forcing the agent to compute + * SHA-256 hashes by hand for every row, while keeping the bucket + * schema's strict id requirement. + * + * The function is pure — input bucket is unchanged. + */ +export function recomputeBucketIds(bucket: Bucket): Bucket { + return { + ...bucket, + values: bucket.values.map( + (row): ValueRow => ({ + ...row, + id: valueRowId(row.source, row.kind, row.value, row.raw), + }), + ), + tokens: bucket.tokens.map( + (row): TokenRow => ({ + ...row, + id: tokenRowId(row.source, row.name), + }), + ), + components: bucket.components.map( + (row): ComponentRow => ({ + ...row, + id: componentRowId(row.source, row.name), + }), + ), + }; +} diff --git a/packages/ghost-core/src/bucket/id.ts b/packages/ghost-core/src/bucket/id.ts new file mode 100644 index 0000000..87fc449 --- /dev/null +++ b/packages/ghost-core/src/bucket/id.ts @@ -0,0 +1,54 @@ +import { createHash } from "node:crypto"; +import type { BucketSource } from "./types.js"; + +/** + * Deterministic ID generation for bucket rows. + * + * Two scans of the same `(target, commit)` over the same source content + * must produce identical IDs so that re-merging is idempotent and git + * diffs over `bucket.json` show only meaningful changes. Scans of + * different commits or different targets produce distinct IDs so that + * fleet-wide merges preserve every observation. + * + * IDs are 16-hex-char (8-byte) prefixes of SHA-256. At ~10^6 rows in the + * universe of all scans this gives collision probability under 2^-32. + */ + +const ID_LENGTH = 16; + +const VALUE_TAG = "value"; +const TOKEN_TAG = "token"; +const COMPONENT_TAG = "component"; + +function digest(...parts: (string | undefined)[]): string { + const hash = createHash("sha256"); + for (const part of parts) { + hash.update(part ?? ""); + hash.update("\x00"); + } + return hash.digest("hex").slice(0, ID_LENGTH); +} + +function sourceKey(source: BucketSource): [string, string] { + return [source.target, source.commit ?? ""]; +} + +export function valueRowId( + source: BucketSource, + kind: string, + value: string, + raw: string, +): string { + const [target, commit] = sourceKey(source); + return digest(target, commit, VALUE_TAG, kind, value, raw); +} + +export function tokenRowId(source: BucketSource, name: string): string { + const [target, commit] = sourceKey(source); + return digest(target, commit, TOKEN_TAG, name); +} + +export function componentRowId(source: BucketSource, name: string): string { + const [target, commit] = sourceKey(source); + return digest(target, commit, COMPONENT_TAG, name); +} diff --git a/packages/ghost-core/src/bucket/index.ts b/packages/ghost-core/src/bucket/index.ts new file mode 100644 index 0000000..3768f2e --- /dev/null +++ b/packages/ghost-core/src/bucket/index.ts @@ -0,0 +1,46 @@ +/** + * Public surface for `ghost.bucket/v1` — types, schemas, ID generation, + * lint, and merge. Consumed by `ghost-expression` and any future ghost + * tool that operates on bucket data. + */ + +export { recomputeBucketIds } from "./fix-ids.js"; +export { componentRowId, tokenRowId, valueRowId } from "./id.js"; +export { + BUCKET_FILENAME, + type BucketLintIssue, + type BucketLintReport, + type BucketLintSeverity, + lintBucket, +} from "./lint.js"; +export { mergeBuckets } from "./merge.js"; +export { + BucketSchema, + BucketSourceSchema, + ColorSpecSchema, + ComponentRowSchema, + RECOMMENDED_VALUE_KINDS, + TokenRowSchema, + ValueRowSchema, + ValueSpecSchema, +} from "./schema.js"; +export type { + BreakpointSpec, + Bucket, + BucketSource, + ColorSpec, + ComponentRow, + LayoutPrimitiveSpec, + MotionSpec, + RadiusSpec, + RecommendedValueKind, + RowBase, + ScalarUnit, + ShadowSpec, + SpacingSpec, + TokenRow, + TypographySpec, + UnknownSpec, + ValueRow, + ValueSpec, +} from "./types.js"; diff --git a/packages/ghost-core/src/bucket/lint.ts b/packages/ghost-core/src/bucket/lint.ts new file mode 100644 index 0000000..0cf5ec9 --- /dev/null +++ b/packages/ghost-core/src/bucket/lint.ts @@ -0,0 +1,138 @@ +import type { ZodIssue } from "zod"; +import { componentRowId, tokenRowId, valueRowId } from "./id.js"; +import { BucketSchema, RECOMMENDED_VALUE_KINDS } from "./schema.js"; +import type { Bucket } from "./types.js"; + +export type BucketLintSeverity = "error" | "warning" | "info"; + +export interface BucketLintIssue { + severity: BucketLintSeverity; + rule: string; + message: string; + /** Dotted path within the bucket (e.g. `values[3].id`). */ + path?: string; +} + +export interface BucketLintReport { + issues: BucketLintIssue[]; + errors: number; + warnings: number; + info: number; +} + +export const BUCKET_FILENAME = "bucket.json"; + +/** + * Lint a parsed bucket object against `ghost.bucket/v1`. + * + * Errors: schema violations (missing fields, wrong types, bad enum values). + * Warnings: unknown value kinds (open-enum policy), ID mismatches (a row's + * recorded `id` doesn't match what the deterministic generator would + * produce for its content), duplicate IDs within the same bucket. + */ +export function lintBucket(input: unknown): BucketLintReport { + const issues: BucketLintIssue[] = []; + + const result = BucketSchema.safeParse(input); + if (!result.success) { + for (const issue of zodIssues(result.error.issues)) { + issues.push(issue); + } + return finalize(issues); + } + + const bucket = result.data as Bucket; + + // Open-enum kind warnings. + bucket.values.forEach((row, idx) => { + if (!RECOMMENDED_VALUE_KINDS.includes(row.kind)) { + issues.push({ + severity: "warning", + rule: "value-kind-unknown", + message: `value row uses non-recommended kind '${row.kind}' — accepted, but cross-fleet tooling may not canonicalize it`, + path: `values[${idx}].kind`, + }); + } + }); + + // Deterministic-ID checks: each row's recorded id must match what the + // generator would produce for its content. Catches scanners that mint + // IDs incorrectly and breaks idempotent merge if not enforced. + bucket.values.forEach((row, idx) => { + const expected = valueRowId(row.source, row.kind, row.value, row.raw); + if (row.id !== expected) { + issues.push({ + severity: "warning", + rule: "id-mismatch", + message: `id '${row.id}' does not match generator output '${expected}' — re-derive via valueRowId(...) to keep merges idempotent`, + path: `values[${idx}].id`, + }); + } + }); + bucket.tokens.forEach((row, idx) => { + const expected = tokenRowId(row.source, row.name); + if (row.id !== expected) { + issues.push({ + severity: "warning", + rule: "id-mismatch", + message: `id '${row.id}' does not match generator output '${expected}'`, + path: `tokens[${idx}].id`, + }); + } + }); + bucket.components.forEach((row, idx) => { + const expected = componentRowId(row.source, row.name); + if (row.id !== expected) { + issues.push({ + severity: "warning", + rule: "id-mismatch", + message: `id '${row.id}' does not match generator output '${expected}'`, + path: `components[${idx}].id`, + }); + } + }); + + // Duplicate-id checks within a single section. (Cross-section duplicates + // are fine since IDs include a section tag.) Within-bucket duplicates + // mean the scanner emitted two rows with the same content, which the + // recorder should have merged. + for (const section of ["values", "tokens", "components"] as const) { + const seen = new Map(); + bucket[section].forEach((row, idx) => { + const prev = seen.get(row.id); + if (prev !== undefined) { + issues.push({ + severity: "error", + rule: "duplicate-id", + message: `duplicate id '${row.id}' in ${section} (also at ${section}[${prev}])`, + path: `${section}[${idx}].id`, + }); + } else { + seen.set(row.id, idx); + } + }); + } + + return finalize(issues); +} + +function zodIssues(issues: ZodIssue[]): BucketLintIssue[] { + return issues.map((issue) => ({ + severity: "error" as const, + rule: `schema/${issue.code}`, + message: issue.message, + path: issue.path.join("."), + })); +} + +function finalize(issues: BucketLintIssue[]): BucketLintReport { + let errors = 0; + let warnings = 0; + let info = 0; + for (const issue of issues) { + if (issue.severity === "error") errors++; + else if (issue.severity === "warning") warnings++; + else info++; + } + return { issues, errors, warnings, info }; +} diff --git a/packages/ghost-core/src/bucket/merge.ts b/packages/ghost-core/src/bucket/merge.ts new file mode 100644 index 0000000..84eea97 --- /dev/null +++ b/packages/ghost-core/src/bucket/merge.ts @@ -0,0 +1,62 @@ +import type { + Bucket, + BucketSource, + ComponentRow, + RowBase, + TokenRow, + ValueRow, +} from "./types.js"; + +/** + * Merge N buckets into one. Concat semantics with id-based dedup. + * + * Two scans of the same `(target, commit)` produce rows with identical + * IDs by construction — those rows are deduplicated to one (first wins). + * Two scans of different commits or different targets produce distinct + * IDs, so all observations survive. + * + * `sources` becomes the union of input sources, also deduped on + * `(target, commit)`. + * + * Idempotent: `mergeBuckets(b)` == `b`. Commutative on the rowset (order + * within sections may differ from input order but content is identical). + */ +export function mergeBuckets(...buckets: Bucket[]): Bucket { + if (buckets.length === 0) { + throw new Error("mergeBuckets requires at least one input bucket"); + } + return { + schema: "ghost.bucket/v1", + sources: dedupSources(buckets.flatMap((b) => b.sources)), + values: dedupRows(buckets.flatMap((b) => b.values)), + tokens: dedupRows(buckets.flatMap((b) => b.tokens)), + components: dedupRows(buckets.flatMap((b) => b.components)), + }; +} + +function dedupRows(rows: T[]): T[] { + const seen = new Set(); + const out: T[] = []; + for (const row of rows) { + if (seen.has(row.id)) continue; + seen.add(row.id); + out.push(row); + } + return out; +} + +function dedupSources(sources: BucketSource[]): BucketSource[] { + const seen = new Set(); + const out: BucketSource[] = []; + for (const source of sources) { + const key = `${source.target}\x00${source.commit ?? ""}`; + if (seen.has(key)) continue; + seen.add(key); + out.push(source); + } + return out; +} + +// Type re-exports kept narrow so consumers don't have to import from `types.js` +// just to use `mergeBuckets` results. +export type { Bucket, BucketSource, ComponentRow, TokenRow, ValueRow }; diff --git a/packages/ghost-core/src/bucket/schema.ts b/packages/ghost-core/src/bucket/schema.ts new file mode 100644 index 0000000..61120d8 --- /dev/null +++ b/packages/ghost-core/src/bucket/schema.ts @@ -0,0 +1,156 @@ +import { z } from "zod"; + +/** + * Zod schemas for `ghost.bucket/v1`. + * + * The `kind` field on value rows is intentionally open (a plain string). + * The validator does not reject unknown kinds — instead the lint step + * surfaces them as warnings so downstream tooling can canonicalize without + * blocking new scanners that emit experimental kinds. + */ + +const BucketSourceSchema = z.object({ + target: z.string().min(1), + commit: z.string().optional(), + scanned_at: z.string().min(1), + scanner_version: z.string().optional(), +}); + +const ScalarUnitSchema = z.object({ + scalar: z.number(), + unit: z.string().min(1), +}); + +const ColorSpecSchema = z.object({ + space: z.enum(["srgb", "p3", "rec2020", "lab", "oklch", "unknown"]), + hex: z.string().optional(), + rgb: z + .object({ + r: z.number(), + g: z.number(), + b: z.number(), + a: z.number().optional(), + }) + .optional(), + hsl: z + .object({ + h: z.number(), + s: z.number(), + l: z.number(), + a: z.number().optional(), + }) + .optional(), +}); + +const TypographySpecSchema = z.object({ + family: z.string().optional(), + weight: z.union([z.string(), z.number()]).optional(), + size: ScalarUnitSchema.optional(), + line_height: z.union([ScalarUnitSchema, z.string()]).optional(), + letter_spacing: ScalarUnitSchema.optional(), +}); + +const ShadowSpecSchema = z.object({ + offset_x: ScalarUnitSchema.optional(), + offset_y: ScalarUnitSchema.optional(), + blur: ScalarUnitSchema.optional(), + spread: ScalarUnitSchema.optional(), + color: z.string().optional(), + inset: z.boolean().optional(), +}); + +const MotionSpecSchema = z.object({ + duration_ms: z.number().optional(), + easing: z.string().optional(), +}); + +const LayoutPrimitiveSpecSchema = z.object({ + kind: z.string().min(1), + scalar: z.number().optional(), + unit: z.string().optional(), + raw: z.string().optional(), +}); + +const BreakpointSpecSchema = ScalarUnitSchema.extend({ + label: z.string().optional(), +}); + +/** + * Spec is open: any of the recommended specs, OR a generic record for + * unknown kinds. We don't bind kind→spec strictly here — the lint step + * surfaces mismatches as warnings so experimental scanners can iterate + * without schema changes. + */ +const ValueSpecSchema = z.union([ + ColorSpecSchema, + TypographySpecSchema, + ShadowSpecSchema, + MotionSpecSchema, + LayoutPrimitiveSpecSchema, + BreakpointSpecSchema, + ScalarUnitSchema, + z.record(z.string(), z.unknown()), +]); + +const RowBaseSchema = z.object({ + id: z.string().min(1), + source: BucketSourceSchema, +}); + +const ValueRowSchema = RowBaseSchema.extend({ + kind: z.string().min(1), + value: z.string().min(1), + raw: z.string(), + spec: ValueSpecSchema.optional(), + occurrences: z.number().int().nonnegative(), + files_count: z.number().int().nonnegative(), + usage: z.record(z.string(), z.number().int().nonnegative()).optional(), + role_hypothesis: z.string().optional(), +}); + +const TokenRowSchema = RowBaseSchema.extend({ + name: z.string().min(1), + alias_chain: z.array(z.string()), + resolved_value: z.string().min(1), + by_theme: z.record(z.string(), z.string()).optional(), + occurrences: z.number().int().nonnegative(), +}); + +const ComponentRowSchema = RowBaseSchema.extend({ + name: z.string().min(1), + discovered_via: z.string().min(1), + variants: z.array(z.string()).optional(), + sizes: z.array(z.string()).optional(), +}); + +export const BucketSchema = z.object({ + schema: z.literal("ghost.bucket/v1"), + sources: z.array(BucketSourceSchema).min(1), + values: z.array(ValueRowSchema), + tokens: z.array(TokenRowSchema), + components: z.array(ComponentRowSchema), +}); + +export { + BucketSourceSchema, + ColorSpecSchema, + ComponentRowSchema, + TokenRowSchema, + ValueRowSchema, + ValueSpecSchema, +}; + +/** + * Recommended value kinds. Used only by the lint step to surface unknown + * kinds as warnings — the schema accepts any string for `kind`. + */ +export const RECOMMENDED_VALUE_KINDS: readonly string[] = [ + "color", + "spacing", + "typography", + "radius", + "shadow", + "breakpoint", + "motion", + "layout-primitive", +]; diff --git a/packages/ghost-core/src/bucket/types.ts b/packages/ghost-core/src/bucket/types.ts new file mode 100644 index 0000000..3da0f94 --- /dev/null +++ b/packages/ghost-core/src/bucket/types.ts @@ -0,0 +1,175 @@ +/** + * Types for `ghost.bucket/v1` — the objective scan artifact. + * + * A bucket is the middle artifact in a scan: produced after topology + * (`map.md`) and before subjective interpretation (`expression.md`). It + * catalogues every concrete design value the agent observed in a target, + * with structured specs and per-row deterministic IDs. + * + * Merge semantics are concat-with-id-dedup. Two scans of the same target at + * the same commit produce identical IDs, so re-merging is idempotent. Two + * scans of different commits (or different targets) produce different IDs, + * so cross-bucket merges preserve every observation as its own row. + */ + +/** Where a scan came from. Denormalized onto every row in the bucket. */ +export interface BucketSource { + /** Target string the scan was pointed at — `github:owner/repo`, `./path`, etc. */ + target: string; + /** Git commit sha at scan time, when knowable. */ + commit?: string; + /** ISO 8601 timestamp the scan started. */ + scanned_at: string; + /** Version of the scanner that produced this row. */ + scanner_version?: string; +} + +/** Fields every row carries regardless of section. */ +export interface RowBase { + /** Deterministic hash of `(source.target, source.commit, kind-tag, content fields)`. */ + id: string; + /** Source attribution. Denormalized so rows survive merges with their origin. */ + source: BucketSource; +} + +// --- Value rows ---------------------------------------------------------- + +/** + * Recommended value kinds. The bucket schema treats `kind` as an open + * string — scanners may emit additional kinds (e.g. `z-index`, `opacity`, + * `cursor`, `gradient`, `iconography`) and validators warn rather than + * reject. The recommended set covers the common cross-fleet vocabulary. + */ +export type RecommendedValueKind = + | "color" + | "spacing" + | "typography" + | "radius" + | "shadow" + | "breakpoint" + | "motion" + | "layout-primitive"; + +export interface ColorSpec { + space: "srgb" | "p3" | "rec2020" | "lab" | "oklch" | "unknown"; + hex?: string; + rgb?: { r: number; g: number; b: number; a?: number }; + hsl?: { h: number; s: number; l: number; a?: number }; +} + +export interface ScalarUnit { + scalar: number; + unit: string; +} + +export interface SpacingSpec extends ScalarUnit {} +export interface RadiusSpec extends ScalarUnit {} +export interface BreakpointSpec extends ScalarUnit { + label?: string; +} + +export interface TypographySpec { + family?: string; + weight?: string | number; + size?: ScalarUnit; + line_height?: ScalarUnit | string; + letter_spacing?: ScalarUnit; +} + +export interface ShadowSpec { + offset_x?: ScalarUnit; + offset_y?: ScalarUnit; + blur?: ScalarUnit; + spread?: ScalarUnit; + color?: string; + inset?: boolean; +} + +export interface MotionSpec { + duration_ms?: number; + easing?: string; +} + +export interface LayoutPrimitiveSpec { + /** Sub-kind: `max-width`, `container-padding`, `grid-track`, `gutter`, etc. Open. */ + kind: string; + scalar?: number; + unit?: string; + raw?: string; +} + +/** Fall-through for unknown / open-enum kinds. */ +export type UnknownSpec = Record; + +export type ValueSpec = + | ColorSpec + | SpacingSpec + | TypographySpec + | RadiusSpec + | ShadowSpec + | BreakpointSpec + | MotionSpec + | LayoutPrimitiveSpec + | UnknownSpec; + +export interface ValueRow extends RowBase { + /** One of `RecommendedValueKind` or an extension kind. Open string. */ + kind: string; + /** Canonical string form (`#f97316`, `8px`, `Inter`). */ + value: string; + /** As-it-appeared in source (`#F97316`, `bg-orange-500`, `var(--brand)`). */ + raw: string; + /** Structured spec per kind. */ + spec?: ValueSpec; + /** Total observed count of this value within this scan. */ + occurrences: number; + /** Distinct files that contained this value. */ + files_count: number; + /** Usage breakdown by context (`className`, `css_var`, `inline_style`, etc.). */ + usage?: Record; + /** Agent-assigned role guess (`brand-primary`, `surface-elevated`). */ + role_hypothesis?: string; +} + +// --- Token rows --------------------------------------------------------- + +export interface TokenRow extends RowBase { + /** Token name as declared in source — e.g. `--color-brand-primary`. */ + name: string; + /** + * Resolution chain from this token to its terminal value. Empty array + * means the token is a leaf (defined inline as a literal). Length > 0 + * means each step indirected through another named token. + */ + alias_chain: string[]; + /** End-of-chain literal value. */ + resolved_value: string; + /** Per-theme variants when the token resolves differently across themes. */ + by_theme?: Record; + /** Total observed usage count of this token within the scan. */ + occurrences: number; +} + +// --- Component rows ----------------------------------------------------- + +export interface ComponentRow extends RowBase { + name: string; + /** Where the component was discovered — `registry.json`, `heuristic`, etc. */ + discovered_via: string; + variants?: string[]; + sizes?: string[]; +} + +// --- Bucket -------------------------------------------------------------- + +export interface Bucket { + schema: "ghost.bucket/v1"; + /** + * Source(s) the bucket came from. Always an array — pre-merge buckets + * have length 1, merged buckets have N entries (one per source scan). + */ + sources: BucketSource[]; + values: ValueRow[]; + tokens: TokenRow[]; + components: ComponentRow[]; +} diff --git a/packages/ghost-core/src/decision-vocabulary.ts b/packages/ghost-core/src/decision-vocabulary.ts new file mode 100644 index 0000000..0ede518 --- /dev/null +++ b/packages/ghost-core/src/decision-vocabulary.ts @@ -0,0 +1,265 @@ +/** + * Canonical decision-dimension vocabulary. + * + * Free-form `decisions[].dimension` slugs are great for authoring but bad + * for fleet aggregation: ghost-ui's `color-strategy` and a hypothetical + * Cash app's `color-system` describe the same axis under different names, + * and N-way overlap on incidentally-shared labels is not a basis for + * cross-system distance. + * + * The fix is a small controlled vocabulary. Profilers pick from this list + * first; non-canonical slugs are still permitted (the schema allows any + * string), but the recommended pattern is to pair them with a + * `dimension_kind` that maps to a canonical slug. Lint warns when a + * non-canonical dimension has no canonical kind. Fleet-rollup primitives + * group by `dimension_kind` (or by `dimension` when it's already + * canonical) so the decision-overlap distance axis becomes meaningful. + * + * The list below was derived from the actual decisions produced by + * profiling ghost-ui — these are not invented categories, they are the + * orthogonal-ish axes that one real expression already surfaces. + */ +export const CANONICAL_DECISION_DIMENSIONS = [ + "color-strategy", + "surface-hierarchy", + "shape-language", + "typography-voice", + "spatial-system", + "density", + "motion", + "elevation", + "theming-architecture", + "interactive-patterns", + "token-architecture", + "font-sourcing", +] as const; + +export type CanonicalDecisionDimension = + (typeof CANONICAL_DECISION_DIMENSIONS)[number]; + +const CANONICAL_SET: ReadonlySet = new Set( + CANONICAL_DECISION_DIMENSIONS, +); + +/** + * Direct synonyms — common slug variants we've observed or expect, mapped + * to the canonical dimension. Lookup is exact-match (post-normalization). + */ +const SYNONYMS: Readonly> = { + // color-strategy + "color-system": "color-strategy", + "color-philosophy": "color-strategy", + "color-approach": "color-strategy", + "palette-strategy": "color-strategy", + "palette-system": "color-strategy", + "hue-strategy": "color-strategy", + // surface-hierarchy + "surface-vocabulary": "surface-hierarchy", + "surface-system": "surface-hierarchy", + "background-hierarchy": "surface-hierarchy", + "background-system": "surface-hierarchy", + // shape-language + "radius-philosophy": "shape-language", + "radius-strategy": "shape-language", + "corner-treatment": "shape-language", + "corner-radii": "shape-language", + geometry: "shape-language", + // typography-voice + "type-voice": "typography-voice", + "type-stack": "typography-voice", + "type-hierarchy": "typography-voice", + "typographic-voice": "typography-voice", + "typography-system": "typography-voice", + // spatial-system + spacing: "spatial-system", + "spacing-scale": "spatial-system", + "spacing-system": "spatial-system", + "layout-rhythm": "spatial-system", + // density + compactness: "density", + "control-density": "density", + // motion + animation: "motion", + "motion-language": "motion", + "motion-system": "motion", + "animation-philosophy": "motion", + // elevation + "shadow-system": "elevation", + "shadow-vocabulary": "elevation", + "depth-language": "elevation", + // theming-architecture + theming: "theming-architecture", + "theme-architecture": "theming-architecture", + "theme-system": "theming-architecture", + themeability: "theming-architecture", + // interactive-patterns + "interaction-patterns": "interactive-patterns", + "focus-treatment": "interactive-patterns", + "hover-system": "interactive-patterns", + "interaction-design": "interactive-patterns", + // token-architecture + "token-system": "token-architecture", + "token-cascade": "token-architecture", + "token-layering": "token-architecture", + // font-sourcing + "font-stack": "font-sourcing", + "font-strategy": "font-sourcing", + "font-loading": "font-sourcing", + "font-bundling": "font-sourcing", +}; + +/** + * Token-level affinity — when a slug has no direct synonym, score it by + * how strongly its dash-separated tokens evoke each canonical dimension. + * The token "color" alone is a strong signal for color-strategy; "shadow" + * is strong for elevation. Used by `closestCanonical` as a fallback. + * + * Each entry is `[token, dimension]`. A token may map to multiple + * dimensions (e.g. "font" hints both font-sourcing and typography-voice); + * the scorer sums signals across dimensions and returns the strongest. + */ +const TOKEN_HINTS: ReadonlyArray< + readonly [string, CanonicalDecisionDimension] +> = [ + ["color", "color-strategy"], + ["palette", "color-strategy"], + ["hue", "color-strategy"], + ["chroma", "color-strategy"], + ["surface", "surface-hierarchy"], + ["background", "surface-hierarchy"], + ["bg", "surface-hierarchy"], + ["radius", "shape-language"], + ["radii", "shape-language"], + ["corner", "shape-language"], + ["shape", "shape-language"], + ["pill", "shape-language"], + ["typography", "typography-voice"], + ["type", "typography-voice"], + ["typographic", "typography-voice"], + ["heading", "typography-voice"], + ["spacing", "spatial-system"], + ["space", "spatial-system"], + ["spatial", "spatial-system"], + ["layout", "spatial-system"], + ["rhythm", "spatial-system"], + ["density", "density"], + ["compact", "density"], + ["motion", "motion"], + ["animation", "motion"], + ["transition", "motion"], + ["shadow", "elevation"], + ["elevation", "elevation"], + ["depth", "elevation"], + ["theme", "theming-architecture"], + ["theming", "theming-architecture"], + ["themeable", "theming-architecture"], + ["interaction", "interactive-patterns"], + ["interactive", "interactive-patterns"], + ["focus", "interactive-patterns"], + ["hover", "interactive-patterns"], + ["token", "token-architecture"], + ["alias", "token-architecture"], + ["cascade", "token-architecture"], + ["font", "font-sourcing"], + ["typeface", "font-sourcing"], +]; + +/** + * Normalize a dimension slug for lookup: trim, lowercase, collapse + * separators (`_`, ` `, repeated `-`) into single dashes. + */ +function normalize(slug: string): string { + return slug + .trim() + .toLowerCase() + .replace(/[_\s]+/g, "-") + .replace(/-+/g, "-") + .replace(/^-|-$/g, ""); +} + +/** + * Returns true when `slug` is in the canonical vocabulary (after + * normalization). Use to gate fleet-aggregation paths that require + * commensurable dimension labels across members. + */ +export function isCanonicalDimension( + slug: string, +): slug is CanonicalDecisionDimension { + return CANONICAL_SET.has(normalize(slug)); +} + +/** + * Suggest the closest canonical dimension for a free-form slug. + * + * Resolution order: + * 1. Exact canonical match (after normalization). + * 2. Direct synonym lookup. + * 3. Token-affinity scoring across `TOKEN_HINTS` — wins when a single + * dimension scores strictly higher than all others. + * 4. `null` when there's no clear winner. Callers should treat null as + * "this slug is genuinely novel; lint warns and the profile keeps it + * long-tail." + * + * Pure / deterministic. No I/O. + */ +export function closestCanonical( + slug: string, +): CanonicalDecisionDimension | null { + if (!slug) return null; + const norm = normalize(slug); + if (!norm) return null; + + if (CANONICAL_SET.has(norm)) return norm as CanonicalDecisionDimension; + + const synonym = SYNONYMS[norm]; + if (synonym) return synonym; + + const tokens = norm.split("-").filter(Boolean); + if (tokens.length === 0) return null; + + const scores = new Map(); + for (const token of tokens) { + for (const [hint, dim] of TOKEN_HINTS) { + if (hint === token) { + scores.set(dim, (scores.get(dim) ?? 0) + 1); + } + } + } + if (scores.size === 0) return null; + + let best: CanonicalDecisionDimension | null = null; + let bestScore = 0; + let tied = false; + for (const [dim, score] of scores) { + if (score > bestScore) { + best = dim; + bestScore = score; + tied = false; + } else if (score === bestScore) { + tied = true; + } + } + return tied ? null : best; +} + +/** + * Resolve a decision's effective canonical dimension for fleet rollup: + * prefer an explicit `dimension_kind` (when it's canonical), otherwise + * fall back to the slug if it's canonical, otherwise null. + * + * The fleet aggregator groups decisions by this resolved value; null + * means the decision lives in the long tail and is reported per-member, + * not aggregated. + */ +export function resolveDecisionKind(decision: { + dimension: string; + dimension_kind?: string; +}): CanonicalDecisionDimension | null { + if (decision.dimension_kind) { + const norm = normalize(decision.dimension_kind); + if (CANONICAL_SET.has(norm)) return norm as CanonicalDecisionDimension; + } + const norm = normalize(decision.dimension); + if (CANONICAL_SET.has(norm)) return norm as CanonicalDecisionDimension; + return null; +} diff --git a/packages/ghost-core/src/embedding/colors.ts b/packages/ghost-core/src/embedding/colors.ts index c9931a0..983c498 100644 --- a/packages/ghost-core/src/embedding/colors.ts +++ b/packages/ghost-core/src/embedding/colors.ts @@ -265,6 +265,22 @@ export function colorToSemanticColor( return { role, value, oklch: oklch ?? undefined }; } +/** + * Resolve a color's oklch tuple, computing on-the-fly from `value` if the + * field is missing. Defensive backstop for palette comparisons — without + * this, hex-only colors land in the "unmatched" branch and contribute + * distance 1 even when both sides have the same hex. + * + * `loadExpression` (in ghost-expression) already backfills oklch on read; + * this fallback covers third-party producers that emit hex-only. + */ +export function resolveColorOklch( + c: SemanticColor, +): [number, number, number] | null { + if (c.oklch && c.oklch.length === 3) return c.oklch; + return parseColorToOklch(c.value); +} + export function classifySaturation( colors: SemanticColor[], ): "muted" | "vibrant" | "mixed" { diff --git a/packages/ghost-core/src/embedding/compare.ts b/packages/ghost-core/src/embedding/compare.ts index 0e9a160..705ee90 100644 --- a/packages/ghost-core/src/embedding/compare.ts +++ b/packages/ghost-core/src/embedding/compare.ts @@ -3,6 +3,7 @@ import type { Expression, ExpressionComparison, } from "../types.js"; +import { resolveColorOklch } from "./colors.js"; import { computeDriftVectors } from "./vector.js"; export interface CompareOptions { @@ -92,8 +93,17 @@ function comparePalette(a: Expression, b: Expression): DimensionDelta { for (const role of allDominantRoles) { const ca = aByRole.get(role); const cb = bByRole.get(role); - if (ca?.oklch && cb?.oklch) { - distances.push(oklchDistance(ca.oklch, cb.oklch)); + if (!ca || !cb) continue; + const oa = resolveColorOklch(ca); + const ob = resolveColorOklch(cb); + if (oa && ob) { + distances.push(oklchDistance(oa, ob)); + matchedA.add(role); + matchedB.add(role); + } else if (ca.value === cb.value) { + // Both hex-only on a non-parseable value — but the values match. + // Treat as identical rather than falling through to "unmatched". + distances.push(0); matchedA.add(role); matchedB.add(role); } @@ -106,8 +116,16 @@ function comparePalette(a: Expression, b: Expression): DimensionDelta { for (let i = 0; i < unmatchedCount; i++) { const ca = unmatchedA[i]; const cb = unmatchedB[i]; - if (ca?.oklch && cb?.oklch) { - distances.push(oklchDistance(ca.oklch, cb.oklch)); + if (!ca || !cb) { + distances.push(1); + continue; + } + const oa = resolveColorOklch(ca); + const ob = resolveColorOklch(cb); + if (oa && ob) { + distances.push(oklchDistance(oa, ob)); + } else if (ca.value === cb.value) { + distances.push(0); } else { distances.push(1); } @@ -133,8 +151,13 @@ function comparePalette(a: Expression, b: Expression): DimensionDelta { for (const role of sharedRoles) { const ca = a.palette.semantic.find((c) => c.role === role); const cb = b.palette.semantic.find((c) => c.role === role); - if (ca?.oklch && cb?.oklch) { - distances.push(oklchDistance(ca.oklch, cb.oklch)); + if (!ca || !cb) continue; + const oa = resolveColorOklch(ca); + const ob = resolveColorOklch(cb); + if (oa && ob) { + distances.push(oklchDistance(oa, ob)); + } else if (ca.value === cb.value) { + distances.push(0); } } diff --git a/packages/ghost-core/src/embedding/describe.ts b/packages/ghost-core/src/embedding/describe.ts index 40194c9..786da11 100644 --- a/packages/ghost-core/src/embedding/describe.ts +++ b/packages/ghost-core/src/embedding/describe.ts @@ -13,9 +13,6 @@ export function describeExpression(fp: Expression): string { // Observation (Layer 1) — prepend when available for richer semantic embedding if (fp.observation) { sections.push(fp.observation.summary); - if (fp.observation.distinctiveTraits.length > 0) { - sections.push(`${fp.observation.distinctiveTraits.join(". ")}.`); - } } // Design decisions (Layer 2) diff --git a/packages/ghost-core/src/embedding/index.ts b/packages/ghost-core/src/embedding/index.ts index ce7f0a6..62bb309 100644 --- a/packages/ghost-core/src/embedding/index.ts +++ b/packages/ghost-core/src/embedding/index.ts @@ -4,6 +4,7 @@ export { colorToSemanticColor, contrastScore, parseColorToOklch, + resolveColorOklch, saturationScore, } from "./colors.js"; export type { CompareOptions } from "./compare.js"; diff --git a/packages/ghost-core/src/index.ts b/packages/ghost-core/src/index.ts index 5566283..2c3efc2 100644 --- a/packages/ghost-core/src/index.ts +++ b/packages/ghost-core/src/index.ts @@ -1,4 +1,52 @@ // --- Embedding primitives --- + +// --- Bucket (ghost.bucket/v1) --- +export { + type BreakpointSpec, + BUCKET_FILENAME, + type Bucket, + type BucketLintIssue, + type BucketLintReport, + type BucketLintSeverity, + BucketSchema, + type BucketSource, + BucketSourceSchema, + type ColorSpec, + ColorSpecSchema, + type ComponentRow, + ComponentRowSchema, + componentRowId, + type LayoutPrimitiveSpec, + lintBucket, + type MotionSpec, + mergeBuckets, + type RadiusSpec, + RECOMMENDED_VALUE_KINDS, + type RecommendedValueKind, + type RowBase, + recomputeBucketIds, + type ScalarUnit, + type ShadowSpec, + type SpacingSpec, + type TokenRow, + TokenRowSchema, + type TypographySpec, + tokenRowId, + type UnknownSpec, + type ValueRow, + ValueRowSchema, + type ValueSpec, + ValueSpecSchema, + valueRowId, +} from "./bucket/index.js"; +// --- Decision vocabulary (controlled list for fleet aggregation) --- +export { + CANONICAL_DECISION_DIMENSIONS, + type CanonicalDecisionDimension, + closestCanonical, + isCanonicalDimension, + resolveDecisionKind, +} from "./decision-vocabulary.js"; export type { CompareOptions, RoleCandidate } from "./embedding/index.js"; export { classifyContrast, @@ -17,11 +65,35 @@ export { parseColorToOklch, saturationScore, } from "./embedding/index.js"; - +// --- Map (ghost.map/v1) --- +export { + type GitInfo, + type InventoryOutput, + type LanguageHistogramEntry, + MAP_FILENAME, + type MapFrontmatter, + MapFrontmatterSchema, + REQUIRED_BODY_SECTIONS, + type RequiredBodySection, + type TopLevelEntry, +} from "./map/index.js"; +// --- Perceptual prior (drift severity calibration) --- +export { + computeRuleSeverity, + DEFAULT_MATCH, + DEFAULT_TOLERANCE, + escalateForPresence, + escalateTier, + PERCEPTUAL_TIER, + type PerceptualTier, + resolveMatchShape, + resolveTolerance, + TIER_SEVERITY, + tierForCanonical, +} from "./perceptual-prior.js"; // --- Skill bundle loader --- export type { SkillBundleFile } from "./skill-bundle-loader.js"; export { loadSkillBundle } from "./skill-bundle-loader.js"; - // --- Target resolution --- export { resolveTarget } from "./target-resolver.js"; @@ -37,12 +109,12 @@ export type { CSSVarsMap, DesignDecision, DesignObservation, - DesignRole, DetectedFormat, DimensionAck, DimensionDelta, DimensionStance, DivergenceClass, + DriftSeverity, DriftVector, DriftVelocity, EmbeddingConfig, @@ -63,6 +135,9 @@ export type { RegistryItem, RegistryItemType, ResolvedRegistry, + Rule, + RuleKind, + RuleMatchShape, RuleSeverity, SampledFile, SampledMaterial, diff --git a/packages/ghost-core/src/map/index.ts b/packages/ghost-core/src/map/index.ts new file mode 100644 index 0000000..32ba5fe --- /dev/null +++ b/packages/ghost-core/src/map/index.ts @@ -0,0 +1,23 @@ +/** + * Public surface for `ghost.map/v1` schema and types. + * + * Map authoring (`inventory`, `lint`) lives in `ghost-expression` (the tool + * that owns the recipe). The schema/types live here so any ghost tool that + * reads `map.md` can do so via `@ghost/core` without depending on the + * authoring CLI. + */ + +export { + type MapFrontmatter, + MapFrontmatterSchema, + REQUIRED_BODY_SECTIONS, + type RequiredBodySection, +} from "./schema.js"; +export type { + GitInfo, + InventoryOutput, + LanguageHistogramEntry, + TopLevelEntry, +} from "./types.js"; + +export const MAP_FILENAME = "map.md"; diff --git a/packages/ghost-map/src/core/schema.ts b/packages/ghost-core/src/map/schema.ts similarity index 100% rename from packages/ghost-map/src/core/schema.ts rename to packages/ghost-core/src/map/schema.ts diff --git a/packages/ghost-map/src/core/types.ts b/packages/ghost-core/src/map/types.ts similarity index 98% rename from packages/ghost-map/src/core/types.ts rename to packages/ghost-core/src/map/types.ts index 82e2eb5..5a0e139 100644 --- a/packages/ghost-map/src/core/types.ts +++ b/packages/ghost-core/src/map/types.ts @@ -1,5 +1,5 @@ /** - * Shared types for the ghost-map package. + * Shared types for `ghost.map/v1`. * * The inventory shape is the deterministic facts the CLI emits; the recipe * synthesizes the final `map.md` from these signals plus its own reads. diff --git a/packages/ghost-core/src/perceptual-prior.ts b/packages/ghost-core/src/perceptual-prior.ts new file mode 100644 index 0000000..0949652 --- /dev/null +++ b/packages/ghost-core/src/perceptual-prior.ts @@ -0,0 +1,215 @@ +/** + * Ghost's perceptual prior — the opinionated stance that drift severity + * should track *how loudly a change registers visually*, not just whether + * it deviates from a recorded value. + * + * Three perceptual tiers: + * + * - **loud**: visible at first glance, no inspection required. Color + * and typeface family are loud — a new color or font is the change + * everyone notices. + * - **structural**: visible on inspection or interaction. Radius + * philosophy (pill vs. boxy), elevation vocabulary, focus treatment. + * Pill among boxes screams; the wrong shadow on a flat system jars. + * - **rhythmic**: visible only as a system property. Spacing scale + * adherence, density, motion duration. Individual deviations are + * nearly imperceptible — the rhythm matters in aggregate. + * + * Two cross-cutting rules: + * + * 1. **Match shape** is per-`RuleKind`: color is `exact`, spacing is + * `band`, type-size is `percent`, radius/shadow are `structural`. + * Defaults are sensible; per-rule overrides remain available. + * 2. **Presence/absence escalation**: when bucket count for a + * dimension is ≤ `presence_floor`, escalate the rule one tier. + * Sparsity is the design decision — adding to a silent dimension + * is the loudest possible change regardless of base tier. + * + * Tier membership is a position: projects can override per-rule severity + * but cannot remap a dimension's tier. The tiers are the product. + */ + +import type { CanonicalDecisionDimension } from "./decision-vocabulary.js"; +import type { DriftSeverity, Rule, RuleKind, RuleMatchShape } from "./types.js"; + +// --- Tier table --------------------------------------------------------- + +export type PerceptualTier = "loud" | "structural" | "rhythmic"; + +/** + * Maps each canonical dimension to its perceptual tier. The mapping is a + * position, not configuration — see module docstring. + * + * Notes on a few placements: + * - `typography-voice` is structural at the dimension level; a foreign + * font *family* is loud (handled by `RuleKind: "type-family"`), while + * size-detail drift is rhythmic (handled by `RuleKind: "type-size"`). + * Per-rule kind escalation handles that split. + * - `interactive-patterns` is structural — focus rings register on + * interaction, not at first glance. + * - `theming-architecture` and `token-architecture` are rhythmic — + * they're plumbing, perceptible only via downstream symptoms. + */ +export const PERCEPTUAL_TIER: Readonly< + Record +> = { + "color-strategy": "loud", + "font-sourcing": "loud", + "typography-voice": "structural", + "shape-language": "structural", + elevation: "structural", + "surface-hierarchy": "structural", + "interactive-patterns": "structural", + "spatial-system": "rhythmic", + density: "rhythmic", + motion: "rhythmic", + "theming-architecture": "rhythmic", + "token-architecture": "rhythmic", +}; + +/** + * Per-tier default severity for emitted reviewer rules. The emitter writes + * the resolved severity into the slash command so the reader sees a flat + * Critical / Serious / Nit grouping rather than a per-dimension layout. + */ +export const TIER_SEVERITY: Readonly> = { + loud: "critical", + structural: "serious", + rhythmic: "nit", +}; + +// --- Match shape and tolerance defaults -------------------------------- + +/** + * Default match shape per rule kind. Color demands exact equality (any + * non-allowed hex is drift). Spacing tolerates a small absolute band + * because 7px-vs-8px is invisible. Type size uses a percentage band + * because 14→15px is invisible but 14→24px is loud. Radius and shadow + * are structural — pill vs. non-pill matters more than 999 vs. 998. + */ +export const DEFAULT_MATCH: Readonly> = { + color: "exact", + radius: "structural", + spacing: "band", + "type-size": "percent", + "type-family": "exact", + "type-weight": "exact", + shadow: "structural", + motion: "exact", +}; + +/** + * Default tolerance for each match shape. Absent for `exact` and + * `structural` (no tolerance applies). Used when a rule selects a match + * shape but doesn't specify a tolerance. + */ +export const DEFAULT_TOLERANCE: Readonly< + Record +> = { + exact: undefined, + structural: undefined, + band: 2, // ±2 in source unit (typically px) + percent: 0.1, // ±10% relative +}; + +// --- Severity computation ---------------------------------------------- + +const TIER_ORDER: PerceptualTier[] = ["rhythmic", "structural", "loud"]; + +/** + * Escalate a tier one step toward `loud`. `loud` saturates — escalating + * a loud rule against an absent dimension is still critical. + */ +export function escalateTier(tier: PerceptualTier): PerceptualTier { + const idx = TIER_ORDER.indexOf(tier); + if (idx < 0) return tier; + return TIER_ORDER[Math.min(idx + 1, TIER_ORDER.length - 1)] as PerceptualTier; +} + +/** + * Resolve a canonical dimension to its perceptual tier. Returns + * `structural` for unknown / non-canonical inputs — the conservative + * default. The emitter / lint should warn on non-canonical rules so + * they're caught at authoring time. + */ +export function tierForCanonical( + canonical: string | undefined, +): PerceptualTier { + if (!canonical) return "structural"; + const tier = (PERCEPTUAL_TIER as Record)[ + canonical + ]; + return tier ?? "structural"; +} + +/** + * Apply presence/absence escalation: when `bucketCount <= presenceFloor`, + * the dimension is silent (or near-silent) in the project, so any rule + * guarding it is one tier louder than its base. + * + * `presenceFloor` defaults to 0 — only completely-absent dimensions + * trigger escalation by default. Rules that want softer escalation + * (motion in a system with 1–2 structural transitions, say) can set a + * higher floor. + */ +export function escalateForPresence( + base: PerceptualTier, + bucketCount: number, + presenceFloor = 0, +): PerceptualTier { + if (bucketCount <= presenceFloor) return escalateTier(base); + return base; +} + +/** + * Compute the final severity for a rule, given its canonical dimension + * and the bucket count for that dimension in the current expression. + * + * Resolution order: + * 1. Explicit `rule.severity` wins outright. + * 2. Otherwise, base tier from `rule.canonical` → `tierForCanonical`. + * 3. Apply presence/absence escalation against `rule.presence_floor` + * (default 0) and the supplied `bucketCount`. + * 4. Map tier → severity via `TIER_SEVERITY`. + * + * Pure / deterministic. + */ +export function computeRuleSeverity( + rule: Pick, + bucketCount: number, +): DriftSeverity { + if (rule.severity) return rule.severity; + const baseTier = tierForCanonical(rule.canonical); + const finalTier = escalateForPresence( + baseTier, + bucketCount, + rule.presence_floor ?? 0, + ); + return TIER_SEVERITY[finalTier]; +} + +/** + * Compute the final match shape for a rule. Explicit `rule.match` wins; + * otherwise the default for the rule's kind. Returns `exact` when neither + * is set — the most conservative shape. + */ +export function resolveMatchShape( + rule: Pick, +): RuleMatchShape { + if (rule.match) return rule.match; + if (rule.kind) return DEFAULT_MATCH[rule.kind]; + return "exact"; +} + +/** + * Compute the final tolerance for a rule. Explicit `rule.tolerance` wins; + * otherwise the default for the resolved match shape. Returns `undefined` + * for exact/structural matches, where tolerance doesn't apply. + */ +export function resolveTolerance( + rule: Pick, +): number | undefined { + if (rule.tolerance !== undefined) return rule.tolerance; + const shape = resolveMatchShape(rule); + return DEFAULT_TOLERANCE[shape]; +} diff --git a/packages/ghost-core/src/types.ts b/packages/ghost-core/src/types.ts index 06abfa9..efb53ef 100644 --- a/packages/ghost-core/src/types.ts +++ b/packages/ghost-core/src/types.ts @@ -180,6 +180,94 @@ export interface ColorRamp { count: number; } +// --- Rule types (v0 reviewer drift rules; perceptual-prior-aware) --- + +/** + * Perceptual severity for a drift violation. Calibrated to how loudly a + * change registers visually, not to engineering hygiene. See + * `perceptual-prior.ts` for the tier table that drives defaults. + * + * Distinct from `RuleSeverity` (`"error" | "warn" | "off"`) which is the + * config-level severity for `GhostConfig.rules`. The two never mix — + * `DriftSeverity` is for emitted reviewer rules; `RuleSeverity` gates lint + * configuration. + */ +export type DriftSeverity = "critical" | "serious" | "nit"; + +/** + * How a rule's pattern is matched against violators. Color is exact; + * spacing tolerates small absolute drift; type-size tolerates relative + * drift; radius/shadow care about structural shape (pill vs. non-pill), + * not exact px. + */ +export type RuleMatchShape = "exact" | "band" | "percent" | "structural"; + +/** + * The dimension-of-value a rule guards. Used to look up default match + * shape and tolerance. Distinct from canonical dimension because one + * canonical dimension (e.g. `typography-voice`) can host multiple rule + * kinds (family, weight, size). + */ +export type RuleKind = + | "color" + | "radius" + | "spacing" + | "type-size" + | "type-family" + | "type-weight" + | "shadow" + | "motion"; + +export interface Rule { + /** Stable id, slug-style. Used as anchor in emitted reviewer + diff. */ + id: string; + /** + * Canonical dimension this rule belongs to. Drives perceptual-tier + * lookup. Optional — non-canonical rules are emitted but don't roll up + * at fleet aggregation. + */ + canonical?: string; + /** What kind of value the rule guards. Drives default match shape. */ + kind?: RuleKind; + /** One-line summary the reviewer surfaces alongside violations. */ + summary?: string; + /** Regex (or fixed string) the reviewer greps for. */ + pattern: string; + /** + * Where the rule is enforced. Drives which file types / contexts the + * reviewer scans. Open vocabulary; common values: `className`, + * `css_var`, `inline_style`, `import`. Empty array = enforce everywhere. + */ + enforce_at?: string[]; + /** + * Optional explicit severity override. When absent, the emitter computes + * severity from `canonical` (perceptual tier) plus `presence_floor` + * (escalation against the bucket). + */ + severity?: DriftSeverity; + /** Optional explicit match-shape override. */ + match?: RuleMatchShape; + /** Tolerance for `band` (px) or `percent` (0–1). Override of default. */ + tolerance?: number; + /** + * Bucket-count threshold below which severity escalates one tier. The + * default is `0` — only when the underlying dimension is wholly absent + * does adding to it cross a presence boundary. Set to `2` (or higher) + * for cases like motion where a couple of structural transitions don't + * count as "this system uses motion." + */ + presence_floor?: number; + /** + * Surveyor-computed support score: fraction of observed cases that + * already conform to this rule. Used by the human curator to triage — + * <0.85 typically indicates the rule isn't yet load-bearing in the + * codebase. Consumed at lint time as a soft warning. + */ + support?: number; + /** Free-form rationale shown above the rule's table in the emitted reviewer. */ + rationale?: string; +} + // --- Observation & decision types (three-layer expression) --- export interface DesignObservation { @@ -187,8 +275,6 @@ export interface DesignObservation { summary: string; /** Personality traits (e.g. "utilitarian", "restrained", "playful") */ personality: string[]; - /** What makes this expression visually distinctive */ - distinctiveTraits: string[]; /** Closest well-known design languages for reference */ resembles: string[]; } @@ -196,6 +282,18 @@ export interface DesignObservation { export interface DesignDecision { /** Freeform dimension name — LLM chooses what's relevant (e.g. "color-strategy", "motion", "density") */ dimension: string; + /** + * Optional canonical bucket this decision rolls up under. When present, + * fleet-aggregation primitives group by this value. When absent, they + * fall back to `dimension` if it happens to be canonical, otherwise the + * decision is treated as long-tail. + * + * Authoring rule (see `closestCanonical` in `@ghost/core`): when + * `dimension` itself is one of `CANONICAL_DECISION_DIMENSIONS`, omit + * `dimension_kind`. Set it only when you've chosen a project-flavored + * slug that's better described by an existing canonical bucket. + */ + dimension_kind?: string; /** The decision stated abstractly, implementation-agnostic */ decision: string; /** Evidence from the source code supporting this decision */ @@ -208,46 +306,6 @@ export interface DesignDecision { embedding?: number[]; } -/** - * A semantic slot → token binding. Describes which concrete tokens a - * design language uses for a specific role (h1, body, card, button, …). - * - * This is the bridge between abstract tokens (`typography.sizeRamp: [14, 16, …]`) - * and renderable output: a role tells a renderer *which* ramp step belongs to - * *which* slot. All subfields are optional — the agent populates only what it - * can infer from the source. - */ -export interface DesignRole { - /** Semantic slot name — "h1", "body", "card", "button", "input", "list-row", etc. */ - name: string; - /** Tokens the slot binds, grouped by expression dimension. */ - tokens: { - typography?: { - family?: string; - size?: number; - weight?: number; - lineHeight?: number; - }; - spacing?: { - padding?: number; - gap?: number; - margin?: number; - }; - surfaces?: { - borderRadius?: number; - shadow?: "none" | "subtle" | "layered"; - borderWidth?: number; - }; - palette?: { - background?: string; - foreground?: string; - border?: string; - }; - }; - /** Evidence from the source — file paths or file:line references. */ - evidence: string[]; -} - export interface Expression { id: string; source: "registry" | "extraction" | "llm" | "unknown"; @@ -261,14 +319,13 @@ export interface Expression { observation?: DesignObservation; /** Layer 2: Abstract design decisions, implementation-agnostic */ decisions?: DesignDecision[]; - /** - * Semantic slot → token bindings. The bridge from abstract tokens to - * renderable output: each role names a slot ("h1", "card", "button") and - * binds tokens from the dimensions below. Optional — agents populate only - * roles they can infer from the source. + * v0 reviewer rules — human-curated, grep-friendly, severity computed + * by the perceptual prior at emit time. Coexists with `decisions[]` + * during the v0 transition; in v1 the parser stops populating + * `decisions[]` and `rules[]` is the only authoring surface. */ - roles?: DesignRole[]; + rules?: Rule[]; // --- Layer 3: Concrete values --- diff --git a/packages/ghost-core/test/bucket-fix-ids.test.ts b/packages/ghost-core/test/bucket-fix-ids.test.ts new file mode 100644 index 0000000..589bd9b --- /dev/null +++ b/packages/ghost-core/test/bucket-fix-ids.test.ts @@ -0,0 +1,78 @@ +import { describe, expect, it } from "vitest"; +import { recomputeBucketIds } from "../src/bucket/fix-ids.js"; +import { tokenRowId, valueRowId } from "../src/bucket/id.js"; +import type { Bucket, BucketSource } from "../src/bucket/types.js"; + +const SOURCE: BucketSource = { + target: "github:block/ghost", + commit: "abc123", + scanned_at: "2026-04-29T12:00:00Z", +}; + +function bucket(): Bucket { + return { + schema: "ghost.bucket/v1", + sources: [SOURCE], + values: [ + { + id: "", + source: SOURCE, + kind: "color", + value: "#f97316", + raw: "#f97316", + occurrences: 1, + files_count: 1, + }, + { + id: "wrong-id", + source: SOURCE, + kind: "spacing", + value: "8", + raw: "8px", + occurrences: 1, + files_count: 1, + }, + ], + tokens: [ + { + id: "", + source: SOURCE, + name: "--brand-primary", + alias_chain: [], + resolved_value: "#f97316", + occurrences: 1, + }, + ], + components: [], + }; +} + +describe("recomputeBucketIds", () => { + it("populates empty IDs with deterministic hashes", () => { + const fixed = recomputeBucketIds(bucket()); + expect(fixed.values[0].id).toBe( + valueRowId(SOURCE, "color", "#f97316", "#f97316"), + ); + expect(fixed.tokens[0].id).toBe(tokenRowId(SOURCE, "--brand-primary")); + }); + + it("overwrites incorrect IDs with the correct deterministic hash", () => { + const fixed = recomputeBucketIds(bucket()); + expect(fixed.values[1].id).toBe(valueRowId(SOURCE, "spacing", "8", "8px")); + expect(fixed.values[1].id).not.toBe("wrong-id"); + }); + + it("does not mutate the input bucket", () => { + const input = bucket(); + recomputeBucketIds(input); + expect(input.values[0].id).toBe(""); + expect(input.values[1].id).toBe("wrong-id"); + }); + + it("is idempotent — running twice yields the same result", () => { + const once = recomputeBucketIds(bucket()); + const twice = recomputeBucketIds(once); + expect(twice.values).toEqual(once.values); + expect(twice.tokens).toEqual(once.tokens); + }); +}); diff --git a/packages/ghost-core/test/bucket-id.test.ts b/packages/ghost-core/test/bucket-id.test.ts new file mode 100644 index 0000000..2a5ff9a --- /dev/null +++ b/packages/ghost-core/test/bucket-id.test.ts @@ -0,0 +1,114 @@ +import { describe, expect, it } from "vitest"; +import { componentRowId, tokenRowId, valueRowId } from "../src/bucket/id.js"; +import type { BucketSource } from "../src/bucket/types.js"; + +const SOURCE_A: BucketSource = { + target: "github:block/ghost", + commit: "abc123", + scanned_at: "2026-04-29T12:00:00Z", +}; + +const SOURCE_A_OTHER_TIME: BucketSource = { + ...SOURCE_A, + scanned_at: "2099-12-31T00:00:00Z", // different time, same target+commit +}; + +const SOURCE_B_DIFFERENT_COMMIT: BucketSource = { + ...SOURCE_A, + commit: "def456", +}; + +const SOURCE_C_DIFFERENT_TARGET: BucketSource = { + target: "github:block/other", + commit: "abc123", + scanned_at: "2026-04-29T12:00:00Z", +}; + +describe("valueRowId", () => { + it("produces stable hex IDs", () => { + const id = valueRowId(SOURCE_A, "color", "#f97316", "bg-orange-500"); + expect(id).toMatch(/^[0-9a-f]{16}$/); + }); + + it("is deterministic — same inputs give same ID", () => { + const id1 = valueRowId(SOURCE_A, "color", "#f97316", "bg-orange-500"); + const id2 = valueRowId(SOURCE_A, "color", "#f97316", "bg-orange-500"); + expect(id1).toBe(id2); + }); + + it("ignores scanned_at and scanner_version — same target+commit+content gives same ID", () => { + const id1 = valueRowId(SOURCE_A, "color", "#f97316", "bg-orange-500"); + const id2 = valueRowId( + SOURCE_A_OTHER_TIME, + "color", + "#f97316", + "bg-orange-500", + ); + expect(id1).toBe(id2); + }); + + it("differs across commits", () => { + const id1 = valueRowId(SOURCE_A, "color", "#f97316", "bg-orange-500"); + const id2 = valueRowId( + SOURCE_B_DIFFERENT_COMMIT, + "color", + "#f97316", + "bg-orange-500", + ); + expect(id1).not.toBe(id2); + }); + + it("differs across targets", () => { + const id1 = valueRowId(SOURCE_A, "color", "#f97316", "bg-orange-500"); + const id2 = valueRowId( + SOURCE_C_DIFFERENT_TARGET, + "color", + "#f97316", + "bg-orange-500", + ); + expect(id1).not.toBe(id2); + }); + + it("differs across kinds", () => { + const colorId = valueRowId(SOURCE_A, "color", "8", "8px"); + const spacingId = valueRowId(SOURCE_A, "spacing", "8", "8px"); + expect(colorId).not.toBe(spacingId); + }); + + it("differs when raw form differs but value matches", () => { + const id1 = valueRowId(SOURCE_A, "color", "#f97316", "bg-orange-500"); + const id2 = valueRowId(SOURCE_A, "color", "#f97316", "var(--brand)"); + expect(id1).not.toBe(id2); + }); +}); + +describe("section-tagged IDs are non-colliding", () => { + it("token vs value with same name does not collide", () => { + const tokenId = tokenRowId(SOURCE_A, "Button"); + const valueId = valueRowId(SOURCE_A, "color", "Button", "Button"); + expect(tokenId).not.toBe(valueId); + }); + + it("token vs component with same name does not collide", () => { + const tokenId = tokenRowId(SOURCE_A, "Button"); + const componentId = componentRowId(SOURCE_A, "Button"); + expect(tokenId).not.toBe(componentId); + }); +}); + +describe("token / component IDs", () => { + it("are deterministic", () => { + expect(tokenRowId(SOURCE_A, "--color-brand-primary")).toBe( + tokenRowId(SOURCE_A, "--color-brand-primary"), + ); + expect(componentRowId(SOURCE_A, "Button")).toBe( + componentRowId(SOURCE_A, "Button"), + ); + }); + + it("differ across names within a section", () => { + expect(tokenRowId(SOURCE_A, "--brand")).not.toBe( + tokenRowId(SOURCE_A, "--accent"), + ); + }); +}); diff --git a/packages/ghost-core/test/bucket-lint.test.ts b/packages/ghost-core/test/bucket-lint.test.ts new file mode 100644 index 0000000..4e9a9a2 --- /dev/null +++ b/packages/ghost-core/test/bucket-lint.test.ts @@ -0,0 +1,123 @@ +import { describe, expect, it } from "vitest"; +import { valueRowId } from "../src/bucket/id.js"; +import { lintBucket } from "../src/bucket/lint.js"; +import type { Bucket, BucketSource } from "../src/bucket/types.js"; + +const SOURCE: BucketSource = { + target: "github:block/ghost", + commit: "abc123", + scanned_at: "2026-04-29T12:00:00Z", + scanner_version: "0.1.0", +}; + +function makeValueRow( + kind: string, + value: string, + raw: string, + overrides: Partial<{ + occurrences: number; + files_count: number; + role_hypothesis: string; + }> = {}, +) { + return { + id: valueRowId(SOURCE, kind, value, raw), + source: SOURCE, + kind, + value, + raw, + occurrences: overrides.occurrences ?? 1, + files_count: overrides.files_count ?? 1, + role_hypothesis: overrides.role_hypothesis, + }; +} + +function makeBucket(values: ReturnType[] = []): Bucket { + return { + schema: "ghost.bucket/v1", + sources: [SOURCE], + values, + tokens: [], + components: [], + }; +} + +describe("lintBucket", () => { + it("accepts an empty well-formed bucket", () => { + const report = lintBucket(makeBucket()); + expect(report.errors).toBe(0); + expect(report.warnings).toBe(0); + }); + + it("accepts a bucket with recommended-kind value rows", () => { + const bucket = makeBucket([ + makeValueRow("color", "#f97316", "bg-orange-500", { + occurrences: 47, + files_count: 12, + }), + makeValueRow("spacing", "8", "8px", { + occurrences: 312, + files_count: 89, + }), + ]); + const report = lintBucket(bucket); + expect(report.errors).toBe(0); + expect(report.warnings).toBe(0); + }); + + it("rejects missing schema field", () => { + const bucket: unknown = { + ...makeBucket(), + schema: "ghost.bucket/v0", + }; + const report = lintBucket(bucket); + expect(report.errors).toBeGreaterThan(0); + expect(report.issues.some((i) => i.rule.startsWith("schema/"))).toBe(true); + }); + + it("rejects negative occurrences", () => { + const row = makeValueRow("color", "#f97316", "#f97316"); + const report = lintBucket(makeBucket([{ ...row, occurrences: -1 }])); + expect(report.errors).toBeGreaterThan(0); + }); + + it("warns on unknown value kinds without rejecting", () => { + const bucket = makeBucket([ + makeValueRow("z-index", "10", "z-10"), // not in recommended set + ]); + const report = lintBucket(bucket); + expect(report.errors).toBe(0); + expect(report.warnings).toBeGreaterThan(0); + expect(report.issues.some((i) => i.rule === "value-kind-unknown")).toBe( + true, + ); + }); + + it("warns when a row's id does not match the deterministic generator", () => { + const bucket = makeBucket([ + { + ...makeValueRow("color", "#f97316", "#f97316"), + id: "deadbeefdeadbeef", // hand-rolled, not from generator + }, + ]); + const report = lintBucket(bucket); + expect(report.warnings).toBeGreaterThan(0); + expect(report.issues.some((i) => i.rule === "id-mismatch")).toBe(true); + }); + + it("flags duplicate IDs within a section as errors", () => { + const row = makeValueRow("color", "#f97316", "#f97316"); + const report = lintBucket(makeBucket([row, { ...row }])); // same ID, two rows + expect(report.errors).toBeGreaterThan(0); + expect(report.issues.some((i) => i.rule === "duplicate-id")).toBe(true); + }); + + it("rejects sources array with no entries", () => { + const bucket: unknown = { + ...makeBucket(), + sources: [], + }; + const report = lintBucket(bucket); + expect(report.errors).toBeGreaterThan(0); + }); +}); diff --git a/packages/ghost-core/test/bucket-merge.test.ts b/packages/ghost-core/test/bucket-merge.test.ts new file mode 100644 index 0000000..3dfd967 --- /dev/null +++ b/packages/ghost-core/test/bucket-merge.test.ts @@ -0,0 +1,141 @@ +import { describe, expect, it } from "vitest"; +import { tokenRowId, valueRowId } from "../src/bucket/id.js"; +import { mergeBuckets } from "../src/bucket/merge.js"; +import type { + Bucket, + BucketSource, + TokenRow, + ValueRow, +} from "../src/bucket/types.js"; + +const SOURCE_A: BucketSource = { + target: "github:block/ghost", + commit: "abc123", + scanned_at: "2026-04-29T12:00:00Z", +}; + +const SOURCE_B: BucketSource = { + target: "github:block/other", + commit: "def456", + scanned_at: "2026-04-29T12:00:00Z", +}; + +function valueRow( + source: BucketSource, + kind: string, + value: string, + raw: string, + occurrences = 1, +): ValueRow { + return { + id: valueRowId(source, kind, value, raw), + source, + kind, + value, + raw, + occurrences, + files_count: 1, + }; +} + +function tokenRow( + source: BucketSource, + name: string, + resolved: string, +): TokenRow { + return { + id: tokenRowId(source, name), + source, + name, + alias_chain: [], + resolved_value: resolved, + occurrences: 1, + }; +} + +function makeBucket( + source: BucketSource, + values: ValueRow[] = [], + tokens: TokenRow[] = [], +): Bucket { + return { + schema: "ghost.bucket/v1", + sources: [source], + values, + tokens, + components: [], + }; +} + +describe("mergeBuckets", () => { + it("merging a single bucket returns equivalent rowset", () => { + const a = makeBucket(SOURCE_A, [ + valueRow(SOURCE_A, "color", "#f97316", "#f97316"), + ]); + const merged = mergeBuckets(a); + expect(merged.values).toEqual(a.values); + expect(merged.sources).toEqual([SOURCE_A]); + }); + + it("is idempotent — merging the same bucket twice yields the same rowset", () => { + const a = makeBucket(SOURCE_A, [ + valueRow(SOURCE_A, "color", "#f97316", "#f97316"), + valueRow(SOURCE_A, "spacing", "8", "8px"), + ]); + const once = mergeBuckets(a); + const twice = mergeBuckets(a, a); + expect(twice.values).toEqual(once.values); + expect(twice.sources).toEqual(once.sources); + }); + + it("preserves rows with distinct IDs across different sources", () => { + const a = makeBucket(SOURCE_A, [ + valueRow(SOURCE_A, "color", "#f97316", "#f97316"), + ]); + const b = makeBucket(SOURCE_B, [ + valueRow(SOURCE_B, "color", "#f97316", "#f97316"), + ]); + const merged = mergeBuckets(a, b); + expect(merged.values).toHaveLength(2); + expect(merged.sources).toEqual([SOURCE_A, SOURCE_B]); + }); + + it("dedupes rows with identical IDs (same source + same content)", () => { + const row = valueRow(SOURCE_A, "color", "#f97316", "#f97316"); + const a = makeBucket(SOURCE_A, [row]); + const b = makeBucket(SOURCE_A, [row]); // same source, same content -> same ID + const merged = mergeBuckets(a, b); + expect(merged.values).toHaveLength(1); + expect(merged.sources).toHaveLength(1); + }); + + it("preserves tokens and components independently", () => { + const a = makeBucket( + SOURCE_A, + [], + [tokenRow(SOURCE_A, "--brand-primary", "#f97316")], + ); + const b = makeBucket( + SOURCE_B, + [], + [tokenRow(SOURCE_B, "--brand-primary", "#0000ff")], + ); + const merged = mergeBuckets(a, b); + expect(merged.tokens).toHaveLength(2); + // Same token name, different sources, distinct IDs — both survive. + expect(merged.tokens.map((t) => t.resolved_value).sort()).toEqual([ + "#0000ff", + "#f97316", + ]); + }); + + it("throws when given zero buckets", () => { + expect(() => mergeBuckets()).toThrow(/at least one/); + }); + + it("schema field on the merged bucket is ghost.bucket/v1", () => { + const a = makeBucket(SOURCE_A); + const merged = mergeBuckets(a); + expect(merged.schema).toBe("ghost.bucket/v1"); + }); +}); diff --git a/packages/ghost-core/test/decision-vocabulary.test.ts b/packages/ghost-core/test/decision-vocabulary.test.ts new file mode 100644 index 0000000..f883d4e --- /dev/null +++ b/packages/ghost-core/test/decision-vocabulary.test.ts @@ -0,0 +1,114 @@ +import { describe, expect, it } from "vitest"; +import { + CANONICAL_DECISION_DIMENSIONS, + closestCanonical, + isCanonicalDimension, + resolveDecisionKind, +} from "../src/decision-vocabulary.js"; + +describe("CANONICAL_DECISION_DIMENSIONS", () => { + it("contains the documented 12 dimensions", () => { + expect(CANONICAL_DECISION_DIMENSIONS).toHaveLength(12); + expect(CANONICAL_DECISION_DIMENSIONS).toContain("color-strategy"); + expect(CANONICAL_DECISION_DIMENSIONS).toContain("font-sourcing"); + }); + + it("has no duplicates", () => { + const set = new Set(CANONICAL_DECISION_DIMENSIONS); + expect(set.size).toBe(CANONICAL_DECISION_DIMENSIONS.length); + }); +}); + +describe("isCanonicalDimension", () => { + it("accepts every canonical slug", () => { + for (const slug of CANONICAL_DECISION_DIMENSIONS) { + expect(isCanonicalDimension(slug)).toBe(true); + } + }); + + it("rejects unknown slugs", () => { + expect(isCanonicalDimension("warm-neutrals")).toBe(false); + expect(isCanonicalDimension("color-system")).toBe(false); + }); + + it("normalizes whitespace and underscores before checking", () => { + expect(isCanonicalDimension("color_strategy")).toBe(true); + expect(isCanonicalDimension(" Color-Strategy ")).toBe(true); + }); +}); + +describe("closestCanonical", () => { + it("returns the slug itself when already canonical", () => { + expect(closestCanonical("color-strategy")).toBe("color-strategy"); + expect(closestCanonical("motion")).toBe("motion"); + }); + + it("resolves direct synonyms", () => { + expect(closestCanonical("color-system")).toBe("color-strategy"); + expect(closestCanonical("palette-strategy")).toBe("color-strategy"); + expect(closestCanonical("type-stack")).toBe("typography-voice"); + expect(closestCanonical("radius-philosophy")).toBe("shape-language"); + expect(closestCanonical("corner-treatment")).toBe("shape-language"); + expect(closestCanonical("shadow-system")).toBe("elevation"); + expect(closestCanonical("theme-system")).toBe("theming-architecture"); + }); + + it("falls back to token affinity for novel slugs", () => { + expect(closestCanonical("color-cadence")).toBe("color-strategy"); + expect(closestCanonical("custom-shadow-language")).toBe("elevation"); + expect(closestCanonical("fancy-motion-rules")).toBe("motion"); + }); + + it("returns null when no canonical wins clearly", () => { + expect(closestCanonical("entirely-novel-decision")).toBeNull(); + expect(closestCanonical("")).toBeNull(); + }); + + it("returns null on a tie between dimensions", () => { + // "color" → color-strategy, "shadow" → elevation: tied at 1 each + expect(closestCanonical("color-shadow")).toBeNull(); + }); + + it("normalizes input before matching", () => { + expect(closestCanonical("Color_Strategy")).toBe("color-strategy"); + expect(closestCanonical(" shadow_system ")).toBe("elevation"); + }); +}); + +describe("resolveDecisionKind", () => { + it("prefers explicit dimension_kind when canonical", () => { + expect( + resolveDecisionKind({ + dimension: "system-color-deference", + dimension_kind: "color-strategy", + }), + ).toBe("color-strategy"); + }); + + it("falls back to dimension when canonical and kind absent", () => { + expect(resolveDecisionKind({ dimension: "shape-language" })).toBe( + "shape-language", + ); + }); + + it("returns null when neither is canonical", () => { + expect(resolveDecisionKind({ dimension: "warm-neutrals" })).toBeNull(); + expect( + resolveDecisionKind({ + dimension: "warm-neutrals", + dimension_kind: "also-not-canonical", + }), + ).toBeNull(); + }); + + it("ignores a non-canonical kind even when dimension is canonical", () => { + // dimension_kind is opt-in metadata; if author typoed it, fall through + // to the dimension itself rather than silently failing rollup. + expect( + resolveDecisionKind({ + dimension: "color-strategy", + dimension_kind: "typo-here", + }), + ).toBe("color-strategy"); + }); +}); diff --git a/packages/ghost-core/test/perceptual-prior.test.ts b/packages/ghost-core/test/perceptual-prior.test.ts new file mode 100644 index 0000000..0414b69 --- /dev/null +++ b/packages/ghost-core/test/perceptual-prior.test.ts @@ -0,0 +1,227 @@ +import { describe, expect, it } from "vitest"; +import { CANONICAL_DECISION_DIMENSIONS } from "../src/decision-vocabulary.js"; +import { + computeRuleSeverity, + DEFAULT_MATCH, + DEFAULT_TOLERANCE, + escalateForPresence, + escalateTier, + PERCEPTUAL_TIER, + type PerceptualTier, + resolveMatchShape, + resolveTolerance, + TIER_SEVERITY, + tierForCanonical, +} from "../src/perceptual-prior.js"; + +describe("PERCEPTUAL_TIER", () => { + it("covers every canonical dimension", () => { + for (const dim of CANONICAL_DECISION_DIMENSIONS) { + expect(PERCEPTUAL_TIER[dim]).toBeDefined(); + } + }); + + it("places color-strategy and font-sourcing in loud", () => { + expect(PERCEPTUAL_TIER["color-strategy"]).toBe("loud"); + expect(PERCEPTUAL_TIER["font-sourcing"]).toBe("loud"); + }); + + it("places shape-language and elevation in structural", () => { + expect(PERCEPTUAL_TIER["shape-language"]).toBe("structural"); + expect(PERCEPTUAL_TIER["elevation"]).toBe("structural"); + }); + + it("places spatial-system, density, motion in rhythmic", () => { + expect(PERCEPTUAL_TIER["spatial-system"]).toBe("rhythmic"); + expect(PERCEPTUAL_TIER["density"]).toBe("rhythmic"); + expect(PERCEPTUAL_TIER["motion"]).toBe("rhythmic"); + }); +}); + +describe("TIER_SEVERITY", () => { + it("maps tiers to drift severities in perceptual order", () => { + expect(TIER_SEVERITY.loud).toBe("critical"); + expect(TIER_SEVERITY.structural).toBe("serious"); + expect(TIER_SEVERITY.rhythmic).toBe("nit"); + }); +}); + +describe("escalateTier", () => { + it("rhythmic → structural", () => { + expect(escalateTier("rhythmic")).toBe("structural"); + }); + + it("structural → loud", () => { + expect(escalateTier("structural")).toBe("loud"); + }); + + it("loud saturates at loud", () => { + expect(escalateTier("loud")).toBe("loud"); + }); +}); + +describe("tierForCanonical", () => { + it("returns the canonical tier for a known slug", () => { + expect(tierForCanonical("motion")).toBe("rhythmic"); + expect(tierForCanonical("color-strategy")).toBe("loud"); + }); + + it("returns structural for unknown / undefined", () => { + expect(tierForCanonical(undefined)).toBe("structural"); + expect(tierForCanonical("not-a-real-dimension")).toBe("structural"); + }); +}); + +describe("escalateForPresence", () => { + it("escalates when bucket count is below floor", () => { + expect(escalateForPresence("rhythmic", 0, 0)).toBe("structural"); + expect(escalateForPresence("rhythmic", 1, 2)).toBe("structural"); + }); + + it("does not escalate when bucket count is above floor", () => { + expect(escalateForPresence("rhythmic", 5, 2)).toBe("rhythmic"); + expect(escalateForPresence("structural", 10, 0)).toBe("structural"); + }); + + it("treats count == floor as triggering escalation", () => { + // floor is the boundary at which escalation kicks in (≤ floor → escalate) + expect(escalateForPresence("rhythmic", 2, 2)).toBe("structural"); + }); + + it("defaults presence floor to 0", () => { + expect(escalateForPresence("rhythmic", 0)).toBe("structural"); + expect(escalateForPresence("rhythmic", 1)).toBe("rhythmic"); + }); + + it("loud saturates even with escalation triggered", () => { + expect(escalateForPresence("loud", 0, 0)).toBe("loud"); + }); +}); + +describe("computeRuleSeverity", () => { + it("honors explicit severity override", () => { + expect( + computeRuleSeverity( + { canonical: "spatial-system", severity: "critical" }, + 100, + ), + ).toBe("critical"); + }); + + it("derives from canonical tier when no override", () => { + expect(computeRuleSeverity({ canonical: "color-strategy" }, 50)).toBe( + "critical", + ); + expect(computeRuleSeverity({ canonical: "shape-language" }, 50)).toBe( + "serious", + ); + expect(computeRuleSeverity({ canonical: "spatial-system" }, 50)).toBe( + "nit", + ); + }); + + it("escalates a rhythmic rule when bucket count crosses floor", () => { + // motion at 2 occurrences with floor of 2 → escalates rhythmic → structural → serious + expect( + computeRuleSeverity({ canonical: "motion", presence_floor: 2 }, 2), + ).toBe("serious"); + }); + + it("does not escalate when bucket count exceeds floor", () => { + expect( + computeRuleSeverity({ canonical: "motion", presence_floor: 2 }, 12), + ).toBe("nit"); + }); + + it("escalates structural to loud (critical) at zero presence", () => { + expect( + computeRuleSeverity({ canonical: "elevation", presence_floor: 0 }, 0), + ).toBe("critical"); + }); + + it("treats unknown canonical as structural with conservative escalation", () => { + expect(computeRuleSeverity({ canonical: "novel-dimension" }, 5)).toBe( + "serious", + ); + expect(computeRuleSeverity({ canonical: "novel-dimension" }, 0)).toBe( + "critical", + ); + }); +}); + +describe("DEFAULT_MATCH", () => { + it("color is exact", () => { + expect(DEFAULT_MATCH.color).toBe("exact"); + }); + + it("spacing is band", () => { + expect(DEFAULT_MATCH.spacing).toBe("band"); + }); + + it("type-size is percent; type-family and type-weight are exact", () => { + expect(DEFAULT_MATCH["type-size"]).toBe("percent"); + expect(DEFAULT_MATCH["type-family"]).toBe("exact"); + expect(DEFAULT_MATCH["type-weight"]).toBe("exact"); + }); + + it("radius and shadow are structural", () => { + expect(DEFAULT_MATCH.radius).toBe("structural"); + expect(DEFAULT_MATCH.shadow).toBe("structural"); + }); +}); + +describe("DEFAULT_TOLERANCE", () => { + it("exact and structural have no tolerance", () => { + expect(DEFAULT_TOLERANCE.exact).toBeUndefined(); + expect(DEFAULT_TOLERANCE.structural).toBeUndefined(); + }); + + it("band defaults to ±2", () => { + expect(DEFAULT_TOLERANCE.band).toBe(2); + }); + + it("percent defaults to ±10%", () => { + expect(DEFAULT_TOLERANCE.percent).toBeCloseTo(0.1); + }); +}); + +describe("resolveMatchShape", () => { + it("explicit match wins", () => { + expect(resolveMatchShape({ match: "percent", kind: "color" })).toBe( + "percent", + ); + }); + + it("falls back to kind default", () => { + expect(resolveMatchShape({ kind: "spacing" })).toBe("band"); + }); + + it("returns exact when no signal", () => { + expect(resolveMatchShape({})).toBe("exact"); + }); +}); + +describe("resolveTolerance", () => { + it("explicit tolerance wins", () => { + expect(resolveTolerance({ tolerance: 4, kind: "spacing" })).toBe(4); + }); + + it("derives from match shape default", () => { + expect(resolveTolerance({ kind: "spacing" })).toBe(2); + expect(resolveTolerance({ kind: "type-size" })).toBeCloseTo(0.1); + }); + + it("returns undefined for exact / structural", () => { + expect(resolveTolerance({ kind: "color" })).toBeUndefined(); + expect(resolveTolerance({ kind: "radius" })).toBeUndefined(); + }); +}); + +describe("perceptual-prior tier-coverage invariant", () => { + it("every canonical dimension lands in one of three tiers", () => { + const tiers = new Set(["loud", "structural", "rhythmic"]); + for (const dim of CANONICAL_DECISION_DIMENSIONS) { + expect(tiers.has(PERCEPTUAL_TIER[dim])).toBe(true); + } + }); +}); diff --git a/packages/ghost-drift/README.md b/packages/ghost-drift/README.md index 0b708ca..8c63e2f 100644 --- a/packages/ghost-drift/README.md +++ b/packages/ghost-drift/README.md @@ -47,19 +47,21 @@ ghost-drift emit skill # install the agent recip Zero config for every verb. No API key needed. `OPENAI_API_KEY` / `VOYAGE_API_KEY` are optional and only consumed if you ask for a semantic-enriched embedding via the library. -### Authoring `expression.md`? +### Authoring a scan? -Authoring lives in **[`ghost-expression`](../ghost-expression)**. Install it for `lint`, `describe`, `diff`, and `emit review-command` / `emit context-bundle`: +Scans live in **[`ghost-expression`](../ghost-expression)**, which owns the three-stage pipeline (`map.md` topology → `bucket.json` objective → `expression.md` subjective). Install it for `inventory`, `lint`, `describe`, `diff`, `bucket merge` / `fix-ids`, `scan-status`, and `emit review-command` / `emit context-bundle`: ```bash -ghost-expression lint # validate ./expression.md -ghost-expression describe # section ranges + token estimates -ghost-expression diff a.md b.md # structural prose-level diff +ghost-expression inventory # raw repo signals → JSON (feeds map.md) +ghost-expression scan-status # per-stage state + next stage +ghost-expression lint # auto-detects expression.md / map.md / bucket.json +ghost-expression bucket merge a.json b.json # union with id-based dedup +ghost-expression diff a.md b.md # structural prose-level diff between expressions ghost-expression emit review-command # per-project slash command ghost-expression emit context-bundle # generation context bundle ``` -These verbs used to live under `ghost-drift`. They were moved in v0.2.0 — running them on `ghost-drift` now prints a deprecation message pointing here. +The authoring verbs that used to live under `ghost-drift` were moved in v0.2.0; running them on `ghost-drift` now prints a deprecation message pointing here. ## As a library @@ -88,11 +90,11 @@ ghost-drift emit skill The agent runs the recipes; the CLI runs the arithmetic. The CLI never calls an LLM. -(Authoring recipes — `profile` for `expression.md` — ship in `ghost-expression`'s skill bundle. Topology and fleet recipes ship in `ghost-map` and `ghost-fleet` respectively.) +(Authoring recipes — `scan` / `map` / `survey` / `profile` — all ship in `ghost-expression`'s skill bundle, since one tool now owns the whole three-stage scan pipeline. Fleet narrative recipes ship in `ghost-fleet`.) ## Full story -See the [project README](https://github.com/block/ghost#readme) for the philosophy, the five-tool decomposition, the expression format spec, composite comparison, and the reference design language (Ghost UI). +See the [project README](https://github.com/block/ghost#readme) for the philosophy, the four-tool decomposition, the three-stage scan pipeline, the expression format spec, composite comparison, and the reference design language (Ghost UI). ## License diff --git a/packages/ghost-drift/src/core/reporters/expression.ts b/packages/ghost-drift/src/core/reporters/expression.ts index 2eae9cf..ff23e56 100644 --- a/packages/ghost-drift/src/core/reporters/expression.ts +++ b/packages/ghost-drift/src/core/reporters/expression.ts @@ -31,11 +31,6 @@ export function formatExpression(fp: Expression): string { if (fp.observation.personality.length > 0) { lines.push(` Personality: ${fp.observation.personality.join(", ")}`); } - if (fp.observation.distinctiveTraits.length > 0) { - for (const trait of fp.observation.distinctiveTraits) { - lines.push(` ${c(DIM, "-")} ${trait}`); - } - } if (fp.observation.resembles.length > 0) { lines.push(` Resembles: ${fp.observation.resembles.join(", ")}`); } diff --git a/packages/ghost-drift/src/skill-bundle/SKILL.md b/packages/ghost-drift/src/skill-bundle/SKILL.md index 0025e8f..d9a76e3 100644 --- a/packages/ghost-drift/src/skill-bundle/SKILL.md +++ b/packages/ghost-drift/src/skill-bundle/SKILL.md @@ -40,8 +40,8 @@ For authoring or describing an expression itself (write expression.md, lint, des An `expression.md` has: -- **YAML frontmatter (machine layer):** `id`, `source`, `timestamp`, `observation.personality`, `observation.resembles`, `decisions[].dimension`/`.evidence`, `palette`, `spacing`, `typography`, `surfaces`, `roles`. -- **Markdown body (prose layer):** `# Character` (`observation.summary`), `# Signature` (bullets from `distinctiveTraits`), `# Decisions` with `### ` rationale blocks. +- **YAML frontmatter (machine layer):** `id`, `source`, `timestamp`, `observation.personality`, `observation.resembles`, `decisions[].dimension`, `rules[]`, `palette`, `spacing`, `typography`, `surfaces`. +- **Markdown body (prose layer):** `# Character` (`observation.summary`), `# Decisions` with `### ` rationale blocks ending in `**Evidence:**` bullets. Validate via `ghost-expression lint` before drawing conclusions from a drift comparison. diff --git a/packages/ghost-drift/src/skill-bundle/references/review.md b/packages/ghost-drift/src/skill-bundle/references/review.md index 8393ed8..77802da 100644 --- a/packages/ghost-drift/src/skill-bundle/references/review.md +++ b/packages/ghost-drift/src/skill-bundle/references/review.md @@ -28,11 +28,11 @@ Ghost has no `ghost review` CLI command. You — the host agent — are the revi ghost-expression describe expression.md -This prints a section map — frontmatter range, body sections (`# Character`, `# Signature`, `# Decisions`, `# Fragments`), and each `### dimension` block under Decisions, with line ranges and token estimates. Use it to plan what to load. +This prints a section map — frontmatter range, body sections (`# Character`, `# Decisions`, `# Fragments`), and each `### dimension` block under Decisions, with line ranges and token estimates. Use it to plan what to load. Then read selectively: -- **Always read the frontmatter.** It carries the structural budget — `palette`, `spacing.scale`, `typography.families`/`sizeRamp`, `surfaces.borderRadii`, `roles[]` — that you'll match diff values against. +- **Always read the frontmatter.** It carries the structural budget — `palette`, `spacing.scale`, `typography.families`/`sizeRamp`, `surfaces.borderRadii` — that you'll match diff values against. - **Read decision sections by dimension name.** If the diff touches colors, you'll want `### color-strategy` (and any other `color-*` / `palette-*` dimension). If it touches radii, `### shape-language`, `### surface-hierarchy`, `### elevation`. Match on slug. - **If you're not confident which decisions are relevant — or the diff spans more than two partitions — read the entire `# Decisions` block.** It's typically 2–4k tokens; cheaper than missing a constraint. The describe output tells you the exact line range. diff --git a/packages/ghost-drift/src/skill-bundle/references/verify.md b/packages/ghost-drift/src/skill-bundle/references/verify.md index ff7654f..06b5da4 100644 --- a/packages/ghost-drift/src/skill-bundle/references/verify.md +++ b/packages/ghost-drift/src/skill-bundle/references/verify.md @@ -20,7 +20,7 @@ Ghost has no `ghost verify` CLI command. You drive the loop; the expression is t ### 1. Generate -Produce the UI code. Use whatever generator/recipe your harness provides; respect `palette`, `spacing.scale`, `typography`, `surfaces`, `decisions`, `roles` from the expression. The expression is the constraint set — feed it into the generator's system prompt, or load `tokens.css` (via `ghost-expression emit context-bundle`) as grounding. +Produce the UI code. Use whatever generator/recipe your harness provides; respect `palette`, `spacing.scale`, `typography`, `surfaces`, `decisions` from the expression. The expression is the constraint set — feed it into the generator's system prompt, or load `tokens.css` (via `ghost-expression emit context-bundle`) as grounding. ### 2. Self-review diff --git a/packages/ghost-drift/test/cli.test.ts b/packages/ghost-drift/test/cli.test.ts index 53327a1..875be38 100644 --- a/packages/ghost-drift/test/cli.test.ts +++ b/packages/ghost-drift/test/cli.test.ts @@ -31,10 +31,6 @@ surfaces: Quiet and direct. -# Signature - -- Small, plain surfaces - # Decisions ### shape-language diff --git a/packages/ghost-drift/test/embedding/compare-oklch-fallback.test.ts b/packages/ghost-drift/test/embedding/compare-oklch-fallback.test.ts new file mode 100644 index 0000000..fbb6543 --- /dev/null +++ b/packages/ghost-drift/test/embedding/compare-oklch-fallback.test.ts @@ -0,0 +1,99 @@ +import type { Expression } from "@ghost/core"; +import { compareExpressions } from "@ghost/core"; +import { describe, expect, it } from "vitest"; + +/** + * Regression coverage for the oklch fallback in `comparePalette`. Authored + * expressions can carry palette colors as bare hex (`{ role, value: "#hex" }`) + * with no `oklch` tuple. Without the fallback, such colors landed in the + * "unmatched" branch and contributed distance 1 — even when comparing the + * same expression to itself. + * + * Two layers of defense: + * - `loadExpression` backfills oklch on read (in ghost-expression). + * - `comparePalette` computes oklch on-the-fly when missing AND falls + * back to hex equality when even on-the-fly compute can't resolve. + */ + +function makeExpression( + paletteOverrides: Partial = {}, +): Expression { + return { + id: "test", + source: "llm", + timestamp: "2026-04-29T00:00:00Z", + palette: { + dominant: [{ role: "primary", value: "#1a1a1a" }], + neutrals: { steps: ["#ffffff", "#1a1a1a"], count: 2 }, + semantic: [ + { role: "danger", value: "#f94b4b" }, + { role: "success", value: "#91cb80" }, + ], + saturationProfile: "muted", + contrast: "high", + ...paletteOverrides, + }, + spacing: { scale: [4, 8, 16], regularity: 1, baseUnit: 4 }, + typography: { + families: ["Inter"], + sizeRamp: [12, 16, 24], + weightDistribution: { 400: 1 }, + lineHeightPattern: "normal", + }, + surfaces: { + borderRadii: [4, 8], + shadowComplexity: "deliberate-none", + borderUsage: "minimal", + }, + embedding: [], + }; +} + +describe("comparePalette — oklch missing fallback", () => { + it("self-comparison of hex-only palette returns distance 0", () => { + const expr = makeExpression(); + const result = compareExpressions(expr, expr); + expect(result.dimensions.palette.distance).toBe(0); + }); + + it("identical hex-only palettes (different objects) return distance 0", () => { + const a = makeExpression(); + const b = makeExpression(); + const result = compareExpressions(a, b); + expect(result.dimensions.palette.distance).toBe(0); + }); + + it("different hex-only palettes return non-zero distance", () => { + const a = makeExpression(); + const b = makeExpression({ + dominant: [{ role: "primary", value: "#0066cc" }], + }); + const result = compareExpressions(a, b); + expect(result.dimensions.palette.distance).toBeGreaterThan(0); + }); + + it("hex-only on one side, oklch-populated on the other still matches when value is identical", () => { + const a = makeExpression(); + const b = makeExpression({ + dominant: [ + { role: "primary", value: "#1a1a1a", oklch: [0.218, 0, 89.9] }, + ], + }); + const result = compareExpressions(a, b); + // The on-the-fly parse should resolve a's hex to roughly the same + // oklch — distance should be near 0, not 1. + expect(result.dimensions.palette.distance).toBeLessThan(0.01); + }); + + it("hex-only colors that are non-parseable but identical strings still match", () => { + const a = makeExpression({ + dominant: [{ role: "primary", value: "var(--upstream-brand)" }], + }); + const b = makeExpression({ + dominant: [{ role: "primary", value: "var(--upstream-brand)" }], + }); + const result = compareExpressions(a, b); + // CSS vars don't parse to oklch — fall through to hex equality. + expect(result.dimensions.palette.distance).toBe(0); + }); +}); diff --git a/packages/ghost-expression/README.md b/packages/ghost-expression/README.md index fd3d3c4..5e70864 100644 --- a/packages/ghost-expression/README.md +++ b/packages/ghost-expression/README.md @@ -1,10 +1,18 @@ # ghost-expression -**Author and validate `expression.md` — Ghost's canonical design-language artifact. Four verbs. No LLM calls.** +**Author the three-stage scan of a project's design language: `map.md` → `bucket.json` → `expression.md`. No LLM calls in any verb.** -`ghost-expression` owns the on-disk format every other Ghost tool reads. It parses, lints, lays out (section ranges + token estimates for selective loading), structurally diffs, and emits derived artifacts (per-project review slash commands, generation context bundles, agentskills.io skill bundles). +`ghost-expression` owns the on-disk artifacts every other Ghost tool reads. A scan runs in three stages, each a separate skill recipe with a deterministic CLI verb as its success gate: -The actual *writing* of an `expression.md` is a host-agent recipe — `profile.md` ships in this package's skill bundle and walks an agent through resolving design sources end-to-end. The CLI here is the success gate at the end of that loop: same answer every time, no LLM in the loop. +| Stage | Artifact | Schema | Authored via | Validated by | +|---|---|---|---|---| +| **Topology** | `map.md` | `ghost.map/v1` | `map.md` skill recipe + `ghost-expression inventory` | `ghost-expression lint map.md` | +| **Objective** | `bucket.json` | `ghost.bucket/v1` | `survey.md` skill recipe + `ghost-expression bucket fix-ids` | `ghost-expression lint bucket.json` | +| **Subjective** | `expression.md` | (unversioned) | `profile.md` skill recipe (reads bucket as ground truth) | `ghost-expression lint expression.md` | + +The CLI parses, lints (auto-detects file kind), inventories raw repo signals, runs deterministic data ops on buckets (`merge`, `fix-ids`), structurally diffs expressions, reports per-stage scan progress, and emits derived artifacts (per-project review slash commands, generation context bundles, agentskills.io skill bundles). + +The actual *writing* of each artifact is a host-agent recipe — the four ship in this package's skill bundle and walk an agent through topology / survey / interpretation / end-to-end orchestration. The CLI here is the success gate. For drift detection, comparison, and stance recording (`compare`, `ack`, `track`, `diverge`), see **[`ghost-drift`](../ghost-drift)**. @@ -23,16 +31,30 @@ pnpm add https://github.com/block/ghost/releases/download/ghost-expression%400.0 ## Use ```bash -ghost-expression lint # validate ./expression.md -ghost-expression lint path/to/expression.md # validate a specific file -ghost-expression lint expression.md --format json # machine-readable output - -ghost-expression describe # section ranges + token estimates for ./expression.md -ghost-expression describe expression.md --format json - -ghost-expression diff a/expression.md b/expression.md # structural prose-level diff - # (NOT vector distance — for that, use `ghost-drift compare`) - +# Topology — emit raw repo signals +ghost-expression inventory # signals for cwd +ghost-expression inventory ../other-repo # signals for another path + +# Validation — auto-detects expression.md / map.md / bucket.json +ghost-expression lint # ./expression.md +ghost-expression lint map.md # validates as ghost.map/v1 +ghost-expression lint bucket.json # validates as ghost.bucket/v1 +ghost-expression lint path/to/file --format json # machine-readable output + +# Pipeline orchestration — what stage to run next +ghost-expression scan-status # checks cwd +ghost-expression scan-status path/to/scan-dir + +# Bucket ops — deterministic +ghost-expression bucket merge a.json b.json -o merged.json +ghost-expression bucket fix-ids draft.json -o final.json + +# Inspection of expressions +ghost-expression describe # section ranges + token estimates +ghost-expression diff a/expression.md b/expression.md # structural prose-level diff + # (NOT vector distance — see `ghost-drift compare`) + +# Emit derived artifacts ghost-expression emit review-command # → .claude/commands/design-review.md ghost-expression emit context-bundle # → ghost-context/ (SKILL.md + tokens.css + prompt.md) ghost-expression emit context-bundle --prompt-only # single prompt.md @@ -49,34 +71,51 @@ import { lintExpression, layoutExpression, diffExpressions, + inventory, + lintMap, + scanStatus, } from "ghost-expression"; +import { + lintBucket, + mergeBuckets, + recomputeBucketIds, + type Bucket, +} from "@ghost/core"; + const { expression } = parseExpression(await readFile("expression.md", "utf8")); const report = lintExpression(source); const layout = layoutExpression(source); // section ranges + token estimates -const diff = diffExpressions(a, b); // structural prose diff +const diff = diffExpressions(a, b); // structural prose diff +const status = await scanStatus("./scan"); // per-stage state + recommended next ``` -All exports are browser-safe. +All exports are browser-safe except `inventory` (reads from disk). ## BYOA — bring your own agent -Install the skill bundle so your agent can author against the schema: +Install the skill bundle so your agent can author against the schemas: ```bash ghost-expression emit skill ``` -The bundle ships: +The bundle ships four recipes: + +- **`scan.md`** — meta-recipe orchestrating topology → survey → profile end-to-end via `scan-status` checkpoints. Use when the user wants a full scan rather than a specific stage. +- **`map.md`** — write `map.md` from a target's `inventory` output. Stage 1. +- **`survey.md`** — write `bucket.json` from a target's source code. Stage 2. The load-bearing exhaustiveness rule lives here: enumerate the canonical signal in *this* repo (registry, manifest, named declarations) and cross-check counts; sampling is forbidden. +- **`profile.md`** — interpret a `bucket.json` into `expression.md`. Stage 3. Cannot fabricate values not in the bucket; cites bucket rows as evidence. + +Plus a condensed schema reference (`schema.md`) for the `expression.md` frontmatter / body partition. -- `profile.md` — recipe for writing `expression.md` from a project (mode-branched: `target` / `module` / `rollup`). -- `schema.md` — condensed reference to the frontmatter schema and three-layer body. +Once installed, ask your agent to "scan this design language end-to-end" (or just "profile this design language") and it'll follow the recipes, ending each stage at the relevant `lint` invocation as the success gate. -Once installed, ask your agent to "profile this design language" and it'll follow the recipe, ending at `ghost-expression lint` as the success gate. +## Canonical artifacts -## Canonical artifact +See [`docs/expression-format.md`](https://github.com/block/ghost/blob/main/docs/expression-format.md) for the full `expression.md` spec, including the 49-dim machine-vector breakdown (palette [0–20], spacing [21–30], typography [31–40], surfaces [41–48]). -See [`docs/expression-format.md`](https://github.com/block/ghost/blob/main/docs/expression-format.md) for the full spec, including the 49-dim machine-vector breakdown (palette [0–20], spacing [21–30], typography [31–40], surfaces [41–48]). +The `ghost.bucket/v1` schema and `ghost.map/v1` schema both live in `@ghost/core`; the condensed authoring references ship in this package's skill bundle. ## License diff --git a/packages/ghost-expression/src/cli.ts b/packages/ghost-expression/src/cli.ts index e1c7b4f..936b4f4 100644 --- a/packages/ghost-expression/src/cli.ts +++ b/packages/ghost-expression/src/cli.ts @@ -1,26 +1,38 @@ import { readFileSync } from "node:fs"; -import { readFile } from "node:fs/promises"; +import { readFile, writeFile } from "node:fs/promises"; import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; +import { + type Bucket, + type BucketLintReport, + lintBucket, + mergeBuckets, + recomputeBucketIds, +} from "@ghost/core"; import { cac } from "cac"; import { diffExpressions, EXPRESSION_FILENAME, formatLayout, formatSemanticDiff, + inventory, layoutExpression, lintExpression, + lintMap, loadExpression, + scanStatus, } from "./core/index.js"; import { registerEmitCommand } from "./emit-command.js"; /** * Build the cac CLI for `ghost-expression`. * - * Four deterministic verbs author and validate `expression.md`: - * `lint` (schema check), `describe` (section ranges + token estimates), - * `diff` (structural prose-level diff between two expressions), and - * `emit` (derive review-command, context-bundle, or skill artifacts). + * Verbs author and validate `expression.md` and `bucket.json`: + * `lint` (schema check, auto-detects file kind), `describe` (section ranges + * + token estimates for expressions), `diff` (structural prose-level diff + * between two expressions), `emit` (derive review-command, context-bundle, + * or skill artifacts), and `bucket merge` (deterministic union of N + * `ghost.bucket/v1` files into one). * * Embedding-based comparison lives in `ghost-drift`. `diff` here is * text/structural — what decisions and palette roles changed — not @@ -32,15 +44,22 @@ export function buildCli(): ReturnType { // --- lint --- cli .command( - "lint [expression]", - "Validate expression.md schema and body/frontmatter coherence", + "lint [file]", + "Validate expression.md, map.md, or bucket.json — auto-detects the kind from path/content", ) .option("--format ", "Output format: cli or json", { default: "cli" }) .action(async (path: string | undefined, opts) => { try { const target = resolve(process.cwd(), path ?? EXPRESSION_FILENAME); const raw = await readFile(target, "utf-8"); - const report = lintExpression(raw); + const kind = detectFileKind(target, raw); + + const report = + kind === "bucket" + ? lintBucketFile(raw) + : kind === "map" + ? lintMap(raw) + : lintExpression(raw); if (opts.format === "json") { process.stdout.write(`${JSON.stringify(report, null, 2)}\n`); @@ -71,6 +90,69 @@ export function buildCli(): ReturnType { } }); + // --- scan-status --- + cli + .command( + "scan-status [dir]", + "Report which scan stages have produced artifacts in a directory: topology (map.md), objective (bucket.json), subjective (expression.md). Tells orchestrators which stage to run next.", + ) + .option("--format ", "Output format: cli or json", { default: "cli" }) + .action(async (dirArg: string | undefined, opts) => { + try { + const dir = resolve(process.cwd(), dirArg ?? "."); + const status = await scanStatus(dir); + if (opts.format === "json") { + process.stdout.write(`${JSON.stringify(status, null, 2)}\n`); + } else { + const fmt = (state: string) => + state === "present" ? "present" : "missing"; + process.stdout.write(`scan dir: ${status.dir}\n\n`); + process.stdout.write( + ` topology (map.md): ${fmt(status.topology.state)}\n`, + ); + process.stdout.write( + ` objective (bucket.json): ${fmt(status.objective.state)}\n`, + ); + process.stdout.write( + ` subjective (expression.md): ${fmt(status.subjective.state)}\n\n`, + ); + if (status.recommended_next) { + process.stdout.write( + `next: run the ${status.recommended_next} stage\n`, + ); + } else { + process.stdout.write("next: scan complete — all stages present\n"); + } + } + process.exit(0); + } catch (err) { + console.error( + `Error: ${err instanceof Error ? err.message : String(err)}`, + ); + process.exit(2); + } + }); + + // --- inventory --- + cli + .command( + "inventory [path]", + "Emit deterministic raw signals about a frontend repo as JSON: package manifests, language histogram, candidate config files, registry presence, top-level tree, git remote. Feeds the topology recipe (map.md authoring).", + ) + .action(async (path: string | undefined) => { + try { + const target = resolve(process.cwd(), path ?? "."); + const out = inventory(target); + process.stdout.write(`${JSON.stringify(out, null, 2)}\n`); + process.exit(0); + } catch (err) { + process.stderr.write( + `Error: ${err instanceof Error ? err.message : String(err)}\n`, + ); + process.exit(2); + } + }); + // --- describe --- cli .command( @@ -132,6 +214,92 @@ export function buildCli(): ReturnType { } }); + // --- bucket --- + cli + .command( + "bucket [...buckets]", + "Operate on ghost.bucket/v1 files. Ops: merge (concat with id-based dedup, deterministic and idempotent), fix-ids (recompute every row's id from content; use after authoring rows with empty id fields).", + ) + .option( + "-o, --out ", + "Write the result to this path (default: stdout)", + ) + .action(async (op: string, buckets: string[], opts) => { + try { + if (op !== "merge" && op !== "fix-ids") { + console.error( + `Error: unknown bucket op '${op}'. Supported: merge, fix-ids`, + ); + process.exit(2); + return; + } + if (!Array.isArray(buckets) || buckets.length === 0) { + console.error(`Error: bucket ${op} requires at least one input file`); + process.exit(2); + return; + } + if (op === "fix-ids" && buckets.length !== 1) { + console.error("Error: bucket fix-ids takes exactly one input file"); + process.exit(2); + return; + } + + const parsed: Bucket[] = []; + for (const path of buckets) { + const target = resolve(process.cwd(), path); + const raw = await readFile(target, "utf-8"); + let json: unknown; + try { + json = JSON.parse(raw); + } catch (err) { + console.error( + `Error: ${target} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`, + ); + process.exit(2); + return; + } + if (op === "merge") { + const report = lintBucket(json); + if (report.errors > 0) { + console.error( + `Error: ${target} failed bucket lint with ${report.errors} error(s); fix before merging`, + ); + for (const issue of report.issues) { + if (issue.severity !== "error") continue; + const pathSuffix = issue.path ? ` @ ${issue.path}` : ""; + console.error( + ` [${issue.rule}] ${issue.message}${pathSuffix}`, + ); + } + process.exit(1); + return; + } + } + parsed.push(json as Bucket); + } + + const result = + op === "merge" + ? mergeBuckets(...parsed) + : recomputeBucketIds(parsed[0]); + const out = `${JSON.stringify(result, null, 2)}\n`; + + if (opts.out) { + const outPath = resolve(process.cwd(), opts.out); + await writeFile(outPath, out, "utf-8"); + } else { + process.stdout.write(out); + } + + process.exit(0); + } catch (err) { + console.error( + `Error: ${err instanceof Error ? err.message : String(err)}`, + ); + process.exit(2); + } + }); + registerEmitCommand(cli); cli.help(); @@ -140,6 +308,50 @@ export function buildCli(): ReturnType { return cli; } +/** + * Decide whether a file is an `expression.md`, a `map.md`, or a + * `bucket.json`. JSON paths/contents route to the bucket linter; markdown + * with `schema: ghost.map/v1` in its YAML frontmatter routes to the map + * linter; everything else stays on the expression path. + */ +function detectFileKind( + path: string, + raw: string, +): "bucket" | "map" | "expression" { + if (path.toLowerCase().endsWith(".json")) return "bucket"; + if (raw.trimStart().startsWith("{")) return "bucket"; + // Cheap markdown frontmatter sniff for `schema: ghost.map/v1`. We don't + // parse YAML here; the linter does the heavy lift. + const fmEnd = raw.indexOf("\n---", 3); + if (raw.startsWith("---") && fmEnd > 0) { + const fm = raw.slice(0, fmEnd); + if (/\bschema:\s*ghost\.map\/v1\b/.test(fm)) return "map"; + } + if (path.toLowerCase().endsWith("map.md")) return "map"; + return "expression"; +} + +function lintBucketFile(raw: string): BucketLintReport { + let json: unknown; + try { + json = JSON.parse(raw); + } catch (err) { + return { + issues: [ + { + severity: "error", + rule: "bucket-not-json", + message: `bucket file is not valid JSON: ${err instanceof Error ? err.message : String(err)}`, + }, + ], + errors: 1, + warnings: 0, + info: 0, + }; + } + return lintBucket(json); +} + function readPackageVersion(): string { const here = dirname(fileURLToPath(import.meta.url)); const pkg = JSON.parse( diff --git a/packages/ghost-expression/src/core/body.ts b/packages/ghost-expression/src/core/body.ts index 54a6291..75ce1b4 100644 --- a/packages/ghost-expression/src/core/body.ts +++ b/packages/ghost-expression/src/core/body.ts @@ -2,15 +2,13 @@ import type { DesignDecision } from "@ghost/core"; /** * Structured read of an expression.md body. The body is authoritative for - * prose — # Character, # Signature, and per-dimension rationale under - * # Decisions. Machine-facts (dimension slugs, evidence, tokens) live in - * the frontmatter and are joined in by `applyBody` during parse. + * prose — # Character and per-dimension rationale under # Decisions. + * Machine-facts (dimension slugs, evidence, tokens) live in the + * frontmatter and are joined in by `applyBody` during parse. */ export interface BodyData { /** From `# Character` — authoritative source for DesignObservation.summary */ character?: string; - /** From `# Signature` bullets — authoritative for DesignObservation.distinctiveTraits */ - signature?: string[]; /** From `# Decisions` `### slug` blocks — dimension + prose rationale (no evidence) */ decisions?: DesignDecision[]; } @@ -104,12 +102,13 @@ export function parseBody(md: string): BodyData { const h = sec.heading.toLowerCase(); if (h.startsWith("character")) { out.character = sec.body; - } else if (h.startsWith("signature")) { - out.signature = parseBullets(sec.body); } else if (h.startsWith("decisions")) { const blocks = sectionsAt(sec.body, 3); if (blocks.length) out.decisions = blocks.map(parseDecision); } + // Other H1 sections (legacy `# Signature`, `# Fragments`, etc.) are + // ignored. Legacy `# Signature` blocks in older expression.md files + // parse as inert prose without contributing to the model. } return out; } diff --git a/packages/ghost-expression/src/core/compose.ts b/packages/ghost-expression/src/core/compose.ts index 593a2b1..3e99699 100644 --- a/packages/ghost-expression/src/core/compose.ts +++ b/packages/ghost-expression/src/core/compose.ts @@ -30,14 +30,6 @@ export function mergeExpression( ); } - if (base.roles || overlay.roles) { - merged.roles = mergeByKey( - base.roles ?? [], - overlay.roles ?? [], - (r) => r.name, - ); - } - if (base.palette || overlay.palette) { const basePalette = base.palette; const overlayPalette = overlay.palette; diff --git a/packages/ghost-expression/src/core/context/review-command.ts b/packages/ghost-expression/src/core/context/review-command.ts index 5cd7b3e..676aab5 100644 --- a/packages/ghost-expression/src/core/context/review-command.ts +++ b/packages/ghost-expression/src/core/context/review-command.ts @@ -1,4 +1,10 @@ -import type { Expression } from "@ghost/core"; +import type { DriftSeverity, Expression, Rule } from "@ghost/core"; +import { + computeRuleSeverity, + resolveMatchShape, + resolveTolerance, + tierForCanonical, +} from "@ghost/core"; export interface EmitReviewInput { expression: Expression; @@ -16,6 +22,17 @@ export interface EmitReviewInput { * radii and weights. Universal accessibility rules are out of scope — * those belong in Rams or a sibling a11y skill. * + * Two emission paths: + * - **Rules-driven** (preferred, v0+): when `expression.rules[]` is + * non-empty, group rules by computed perceptual severity and render + * a Critical / Serious / Nit layout. Severity is computed from the + * perceptual prior in `@ghost/core` plus per-rule overrides and + * presence-floor escalation against bucket-proxy counts. + * - **Structured-fallback** (legacy): when no rules[] are present, + * emit the original palette/radius/spacing/typography sections + * derived from frontmatter alone. Preserved verbatim so existing + * expressions keep working through the v0 transition. + * * Pure: deterministic over the same expression. The expression is * expected to be the unioned result of `loadExpression` — body prose * (Character summary, per-decision rationale) is already folded into @@ -23,6 +40,196 @@ export interface EmitReviewInput { */ export function emitReviewCommand(input: EmitReviewInput): string { const { expression: fp } = input; + + if (fp.rules && fp.rules.length > 0) { + return emitRulesDriven(fp); + } + + return emitStructuredFallback(fp); +} + +// --- Rules-driven path (v0+) ------------------------------------------- + +/** + * Render a rules[]-driven slash command. Groups rules by computed + * severity, renders one block per rule with rationale + pattern + match + * shape, then closes with a calibration footer that explains *why* + * severities landed where they did. The calibration footer is what makes + * Ghost's reviewer legibly different from a generic linter — the prior + * is visible, not opaque. + */ +function emitRulesDriven(fp: Expression): string { + const id = fp.id; + const personality = (fp.observation?.personality ?? []).join(", "); + const cousins = (fp.observation?.resembles ?? []).join(", "); + const character = fp.observation?.summary?.trim() ?? ""; + + const resolved = (fp.rules ?? []).map((rule) => ({ + rule, + severity: computeRuleSeverity(rule, bucketCountProxy(rule, fp)), + match: resolveMatchShape(rule), + tolerance: resolveTolerance(rule), + })); + + const grouped: Record = { + critical: [], + serious: [], + nit: [], + }; + for (const r of resolved) grouped[r.severity].push(r); + + const sections: string[] = []; + if (grouped.critical.length) { + sections.push(renderSeverityBlock("Critical", grouped.critical)); + } + if (grouped.serious.length) { + sections.push(renderSeverityBlock("Serious", grouped.serious)); + } + if (grouped.nit.length) { + sections.push(renderSeverityBlock("Nit", grouped.nit)); + } + + const parts = [ + frontmatter(id), + header(id, personality, cousins, character), + modeSection(), + ...sections, + outputTemplate(id), + guidelines(), + calibrationFooter(fp, resolved), + ]; + return `${parts.filter(Boolean).join("\n\n").trim()}\n`; +} + +interface ResolvedRule { + rule: Rule; + severity: DriftSeverity; + match: string; + tolerance: number | undefined; +} + +function renderSeverityBlock(label: string, items: ResolvedRule[]): string { + const lines: string[] = [`## ${label} (${items.length})`]; + for (const item of items) { + lines.push("", renderRule(item)); + } + return lines.join("\n"); +} + +function renderRule(item: ResolvedRule): string { + const { rule, match, tolerance } = item; + const heading = rule.canonical + ? `### \`${rule.id}\` — ${rule.canonical}` + : `### \`${rule.id}\``; + const lines: string[] = [heading]; + if (rule.summary) lines.push("", rule.summary); + if (rule.rationale) lines.push("", `> ${rule.rationale}`); + lines.push("", `**Pattern:** \`${rule.pattern}\``); + + const matchLine = + tolerance !== undefined + ? `**Match:** \`${match}\` (tolerance: \`${tolerance}\`)` + : `**Match:** \`${match}\``; + lines.push(matchLine); + + if (rule.enforce_at?.length) { + const where = rule.enforce_at.map((e) => `\`${e}\``).join(", "); + lines.push(`**Enforce at:** ${where}`); + } + if (typeof rule.support === "number") { + lines.push(`**Support:** ${(rule.support * 100).toFixed(0)}%`); + } + return lines.join("\n"); +} + +function calibrationFooter(fp: Expression, resolved: ResolvedRule[]): string { + const tierCounts = { loud: 0, structural: 0, rhythmic: 0 }; + const escalated: string[] = []; + + for (const r of resolved) { + const baseTier = tierForCanonical(r.rule.canonical); + tierCounts[baseTier]++; + const finalTierFromSeverity = + r.severity === "critical" + ? "loud" + : r.severity === "serious" + ? "structural" + : "rhythmic"; + if ( + finalTierFromSeverity !== baseTier && + r.rule.severity === undefined // not a manual override + ) { + escalated.push(`\`${r.rule.id}\``); + } + } + + const lines: string[] = [ + "## How this reviewer was calibrated", + "", + `Severity grouping reflects perceptual weight, not arithmetic. \`${fp.id}\` has ${tierCounts.loud} loud-tier, ${tierCounts.structural} structural-tier, and ${tierCounts.rhythmic} rhythmic-tier rules under the canonical perceptual prior.`, + ]; + if (escalated.length) { + lines.push( + "", + `**Presence-floor escalation triggered for:** ${escalated.join(", ")}. These dimensions are silent (or near-silent) in the bucket — adding to them crosses a presence boundary, which is the loudest possible change.`, + ); + } + lines.push( + "", + "Color and font-family rules are loud (critical) by default. Shape, elevation, surface, and interactive-pattern rules are structural (serious). Spacing, density, motion-detail, and theming rules are rhythmic (nit).", + "", + `Generated from \`expression.md\` (${(fp.rules ?? []).length} rules). Re-run \`ghost-expression emit review-command\` after expression updates.`, + ); + return lines.join("\n"); +} + +/** + * Coarse proxy for bucket-count per canonical dimension, derived from the + * structured frontmatter fields. v0 expressions don't carry the bucket + * directly; this proxy lets presence-floor escalation work against the + * derived counts. v1 will replace this with the actual bucket count once + * `loadExpression` returns the bucket alongside the expression. + */ +function bucketCountProxy(rule: Rule, fp: Expression): number { + switch (rule.canonical) { + case "color-strategy": + return ( + fp.palette.dominant.length + + fp.palette.neutrals.count + + fp.palette.semantic.length + ); + case "surface-hierarchy": + return fp.palette.semantic.length + fp.palette.dominant.length; + case "shape-language": + return fp.surfaces.borderRadii.length; + case "elevation": + return fp.surfaces.shadowComplexity === "deliberate-none" + ? 0 + : fp.surfaces.shadowComplexity === "subtle" + ? 2 + : 5; + case "spatial-system": + case "density": + return fp.spacing.scale.length; + case "typography-voice": + return fp.typography.sizeRamp.length; + case "font-sourcing": + return fp.typography.families.length; + case "motion": + // Motion isn't in structured fields; default to a count above + // typical floors so escalation only happens via explicit author + // hint (rule.presence_floor: 2+). + return 100; + default: + // Unknown canonical → leave room above floor 0 so escalation + // doesn't fire incorrectly, but author can override via floor. + return 100; + } +} + +// --- Structured-fallback path (legacy) --------------------------------- + +function emitStructuredFallback(fp: Expression): string { const id = fp.id; const personality = (fp.observation?.personality ?? []).join(", "); const cousins = (fp.observation?.resembles ?? []).join(", "); diff --git a/packages/ghost-expression/src/core/context/writer.ts b/packages/ghost-expression/src/core/context/writer.ts index 14ffe16..6b80dde 100644 --- a/packages/ghost-expression/src/core/context/writer.ts +++ b/packages/ghost-expression/src/core/context/writer.ts @@ -138,18 +138,17 @@ export function buildSkillMd( const body = `This skill grounds UI generation in the **${name}** design language. -Read \`expression.md\` first — it is the source of truth. It has four layered sections: +Read \`expression.md\` first — it is the source of truth. It has these layered sections: -1. **Character** — what this expression is (one-paragraph summary) -2. **Signature** — what makes it distinctive (bullet list of traits) -3. **Decisions** — specific design choices with evidence from the source -4. **Values** — hard Do / Don't rules +1. **Character** — what this expression is (one-paragraph summary in the body) +2. **Decisions** — abstract design choices with evidence from the source (body \`### dimension\` blocks) +3. **Rules** — grep-friendly review patterns with severity (frontmatter \`rules[]\`); \`presence_floor\` rules codify load-bearing absences When generating UI in this language: -- Treat **Values** as non-negotiable gates — never violate a Don't. +- Treat **Rules** as non-negotiable gates — every emitted reviewer enforces them. - Use **Decisions** as the lookup for specific choices (spacing scale, type ramp, radii). -- Let **Character** and **Signature** shape overall feel, density, and voice. +- Let **Character** shape overall feel, density, and voice. - Prefer tokens from the YAML frontmatter (palette, spacing, typography, surfaces) over arbitrary values. ## Files @@ -175,10 +174,6 @@ function buildPromptMd(expression: Expression, name: string): string { const summary = expression.observation?.summary?.trim(); if (summary) parts.push(`# Character\n\n${summary}`); - const traits = expression.observation?.distinctiveTraits ?? []; - if (traits.length) - parts.push(`# Signature\n\n${traits.map((t) => `- ${t}`).join("\n")}`); - const decisions = expression.decisions ?? []; if (decisions.length) parts.push(`# Decisions\n\n${decisions.map(formatDecision).join("\n\n")}`); @@ -193,18 +188,18 @@ function buildPromptMd(expression: Expression, name: string): string { } function buildReadmeMd(expression: Expression, name: string): string { - const traits = expression.observation?.distinctiveTraits ?? []; - const traitsLine = traits.length - ? ` Signature traits: ${traits.slice(0, 3).join(", ")}.` + const personality = expression.observation?.personality ?? []; + const personalityLine = personality.length + ? ` Personality: ${personality.slice(0, 3).join(", ")}.` : ""; return `# ${name} — design context bundle -Generated by \`ghost-drift emit context-bundle\`. Grounding material for AI UI generation in the **${name}** design language.${traitsLine} +Generated by \`ghost-drift emit context-bundle\`. Grounding material for AI UI generation in the **${name}** design language.${personalityLine} ## Files - \`SKILL.md\` — Agent Skill manifest (user-invocable) -- \`expression.md\` — canonical design language (YAML frontmatter + Character/Signature/Decisions) +- \`expression.md\` — canonical design language (YAML frontmatter + Character/Decisions) - \`tokens.css\` — CSS custom properties derived from expression tokens - \`README.md\` — this file @@ -219,9 +214,9 @@ Generated by \`ghost-drift emit context-bundle\`. Grounding material for AI UI g } function buildSkillDescription(expression: Expression, name: string): string { - const traits = expression.observation?.distinctiveTraits ?? []; - const traitPhrase = traits.length - ? ` (${traits.slice(0, 3).join(", ")})` + const personality = expression.observation?.personality ?? []; + const traitPhrase = personality.length + ? ` (${personality.slice(0, 3).join(", ")})` : ""; return `Use this skill to generate UI in the ${name} design language${traitPhrase}. Contains the canonical expression and token reference.`; } diff --git a/packages/ghost-expression/src/core/fragments.ts b/packages/ghost-expression/src/core/fragments.ts index 90eb661..841fbf2 100644 --- a/packages/ghost-expression/src/core/fragments.ts +++ b/packages/ghost-expression/src/core/fragments.ts @@ -68,6 +68,10 @@ function parseFragment( const dimension = typeof yamlObj.dimension === "string" ? yamlObj.dimension : filenameSlug; + const dimensionKind = + typeof yamlObj.dimension_kind === "string" + ? yamlObj.dimension_kind + : undefined; const evidence = Array.isArray(yamlObj.evidence) ? yamlObj.evidence.filter((e): e is string => typeof e === "string") : []; @@ -75,7 +79,12 @@ function parseFragment( const decisionText = prose.trim(); if (!dimension || !decisionText) return null; - return { dimension, decision: decisionText, evidence }; + return { + dimension, + decision: decisionText, + evidence, + ...(dimensionKind ? { dimension_kind: dimensionKind } : {}), + }; } /** diff --git a/packages/ghost-expression/src/core/frontmatter.ts b/packages/ghost-expression/src/core/frontmatter.ts index 2866990..1bdd557 100644 --- a/packages/ghost-expression/src/core/frontmatter.ts +++ b/packages/ghost-expression/src/core/frontmatter.ts @@ -26,9 +26,9 @@ export interface FrontmatterData { /** * Expression fields that are populated from YAML frontmatter. Prose - * fields (observation.summary, observation.distinctiveTraits, decisions[].decision, - * values) are populated from the markdown body by `applyBody` — they are - * deliberately NOT listed here. + * fields (observation.summary, decisions[].decision) are populated from + * the markdown body by `applyBody` — they are deliberately NOT listed + * here. */ const EXPRESSION_KEYS = new Set([ "id", @@ -37,11 +37,11 @@ const EXPRESSION_KEYS = new Set([ "sources", "observation", "decisions", + "rules", "palette", "spacing", "typography", "surfaces", - "roles", "embedding", ]); @@ -111,11 +111,11 @@ export function mergeFrontmatter( "sources", "observation", "decisions", + "rules", "palette", "spacing", "typography", "surfaces", - "roles", "embedding", ]; for (const key of ordered) { @@ -154,6 +154,7 @@ function stripDecisionProse( if (!decisions?.length) return undefined; return decisions.map((d) => { const out: Record = { dimension: d.dimension }; + if (d.dimension_kind) out.dimension_kind = d.dimension_kind; if (d.embedding) out.embedding = d.embedding; return out; }); diff --git a/packages/ghost-expression/src/core/index.ts b/packages/ghost-expression/src/core/index.ts index 1c05b15..20cf868 100644 --- a/packages/ghost-expression/src/core/index.ts +++ b/packages/ghost-expression/src/core/index.ts @@ -1,7 +1,7 @@ import { readFile } from "node:fs/promises"; import { dirname, isAbsolute, resolve } from "node:path"; -import type { DesignDecision, Expression } from "@ghost/core"; -import { computeEmbedding } from "@ghost/core"; +import type { DesignDecision, Expression, SemanticColor } from "@ghost/core"; +import { computeEmbedding, parseColorToOklch } from "@ghost/core"; import { mergeExpression } from "./compose.js"; import { loadDecisionFragments, @@ -54,6 +54,7 @@ export { serializeEmbeddingFragment, } from "./fragments.js"; export type { ExpressionMeta, FrontmatterData } from "./frontmatter.js"; +export { inventory } from "./inventory.js"; export type { ExpressionLayout, ExpressionLayoutSection, @@ -66,8 +67,21 @@ export type { LintSeverity, } from "./lint.js"; export { lintExpression } from "./lint.js"; +export type { + MapLintIssue, + MapLintReport, + MapLintSeverity, +} from "./lint-map.js"; +export { lintMap } from "./lint-map.js"; export type { ParsedExpression, ParseOptions } from "./parser.js"; export { parseExpression, splitRaw } from "./parser.js"; +export type { + ScanStage, + ScanStageReport, + ScanStageState, + ScanStatus, +} from "./scan-status.js"; +export { scanStatus } from "./scan-status.js"; export type { FrontmatterShape } from "./schema.js"; export { FrontmatterSchema, @@ -98,7 +112,7 @@ export interface LoadOptions { * * If the file declares `extends:`, the base expression is loaded recursively and * merged per the rules in compose.ts: overlay wins, decisions merged by - * dimension, palette roles merged by role. + * dimension, palette colors merged by role. * * If a `decisions/` directory sits next to the expression.md, each .md * inside is assembled into the expression's decisions[], merged by @@ -127,6 +141,13 @@ export async function loadExpression( } } + // Backfill `oklch` on palette colors that arrived hex-only. Deterministic + // (same hex → same oklch), so re-parsing the same expression always + // yields the same in-memory shape. Without this, `comparePalette` + // misreads hex-only colors as fully unmatched (distance 1) and even + // self-distance comes out non-zero. + backfillPaletteOklch(parsed.expression); + if (!options.noEmbeddingBackfill) { parsed.expression.embedding = await resolveEmbedding( parsed.expression, @@ -138,6 +159,22 @@ export async function loadExpression( return parsed; } +function backfillPaletteOklch(expression: Expression): void { + if (!expression.palette) return; + if (expression.palette.dominant) { + expression.palette.dominant = expression.palette.dominant.map(ensureOklch); + } + if (expression.palette.semantic) { + expression.palette.semantic = expression.palette.semantic.map(ensureOklch); + } +} + +function ensureOklch(color: SemanticColor): SemanticColor { + if (color.oklch && color.oklch.length === 3) return color; + const oklch = parseColorToOklch(color.value); + return oklch ? { ...color, oklch } : color; +} + /** * Resolve the embedding for an expression.md in order: * 1. Inline `embedding:` in frontmatter (trust as cache). diff --git a/packages/ghost-map/src/core/inventory.ts b/packages/ghost-expression/src/core/inventory.ts similarity index 99% rename from packages/ghost-map/src/core/inventory.ts rename to packages/ghost-expression/src/core/inventory.ts index 4766dad..dbc8bc1 100644 --- a/packages/ghost-map/src/core/inventory.ts +++ b/packages/ghost-expression/src/core/inventory.ts @@ -6,7 +6,7 @@ import type { InventoryOutput, LanguageHistogramEntry, TopLevelEntry, -} from "./types.js"; +} from "@ghost/core"; /** * Canonical package manifests we scan for at the inventoried root. diff --git a/packages/ghost-expression/src/core/layout.ts b/packages/ghost-expression/src/core/layout.ts index c2cb6b1..ef04f4f 100644 --- a/packages/ghost-expression/src/core/layout.ts +++ b/packages/ghost-expression/src/core/layout.ts @@ -56,7 +56,8 @@ export function layoutExpression(raw: string): ExpressionLayout { }); } - // H1 body sections: # Character, # Signature, # Decisions, # Fragments, … + // H1 body sections: # Character, # Decisions, # Fragments, … + // Legacy `# Signature` blocks parse here too as inert body sections. const h1s = scanHeadings(lines, 1, bodyStart); for (let i = 0; i < h1s.length; i++) { const h = h1s[i]; @@ -127,7 +128,6 @@ function detectPartitions(yamlText: string): string[] { "spacing", "typography", "surfaces", - "roles", "observation", "decisions", "embedding", diff --git a/packages/ghost-map/src/core/lint.ts b/packages/ghost-expression/src/core/lint-map.ts similarity index 91% rename from packages/ghost-map/src/core/lint.ts rename to packages/ghost-expression/src/core/lint-map.ts index 8351a98..eb88308 100644 --- a/packages/ghost-map/src/core/lint.ts +++ b/packages/ghost-expression/src/core/lint-map.ts @@ -1,23 +1,19 @@ +import { MapFrontmatterSchema, REQUIRED_BODY_SECTIONS } from "@ghost/core"; import { parse as parseYaml } from "yaml"; import type { z } from "zod"; -import { - MapFrontmatterSchema, - REQUIRED_BODY_SECTIONS, - type RequiredBodySection, -} from "./schema.js"; -export type LintSeverity = "error" | "warning" | "info"; +export type MapLintSeverity = "error" | "warning" | "info"; -export interface LintIssue { - severity: LintSeverity; +export interface MapLintIssue { + severity: MapLintSeverity; rule: string; message: string; /** Dotted path within frontmatter (e.g. "languages[0].share"), or section name. */ path?: string; } -export interface LintReport { - issues: LintIssue[]; +export interface MapLintReport { + issues: MapLintIssue[]; errors: number; warnings: number; info: number; @@ -30,8 +26,8 @@ export interface LintReport { * violations, missing body sections, out-of-order body sections, and empty * body sections. */ -export function lintMap(raw: string): LintReport { - const issues: LintIssue[] = []; +export function lintMap(raw: string): MapLintReport { + const issues: MapLintIssue[] = []; const split = splitFrontmatter(raw); if (!split) { @@ -95,8 +91,8 @@ export function lintMap(raw: string): LintReport { */ function checkDesignSystemCoherence( fm: ReturnType, -): LintIssue[] { - const out: LintIssue[] = []; +): MapLintIssue[] { + const out: MapLintIssue[] = []; const ds = fm.design_system; const hasEntry = (ds.entry_files?.length ?? 0) > 0; const hasDerived = (ds.derived_files?.length ?? 0) > 0; @@ -160,7 +156,7 @@ function splitFrontmatter(raw: string): FrontmatterSplit | null { return { frontmatter, body }; } -function zodIssues(error: z.ZodError): LintIssue[] { +function zodIssues(error: z.ZodError): MapLintIssue[] { return error.issues.map((issue) => { const path = issue.path.filter( (segment): segment is string | number => typeof segment !== "symbol", @@ -170,7 +166,7 @@ function zodIssues(error: z.ZodError): LintIssue[] { rule: `frontmatter:${issue.code}`, message: issue.message, path: path.length > 0 ? formatPath(path) : undefined, - } satisfies LintIssue; + } satisfies MapLintIssue; }); } @@ -194,8 +190,8 @@ interface FoundSection { bodyText: string; // content between this heading and the next } -function checkBodySections(body: string): LintIssue[] { - const issues: LintIssue[] = []; +function checkBodySections(body: string): MapLintIssue[] { + const issues: MapLintIssue[] = []; const sections = scanH2Sections(body); // Build a lookup of which required sections appear, in what order. @@ -305,7 +301,7 @@ function scanH2Sections(body: string): FoundSection[] { return out; } -function finalize(issues: LintIssue[]): LintReport { +function finalize(issues: MapLintIssue[]): MapLintReport { let errors = 0; let warnings = 0; let info = 0; @@ -316,8 +312,3 @@ function finalize(issues: LintIssue[]): LintReport { } return { issues, errors, warnings, info }; } - -export const MAP_FILENAME = "map.md"; - -// Type re-exports for callers -export type { RequiredBodySection }; diff --git a/packages/ghost-expression/src/core/lint.ts b/packages/ghost-expression/src/core/lint.ts index 54f64b1..a73f178 100644 --- a/packages/ghost-expression/src/core/lint.ts +++ b/packages/ghost-expression/src/core/lint.ts @@ -1,12 +1,11 @@ -import type { Expression } from "@ghost/core"; +import { + closestCanonical, + type Expression, + isCanonicalDimension, +} from "@ghost/core"; import { parse as parseYaml } from "yaml"; import type { BodyData } from "./body.js"; import { parseExpression, splitRaw } from "./parser.js"; -import { - formatReferenceError, - isTokenReference, - resolveTokenReference, -} from "./references.js"; import { FrontmatterSchema } from "./schema.js"; export type LintSeverity = "error" | "warning" | "info"; @@ -72,7 +71,7 @@ export function lintExpression( checkStrayEvidenceInBody(bodyText, rawIssues); checkEvidenceHexes(expression, rawIssues); checkUnusedPalette(expression, rawIssues); - checkRoleReferences(expression, rawIssues); + checkNonCanonicalDimensions(expression, rawIssues); return finalize(rawIssues, strict, off); } @@ -199,18 +198,10 @@ function checkEvidenceHexes(fp: Expression, issues: LintIssue[]): void { /** * Flag palette colors that don't appear anywhere a reader could justify - * them. The search covers three citation paths: - * 1. Hex literal in a decision body's Evidence bullet text. - * 2. Hex literal directly in a `roles[].tokens.palette.` field - * or in a `roles[].evidence` string. - * 3. Slug-binding propagation: `roles[].tokens.palette.` carrying - * a `{palette.dominant.X}` / `{palette.semantic.X}` reference resolves - * through the palette to a hex, which counts as cited. - * - * Rationale: forcing every neutral step to be name-dropped in decision - * prose was over-citing prose for no reader benefit. Role bindings are - * the load-bearing place a hex earns its keep — and the propagation - * makes named slots equivalent to inline hexes for citation purposes. + * them — i.e. not cited as a hex literal in any decision's body Evidence + * bullets or rationale prose. Severity is `info` (a soft hint, not an + * error) because some palette entries are honestly load-bearing without + * decision-level commentary (every neutral step in a wide ramp). */ function checkUnusedPalette(fp: Expression, issues: LintIssue[]): void { const paletteHexes = collectPaletteHexes(fp); @@ -224,72 +215,54 @@ function checkUnusedPalette(fp: Expression, issues: LintIssue[]): void { .map((d) => d.decision) .join("\n") .toLowerCase(); - const roleText = collectRoleHexCitations(fp); - const slugCitedHexes = collectSlugBoundHexes(fp); - const haystack = `${evidenceText}\n${decisionText}\n${roleText}`; + const haystack = `${evidenceText}\n${decisionText}`; for (const hex of paletteHexes) { if (haystack.includes(hex)) continue; - if (slugCitedHexes.has(hex)) continue; issues.push({ severity: "info", rule: "unused-palette", - message: `Palette color ${hex} is not cited in any decision or role binding.`, + message: `Palette color ${hex} is not cited in any decision.`, }); } } /** - * Collect every hex citation reachable from `roles[]`: - * - direct hex literals in `roles[].tokens.palette.` for any slot key - * - any hex that appears inline in a `roles[].evidence` bullet - * - * Returns one big lowercase string so the caller can run substring - * checks against it. Local references (`{palette.dominant.accent}`) are - * resolved separately by `collectSlugBoundHexes`. + * Soft check: a `decisions[].dimension` slug should either be in the + * canonical vocabulary or pair with a canonical `dimension_kind`. Anything + * else lives in the long tail and won't roll up at fleet scale. This + * never errors — it suggests, so authoring stays free-form by default. */ -function collectRoleHexCitations(fp: Expression): string { - const out: string[] = []; - const HEX_LITERAL = /^#[0-9a-f]{3,8}$/i; - for (const role of fp.roles ?? []) { - const palette = role.tokens?.palette; - if (palette) { - for (const value of Object.values(palette)) { - if (typeof value === "string" && HEX_LITERAL.test(value)) { - out.push(value.toLowerCase()); - } - } - } - for (const ev of role.evidence ?? []) { - out.push(ev.toLowerCase()); - } - } - return out.join("\n"); -} - -/** - * Walk `roles[].tokens.palette` for `{palette.dominant.X}` / - * `{palette.semantic.X}` references and return the set of palette hexes - * those references resolve to. Used by `unused-palette` so a hex cited - * only via a slug binding still counts as used. - * - * Unresolvable references are silently skipped — the dedicated - * `broken-role-reference` rule reports those. - */ -function collectSlugBoundHexes(fp: Expression): Set { - const out = new Set(); - for (const role of fp.roles ?? []) { - const palette = role.tokens?.palette; - if (!palette) continue; - for (const value of Object.values(palette)) { - if (!isTokenReference(value)) continue; - const result = resolveTokenReference(fp, value); - if (result.value) { - out.add(result.value.toLowerCase()); - } +function checkNonCanonicalDimensions( + fp: Expression, + issues: LintIssue[], +): void { + const decisions = fp.decisions ?? []; + decisions.forEach((d, idx) => { + if (isCanonicalDimension(d.dimension)) return; + if (d.dimension_kind && isCanonicalDimension(d.dimension_kind)) return; + const suggestion = closestCanonical(d.dimension); + if (d.dimension_kind && !isCanonicalDimension(d.dimension_kind)) { + const fix = closestCanonical(d.dimension_kind) ?? suggestion; + issues.push({ + severity: "warning", + rule: "non-canonical-dimension", + message: `Decision \`${d.dimension}\` has \`dimension_kind: ${d.dimension_kind}\`, which is also not in the canonical vocabulary${ + fix ? ` (closest: \`${fix}\`)` : "" + }. Set \`dimension_kind\` to a canonical slug so fleet aggregation can group this decision.`, + path: `decisions[${idx}].dimension_kind`, + }); + return; } - } - return out; + issues.push({ + severity: "warning", + rule: "non-canonical-dimension", + message: `Decision \`${d.dimension}\` is not a canonical dimension${ + suggestion ? ` (closest: \`${suggestion}\`)` : "" + }. Either rename, or add \`dimension_kind: \` so fleet aggregation can group this decision.`, + path: `decisions[${idx}].dimension`, + }); + }); } function collectPaletteHexes(fp: Expression): Set { @@ -300,76 +273,3 @@ function collectPaletteHexes(fp: Expression): Set { out.add(step.toLowerCase()); return out; } - -/** - * Role palette slots may reference named palette entries via - * `{palette.dominant.}` or `{palette.semantic.}`, or - * opaque external token refs (`{base.color.brand.x}`) for repos - * that pull tokens from a Style-Dictionary-style pipeline. - * - * The slot vocabulary is open (Phase 5b) — any key the consumer - * defines is walked. Local refs that don't resolve fire - * `broken-role-reference`; external refs are accepted as opaque (see - * `isExternalTokenReference`). - */ -function checkRoleReferences(fp: Expression, issues: LintIssue[]): void { - const roles = fp.roles ?? []; - roles.forEach((role, ri) => { - const palette = role.tokens?.palette; - if (!palette) return; - for (const [field, value] of Object.entries(palette)) { - if (!isTokenReference(value)) continue; - // External token refs (Style-Dictionary-style namespaces) are - // accepted as opaque — we can't resolve them without consulting - // the upstream package, and the agent authored them deliberately. - if (isExternalTokenReference(value)) continue; - const result = resolveTokenReference(fp, value); - if (result.error) { - issues.push({ - severity: "error", - rule: "broken-role-reference", - message: formatReferenceError(result.error), - path: `roles[${ri}].tokens.palette.${field}`, - }); - } - } - }); -} - -/** - * Heuristic for "this is a deliberately-opaque external token ref." - * Returns true when the reference clearly targets a foreign namespace — - * either it starts with a recognized Style-Dictionary-style head, or - * it has 4+ dotted segments (deeper than local `palette..`). - * - * Local refs (`{palette.dominant.accent}`, `{palette.semantic.error}`) - * are NOT external — the caller resolves them against the palette and - * fires `broken-role-reference` if they miss. References starting with - * `palette.` but pointing at an unsupported sub-namespace are also - * routed through the resolver so its `unsupported-namespace` error - * surfaces properly. - */ -function isExternalTokenReference(value: string): boolean { - const match = /^\{([^}]+)\}$/.exec(value); - if (!match) return false; - const path = match[1]; - const segments = path.split("."); - // Anything in the local `palette.*` namespace is resolved locally, - // even if the sub-namespace is wrong (the resolver's - // `unsupported-namespace` error is the right diagnostic). - if (segments[0] === "palette") return false; - // External Style-Dictionary-style namespace heads — passthrough. - const externalHeads = new Set([ - "base", - "core", - "semantic", - "component", - "tokens", - "ref", - "sys", - ]); - if (externalHeads.has(segments[0] ?? "")) return true; - // Deeply-nested refs (4+ segments) — heuristic that this is a - // pipeline-generated token, not a flat slug we should resolve. - return segments.length >= 4; -} diff --git a/packages/ghost-expression/src/core/parser.ts b/packages/ghost-expression/src/core/parser.ts index 4f3c85e..a05ccb7 100644 --- a/packages/ghost-expression/src/core/parser.ts +++ b/packages/ghost-expression/src/core/parser.ts @@ -84,8 +84,8 @@ function isDelimiter(line: string): boolean { * Contract: frontmatter and body own disjoint fields. * • Frontmatter owns machine-facts: id, tokens, dimension slugs, evidence, * personality/resembles tags, embedding. - * • Body owns prose: `# Character` → summary, `# Signature` → distinctive - * traits, `### dimension` → decision rationale. + * • Body owns prose: `# Character` → summary, `### dimension` → + * decision rationale. * * The returned expression unions both sources. Since the two sides never * carry the same field, there is no precedence rule — each field has one @@ -116,7 +116,7 @@ export function parseExpression( /** * Fold body-owned prose fields into the expression. The body provides - * Character/Signature prose for `observation` and rationale for `decisions` + * Character prose for `observation` and rationale for `decisions` * (keyed by dimension). Frontmatter-only dimensions keep their evidence * but get no body prose (decision text left empty). */ @@ -137,18 +137,12 @@ function mergeObservation( body: BodyData, ): DesignObservation | undefined { const summary = body.character?.trim() ?? ""; - const distinctiveTraits = body.signature ?? []; const personality = yamlObs?.personality ?? []; const resembles = yamlObs?.resembles ?? []; - if ( - !summary && - distinctiveTraits.length === 0 && - personality.length === 0 && - resembles.length === 0 - ) { + if (!summary && personality.length === 0 && resembles.length === 0) { return undefined; } - return { summary, personality, distinctiveTraits, resembles }; + return { summary, personality, resembles }; } /** @@ -177,6 +171,7 @@ function mergeDecisions( dimension: y.dimension, decision: b?.decision ?? "", evidence: b?.evidence ?? [], + ...(y.dimension_kind ? { dimension_kind: y.dimension_kind } : {}), ...(y.embedding ? { embedding: y.embedding } : {}), }); } diff --git a/packages/ghost-expression/src/core/references.ts b/packages/ghost-expression/src/core/references.ts deleted file mode 100644 index 450d9e6..0000000 --- a/packages/ghost-expression/src/core/references.ts +++ /dev/null @@ -1,125 +0,0 @@ -import type { Expression } from "@ghost/core"; - -/** - * Role token reference syntax: `{palette.dominant.}` or - * `{palette.semantic.}`. Lets a role bind its palette to a named - * palette slot instead of a raw hex — renames cascade, and the linter - * can flag a role pointing at a palette entry that no longer exists. - * - * Only the named palette slots are referenceable in v1. Positional - * inventories (neutrals.steps, typography.sizeRamp, spacing.scale, - * surfaces.borderRadii) have no names to point at, so role tokens for - * those dimensions stay raw. - */ - -/** Matches `{namespace.role}` where namespace contains one or more segments. */ -const REFERENCE_RE = /^\{([a-z][a-z0-9-]*(?:\.[a-z][a-z0-9-]*)+)\}$/i; - -export interface ParsedTokenReference { - /** Full reference string, e.g. `palette.dominant.accent`. */ - path: string; - /** Namespace segment, e.g. `palette.dominant`. */ - namespace: string; - /** Terminal segment — the role name to look up. */ - role: string; -} - -/** True when `value` is wrapped in `{...}` and shaped like a dotted path. */ -export function isTokenReference(value: unknown): value is string { - return typeof value === "string" && REFERENCE_RE.test(value); -} - -/** Parse `{palette.dominant.accent}` → structured form, or null if malformed. */ -export function parseTokenReference( - value: string, -): ParsedTokenReference | null { - const match = REFERENCE_RE.exec(value); - if (!match) return null; - const path = match[1]; - const lastDot = path.lastIndexOf("."); - return { - path, - namespace: path.slice(0, lastDot), - role: path.slice(lastDot + 1), - }; -} - -export type TokenReferenceError = - | { kind: "malformed"; value: string } - | { kind: "unsupported-namespace"; namespace: string; supported: string[] } - | { kind: "unknown-role"; namespace: string; role: string }; - -export interface ResolveResult { - value: string | null; - error: TokenReferenceError | null; -} - -const SUPPORTED_NAMESPACES = ["palette.dominant", "palette.semantic"]; - -/** - * Resolve a `{...}` reference against an expression. Returns the primitive - * value (hex string) when resolvable, plus a structured error describing why - * resolution failed otherwise. Callers that just want the value can ignore - * `error`; the linter uses it to report `broken-role-reference` precisely. - */ -export function resolveTokenReference( - fp: Expression, - value: string, -): ResolveResult { - const parsed = parseTokenReference(value); - if (!parsed) { - return { value: null, error: { kind: "malformed", value } }; - } - - const list = lookupNamespace(fp, parsed.namespace); - if (!list) { - return { - value: null, - error: { - kind: "unsupported-namespace", - namespace: parsed.namespace, - supported: SUPPORTED_NAMESPACES, - }, - }; - } - - const hit = list.find((c) => c.role === parsed.role); - if (!hit) { - return { - value: null, - error: { - kind: "unknown-role", - namespace: parsed.namespace, - role: parsed.role, - }, - }; - } - - return { value: hit.value, error: null }; -} - -function lookupNamespace( - fp: Expression, - namespace: string, -): { role: string; value: string }[] | null { - switch (namespace) { - case "palette.dominant": - return fp.palette?.dominant ?? []; - case "palette.semantic": - return fp.palette?.semantic ?? []; - default: - return null; - } -} - -/** Human-readable message for a resolve error — used by the linter. */ -export function formatReferenceError(error: TokenReferenceError): string { - switch (error.kind) { - case "malformed": - return `\`${error.value}\` is not a valid token reference (expected \`{namespace.role}\`).`; - case "unsupported-namespace": - return `Reference targets \`${error.namespace}\` which is not a valid reference namespace. Supported: ${error.supported.map((n) => `\`${n}\``).join(", ")}.`; - case "unknown-role": - return `Reference \`{${error.namespace}.${error.role}}\` does not resolve — no entry with role \`${error.role}\` in \`${error.namespace}\`.`; - } -} diff --git a/packages/ghost-expression/src/core/scan-status.ts b/packages/ghost-expression/src/core/scan-status.ts new file mode 100644 index 0000000..bfec25f --- /dev/null +++ b/packages/ghost-expression/src/core/scan-status.ts @@ -0,0 +1,93 @@ +import { stat } from "node:fs/promises"; +import { resolve } from "node:path"; +import { BUCKET_FILENAME, MAP_FILENAME } from "@ghost/core"; +import { EXPRESSION_FILENAME } from "./index.js"; + +/** + * Per-stage state in a scan directory. + * + * `missing` — the artifact doesn't exist yet. + * `present` — the artifact exists. Existence is the only signal v1 + * surfaces; hash-based freshness (`stale` vs `present`) is a planned + * enhancement once `.scan-meta.json` is in play. + */ +export type ScanStageState = "missing" | "present"; + +export interface ScanStageReport { + state: ScanStageState; + /** Absolute path to the artifact (whether it exists or not). */ + path: string; +} + +export type ScanStage = "topology" | "objective" | "subjective"; + +export interface ScanStatus { + /** Absolute path to the scan directory. */ + dir: string; + topology: ScanStageReport; + objective: ScanStageReport; + subjective: ScanStageReport; + /** + * The next stage an orchestrator should run, or `null` if every stage + * is `present`. Stages run in order: topology → objective → subjective. + * The recommendation surfaces the first stage in `missing` state. + */ + recommended_next: ScanStage | null; +} + +/** + * Inspect a scan directory and report which stages have produced artifacts. + * + * Existence-only check today. The artifacts checked are: + * + * - topology → `map.md` + * - objective → `bucket.json` + * - subjective → `expression.md` + * + * Hash-keyed freshness (`.scan-meta.json` with input/output hashes per + * stage) is the planned enhancement. For v1, orchestrators that want + * "force rerun" behavior delete the artifact themselves before calling + * scan-status — same idiom design-world-model already uses. + */ +export async function scanStatus(dirPath: string): Promise { + const dir = resolve(dirPath); + const mapPath = resolve(dir, MAP_FILENAME); + const bucketPath = resolve(dir, BUCKET_FILENAME); + const expressionPath = resolve(dir, EXPRESSION_FILENAME); + + const [topologyPresent, objectivePresent, subjectivePresent] = + await Promise.all([ + pathExists(mapPath), + pathExists(bucketPath), + pathExists(expressionPath), + ]); + + const topology: ScanStageReport = { + state: topologyPresent ? "present" : "missing", + path: mapPath, + }; + const objective: ScanStageReport = { + state: objectivePresent ? "present" : "missing", + path: bucketPath, + }; + const subjective: ScanStageReport = { + state: subjectivePresent ? "present" : "missing", + path: expressionPath, + }; + + let recommended_next: ScanStage | null = null; + if (topology.state === "missing") recommended_next = "topology"; + else if (objective.state === "missing") recommended_next = "objective"; + else if (subjective.state === "missing") recommended_next = "subjective"; + + return { dir, topology, objective, subjective, recommended_next }; +} + +async function pathExists(path: string): Promise { + try { + const s = await stat(path); + return s.isFile() && s.size > 0; + } catch { + return false; + } +} diff --git a/packages/ghost-expression/src/core/schema.ts b/packages/ghost-expression/src/core/schema.ts index fe4d293..f177c59 100644 --- a/packages/ghost-expression/src/core/schema.ts +++ b/packages/ghost-expression/src/core/schema.ts @@ -49,8 +49,8 @@ const SurfacesSchema = z.object({ }); /** - * Frontmatter observation: short machine-tags only. The Character paragraph - * (summary) and Signature bullets (distinctiveTraits) live in the body. + * Frontmatter observation: short machine-tags only. The Character + * paragraph (summary) lives in the body. */ const DesignObservationSchema = z .object({ @@ -60,72 +60,59 @@ const DesignObservationSchema = z .strict(); /** - * Frontmatter decision: dimension slug + optional embedding only. - * Both the prose rationale AND the evidence bullets live in the body - * under `### dimension` → `**Evidence:**`. Evidence in frontmatter is - * rejected by the strict schema. + * Frontmatter decision: dimension slug + optional kind + optional + * embedding only. Both the prose rationale AND the evidence bullets live + * in the body under `### dimension` → `**Evidence:**`. Evidence in + * frontmatter is rejected by the strict schema. + * + * `dimension_kind` is the optional canonical-vocabulary mapping used by + * fleet aggregation. See `CANONICAL_DECISION_DIMENSIONS` in `@ghost/core` + * and the soft `non-canonical-dimension` lint rule for guidance. */ const DesignDecisionSchema = z .object({ dimension: z.string(), + dimension_kind: z.string().optional(), embedding: z.array(z.number()).optional(), }) .strict(); /** - * Semantic slot → token binding. Each role names a slot ("h1", "card", - * "button") and binds tokens from the expression dimensions. Every - * sub-block is optional — a role can be partial when the source only - * supplies some tokens. + * v0 reviewer rule: a grep-able pattern fitted to this expression's + * design language. Severity, match shape, and tolerance are typically + * computed at emit time from the perceptual prior in `@ghost/core`; + * explicit fields here are overrides. + * + * Rules coexist with `decisions[]` during the v0 transition. Both are + * optional. Lint validation of the perceptual prior (e.g. warning on + * unrealistic tolerances) is not yet wired — the schema permits the + * shape so authors can begin populating rules without lint rejection. */ -const DesignRoleSchema = z +const RuleSchema = z .object({ - name: z.string(), - tokens: z - .object({ - typography: z - .object({ - family: z.string().optional(), - size: z.number().optional(), - weight: z.number().optional(), - lineHeight: z.number().optional(), - }) - .strict() - .optional(), - spacing: z - .object({ - padding: z.number().optional(), - gap: z.number().optional(), - margin: z.number().optional(), - }) - .strict() - .optional(), - surfaces: z - .object({ - borderRadius: z.number().optional(), - shadow: z.enum(["none", "subtle", "layered"]).optional(), - borderWidth: z.number().optional(), - }) - .strict() - .optional(), - /** - * Palette slot bindings. Open-ended record — keys are slot names - * the consumer chooses, values are either raw hex literals - * (`"#1a1a1a"`) or `{palette.dominant.X}` / `{palette.semantic.X}` - * references that resolve through the local palette, or opaque - * external token refs (`{base.color.brand.x}`) for repos that - * pull tokens from a Style-Dictionary-style pipeline. - * - * Conventional keys (the recipe should reach for these first): - * `background`, `foreground`, `surface`, `border`, `accent`, - * `muted`, `link`. Phase 5b widened this from a fixed three-key - * shape to an open record so richer real-world vocabularies - * (separator, ring, popover, …) don't hard-error. - */ - palette: z.record(z.string(), z.string()).optional(), - }) - .strict(), - evidence: z.array(z.string()), + id: z.string(), + canonical: z.string().optional(), + kind: z + .enum([ + "color", + "radius", + "spacing", + "type-size", + "type-family", + "type-weight", + "shadow", + "motion", + ]) + .optional(), + summary: z.string().optional(), + pattern: z.string(), + enforce_at: z.array(z.string()).optional(), + severity: z.enum(["critical", "serious", "nit"]).optional(), + match: z.enum(["exact", "band", "percent", "structural"]).optional(), + tolerance: z.number().optional(), + presence_floor: z.number().int().nonnegative().optional(), + support: z.number().min(0).max(1).optional(), + rationale: z.string().optional(), }) .strict(); @@ -133,7 +120,7 @@ const DesignRoleSchema = z * Schema for the YAML frontmatter in an expression.md file. Covers the * machine-layer of Expression plus expression-level metadata. * - * Note: narrative prose fields (observation.summary, distinctiveTraits, + * Note: narrative prose fields (observation.summary, * decisions[].decision) are NOT allowed here — they belong in the body. * `.strict()` on nested schemas enforces this. * @@ -166,6 +153,8 @@ export const FrontmatterSchema = z // expression — narrative tags (optional; prose lives in body) observation: DesignObservationSchema.optional(), decisions: z.array(DesignDecisionSchema).optional(), + /** v0 reviewer rules — optional during the transition. */ + rules: z.array(RuleSchema).optional(), // expression — structured (required) palette: PaletteSchema, @@ -173,13 +162,6 @@ export const FrontmatterSchema = z typography: TypographySchema, surfaces: SurfacesSchema, - /** - * Semantic slot → token bindings. Optional. The bridge from abstract - * tokens to rendering: each role names a slot and binds tokens from - * the dimensions above. - */ - roles: z.array(DesignRoleSchema).optional(), - /** * Optional at root — loader falls back to sibling `embedding.md` or * recomputes from structured blocks. Present embeddings are trusted @@ -211,12 +193,12 @@ export const PartialFrontmatterSchema = z observation: DesignObservationSchema.optional(), decisions: z.array(DesignDecisionSchema).optional(), + rules: z.array(RuleSchema).optional(), palette: PaletteSchema.optional(), spacing: SpacingSchema.optional(), typography: TypographySchema.optional(), surfaces: SurfacesSchema.optional(), - roles: z.array(DesignRoleSchema).optional(), embedding: z.array(z.number()).optional(), }) .strict(); diff --git a/packages/ghost-expression/src/core/writer.ts b/packages/ghost-expression/src/core/writer.ts index 36a4844..32b2f7e 100644 --- a/packages/ghost-expression/src/core/writer.ts +++ b/packages/ghost-expression/src/core/writer.ts @@ -26,7 +26,7 @@ export interface SerializeOptions { * Contract: frontmatter and body own disjoint fields. * • Frontmatter carries the machine-layer (id, tokens, dimension slugs, * evidence, personality/resembles tags, embedding). - * • Body carries prose (# Character, # Signature, # Decisions rationale). + * • Body carries prose (# Character, # Decisions rationale). * * Each field has exactly one home — so there is no precedence rule and no * way for the two sides to drift. @@ -69,12 +69,6 @@ function buildBody( if (observation?.summary?.trim()) { parts.push(`# Character\n\n${observation.summary.trim()}`); } - if (observation?.distinctiveTraits?.length) { - const bullets = observation.distinctiveTraits - .map((t) => `- ${t}`) - .join("\n"); - parts.push(`# Signature\n\n${bullets}`); - } if (decisions?.length) { const blocks = decisions .filter((d) => d.decision?.trim()) diff --git a/packages/ghost-expression/src/skill-bundle/SKILL.md b/packages/ghost-expression/src/skill-bundle/SKILL.md index a6e40f9..a5d09fd 100644 --- a/packages/ghost-expression/src/skill-bundle/SKILL.md +++ b/packages/ghost-expression/src/skill-bundle/SKILL.md @@ -9,28 +9,45 @@ metadata: # Ghost Expression — Authoring the Canonical Artifact -This skill helps you author the project's design language — its `expression.md` (YAML frontmatter + three-layer Markdown: Character → Signature → Decisions). You profile a project to write one, then validate, describe, diff, and emit derived artifacts. The **change** half (compare two expressions for drift, acknowledge it, track another expression as your reference) lives in the sibling `ghost-drift` skill. +This skill helps you author the project's design language — its `expression.md` (YAML frontmatter + Markdown body: Character → Decisions). You profile a project to write one, then validate, describe, diff, and emit derived artifacts. The **change** half (compare two expressions for drift, acknowledge it, track another expression as your reference) lives in the sibling `ghost-drift` skill. You do the synthesis (the profile recipe). The `ghost-expression` CLI is the calculator you reach for when you need a reproducible answer: parsing, schema validation, layout, structural diff. Call it freely; the output is ground truth. +**Two install paths, same recipes.** When the user installed via `curl … | sh` (the no-CLI v0 path) the `ghost-expression` binary is *not* on PATH. The recipes degrade gracefully: every CLI-using step has a prose fallback you can execute via `Read` / `Glob` / `Bash` / `Grep`. Detect availability once, at the start of a workflow: + +```sh +command -v ghost-expression >/dev/null && echo "cli" || echo "prose" +``` + +When the CLI is present, prefer it — the output is deterministic and idempotent. When it isn't, follow the fallback recipes. Don't ask the user to install the CLI mid-workflow; the prose path is real, not a degraded mode. + ## CLI verbs | Verb | Purpose | |---|---| -| `ghost-expression lint [expression.md]` | Validate schema + body/frontmatter coherence. Use this before declaring an expression valid. | +| `ghost-expression lint [file]` | Validate `expression.md`, `map.md`, or `bucket.json` (auto-detects by `.json` extension, `schema: ghost.map/v1` frontmatter, or filename). Use before declaring an artifact valid. | +| `ghost-expression inventory [path]` | Emit deterministic raw repo signals (manifests, language histogram, candidate config files, registry presence, top-level tree, git remote) as JSON. Feeds the topology recipe. | +| `ghost-expression scan-status [dir]` | Report which scan stages have produced artifacts (`map.md`, `bucket.json`, `expression.md`) and which stage to run next. Use to decide what to do at the start of a scan or between stages. | | `ghost-expression describe [expression.md]` | Print a section map (line ranges + token estimates) so you can selectively read only the sections you need instead of loading the whole file. Use before review/generate when the expression is large. | -| `ghost-expression diff ` | Structural prose-level diff — what decisions, palette roles, and tokens changed. **Not the same as `ghost-drift compare`** (which returns embedding distance). Use diff when you want to read what changed; use compare when you want a number. | +| `ghost-expression diff ` | Structural prose-level diff between two expressions — what decisions, palette roles, and tokens changed. **Not the same as `ghost-drift compare`** (which returns embedding distance). Use diff when you want to read what changed; use compare when you want a number. | +| `ghost-expression bucket [...buckets]` | Operate on `ghost.bucket/v1` files. `merge` — concat with id-based dedup, deterministic and idempotent (useful for modular rollups and fleet cohort views). `fix-ids` — recompute every row's `id` from content (use after authoring rows with empty `id` fields). | | `ghost-expression emit ` | Derive per-project artifacts from `expression.md`. Kinds: `review-command` (Rams-style slash command), `context-bundle` (multi-file generation prompt), `skill` (this agentskills.io bundle). | -Four verbs. If you find yourself reaching for `ghost-expression profile` — that is *your* workflow, not a CLI command. Follow [references/profile.md](references/profile.md). +If you find yourself reaching for `ghost-expression scan` / `ghost-expression survey` / `ghost-expression profile` — those are *your* workflows, not CLI commands. Follow the recipes below. ## Workflows (your job, not the CLI's) +A full scan of a target produces three artifacts in sequence: `map.md` (topology) → `bucket.json` (objective values) → `expression.md` (subjective interpretation). Each stage feeds the next; each stage is its own recipe. + When the user asks you to: -- "Profile my design language" / "write expression.md" → [references/profile.md](references/profile.md) -- "Diff these two expressions" / "what changed between these expressions" → run `ghost-expression diff `. For embedding distance use `ghost-drift compare`. -- "Lint my expression" → run `ghost-expression lint`. Fix anything it reports. +- "Scan my project" / "do a full scan" / "go end-to-end" → [references/scan.md](references/scan.md). The meta-recipe — orchestrates topology → survey → profile. Use when the user wants the full pipeline, not a specific stage. +- "Map my repo" / "where does the design system live" / "write map.md" → [references/map.md](references/map.md). Pre-req: none. Output: validated `map.md`. +- "Survey my design language" / "scan values" / "extract design tokens" → [references/survey.md](references/survey.md). Pre-req: `map.md` exists. Output: validated `bucket.json`. +- "Profile my design language" / "write expression.md" / "interpret these values" → [references/profile.md](references/profile.md). Pre-req: `map.md` AND `bucket.json` exist (run topology + survey first). Output: validated `expression.md`. +- "Diff these two expressions" → run `ghost-expression diff `. For embedding distance use `ghost-drift compare`. +- "Lint my expression" / "lint my bucket" → run `ghost-expression lint `. Fix anything it reports. +- "Merge these buckets" / "compose a cohort bucket" → run `ghost-expression bucket merge `. For drift detection (compare under change, ack/track/diverge, review PR diffs against an expression) install the `ghost-drift` skill. @@ -38,8 +55,8 @@ For drift detection (compare under change, ack/track/diverge, review PR diffs ag An `expression.md` has: -- **YAML frontmatter (machine layer):** `id`, `source`, `timestamp`, `observation.personality`, `observation.resembles`, `decisions[].dimension`/`.evidence`, `palette`, `spacing`, `typography`, `surfaces`, `roles`. -- **Markdown body (prose layer):** `# Character` (`observation.summary`), `# Signature` (bullets from `distinctiveTraits`), `# Decisions` with `### ` rationale blocks. +- **YAML frontmatter (machine layer):** `id`, `source`, `timestamp`, `observation.personality`, `observation.resembles`, `decisions[].dimension`, `rules[]`, `palette`, `spacing`, `typography`, `surfaces`. +- **Markdown body (prose layer):** `# Character` (`observation.summary`), `# Decisions` with `### ` rationale blocks ending in `**Evidence:**` bullets. Each field lives in exactly one layer — no duplication. Putting prose in frontmatter is a lint error. Full spec: [references/schema.md](references/schema.md). Starting template: [assets/expression.template.md](assets/expression.template.md). diff --git a/packages/ghost-expression/src/skill-bundle/assets/expression.template.md b/packages/ghost-expression/src/skill-bundle/assets/expression.template.md index e60ab65..2f728f8 100644 --- a/packages/ghost-expression/src/skill-bundle/assets/expression.template.md +++ b/packages/ghost-expression/src/skill-bundle/assets/expression.template.md @@ -47,19 +47,12 @@ surfaces: borderRadii: [4, 8] shadowComplexity: deliberate-none borderUsage: minimal - -roles: [] --- # Character 2-4 sentences on the personality of this design language. This prose becomes `observation.summary` when parsed. -# Signature - -- Distinctive trait 1. -- Distinctive trait 2. - # Decisions ### color-strategy diff --git a/packages/ghost-expression/src/skill-bundle/references/map.md b/packages/ghost-expression/src/skill-bundle/references/map.md new file mode 100644 index 0000000..558d3ae --- /dev/null +++ b/packages/ghost-expression/src/skill-bundle/references/map.md @@ -0,0 +1,113 @@ +--- +name: map +description: Author the map.md for a target — Ghost's topology card. The first stage of a scan. +handoffs: + - label: Survey values into bucket.json + command: (next stage — survey recipe) + prompt: Survey the target's design values into bucket.json + - label: Validate the map + command: ghost-expression lint map.md + prompt: Lint the map.md I just wrote +--- + +# Recipe: Author a target's map.md + +**Goal:** produce a valid `map.md` (`ghost.map/v1`) that captures the *topology* of the target — what platform it ships on, what it builds with, where the design system lives, what feature areas matter for sampling. `map.md` is the first stage of a scan: every later stage (`survey.md` → `bucket.json`, `profile.md` → `expression.md`) reads it to skip rediscovery. + +This recipe is *your* job. Ghost's CLI provides `ghost-expression inventory` (deterministic raw signals) and `ghost-expression lint ` (validation), but you do the synthesis. + +## Steps + +### 1. Gather raw signals + +**Preferred (CLI present):** + +Run `ghost-expression inventory [path]` from (or pointed at) the target root. It returns deterministic JSON: package manifests, language histogram, candidate config files, registry presence, top-level tree, git remote, plus best-effort platform and build-system hints. Read it as the foundation — reproducible from inputs. + +**Prose fallback (no CLI):** + +Build the inventory yourself with `Glob` / `Read` / `Bash`: + +- **Package manifests:** `Glob: **/{package.json,pnpm-workspace.yaml,Cargo.toml,go.mod,Package.swift,build.gradle*,pyproject.toml,requirements.txt}` — exclude `node_modules`. Read each; record `name` and top-level dep names. +- **Language histogram:** `Bash: find . -type f -name '*.tsx' -o -name '*.ts' -o -name '*.swift' -o -name '*.kt' …` (extension list per the kinds you care about) and count. Convert to `{name, files, share}` rows where `share = files / total`. +- **Candidate config files:** `Glob` for `tailwind.config.*`, `tsconfig*.json`, `vite.config.*`, `next.config.*`, `tokens/**`, `theme/**`, etc. +- **Registry presence:** check `Read: /registry.json` — if it parses and has `name` + `items[]`, record its path. +- **Top-level tree:** `Bash: ls -la ` for one level deep. +- **Git remote:** `Bash: git -C remote get-url origin` (best-effort, fine if absent). + +Format as a JSON object so the rest of the recipe can quote from it. Skip fields you can't determine; partial inventory is fine. + +### 2. Resolve the schema fields + +The `ghost.map/v1` frontmatter requires: + +- **`schema: ghost.map/v1`** (literal) +- **`id`** — slug (lowercase alphanumeric plus `.` `_` `-`, leading alphanumeric). For fleet scans, this is the fleet target id. +- **`repo`** — GitHub `org/repo`, or any source identifier that uniquely names this target. +- **`mapped_at`** — current ISO date (`YYYY-MM-DD`) or full datetime. +- **`platform`** — one of `web`, `ios`, `android`, `desktop`, `flutter`, `mixed`, `other`, or an array spanning multiple. The inventory's `platform_hints` is your starting point — accept it when consistent, override when you have evidence. +- **`languages`** — array of `{name, files, share}` from the inventory histogram. `share` is fraction in [0,1]. +- **`build_system`** — one of `gradle`, `bazel`, `xcode`, `pnpm`, `npm`, `yarn`, `cargo`, `go`, `maven`, `sbt`, `cmake`, `style-dictionary`, `vite`, `webpack`, `parcel`, `rollup`, `turbopack`, `esbuild`, `nx`, `turbo`, `mixed`, `other`, or an array. The inventory's `build_system_hints` plus lockfile presence (`pnpm-lock.yaml` → `pnpm`, `yarn.lock` → `yarn`, `package-lock.json` → `npm`) usually answers this. +- **`package_manifests`** — array of paths from the inventory. +- **`composition.frameworks`** — array of `{name, version?}` (e.g. `react`, `next`, `swiftui`, `compose`, `style-dictionary`). +- **`composition.rendering`** — short slug (`react-spa`, `next-app-router`, `swiftui`, `compose`, `static`, `mixed`, …). +- **`composition.styling`** — array (e.g. `["tailwindcss"]`, `["scss-modules"]`, `["styled-components"]`). +- **`composition.navigation`** — optional short slug (`next-router`, `react-router`, `swiftui-navigation`, …). +- **`registry`** — optional `{path, components}` if a shadcn-style registry exists. +- **`design_system`** — `{paths[], entry_files?, derived_files?, path_patterns?, token_source?, upstream?, status}`. `token_source` is `inline` / `external` / `mixed`. `status` is `active` / `mixed` / `unclear`. Set `upstream` when `token_source` is `external` or `mixed`. +- **`ui_surface`** — `{include[], exclude[]}` — globs for sampling scope. +- **`feature_areas`** — array of `{name, paths[], sub_areas?[]}` describing the surfaces worth sampling. 3–8 areas is typical; fewer is fine for small repos. +- **`orientation_files`** — array of files an agent should read first to understand the target. + +### 3. Use a manifest if one is provided + +If a `manifest.yaml` is present in CWD (some fleet orchestrators inject hand-curated sampling manifests for big repos), treat it as authoritative for `feature_areas`, `module_signals`, and `design_system.path_patterns`. Don't contradict it without evidence. + +If no manifest is provided, derive `feature_areas` from the inventory's `top_level_tree` and your own brief exploration: which directories represent product surfaces (e.g. `apps/dashboard`, `packages/ui`, `src/features/*`)? + +### 4. Body sections + +`map.md` requires a short prose body with three sections — keep them tight, two-to-four sentences each. The body is interpretation; the frontmatter is ground truth. Sections must appear in this order: + +- `## Identity` — what is this repo, what does it produce, who consumes it? +- `## Topology` — how is the codebase organized? Where does the design system live relative to product code? +- `## Conventions` — notable patterns (token pipelines, registry, framework choices, language mixes) that shape how someone navigates. + +### 5. Validate + +**Preferred (CLI present):** + + ghost-expression lint map.md + +Fix any errors. Lint passing is the success gate — do not declare done until it exits 0. + +**Prose fallback (no CLI):** + +Walk the file yourself against the schema in [schema.md](schema.md). Required checks: + +- Frontmatter parses as valid YAML. +- `schema: ghost.map/v1` literal present. +- Required fields populated: `id` (slug), `repo`, `mapped_at`, `platform`, `languages`, `build_system`, `package_manifests`, `composition.frameworks`, `composition.rendering`, `composition.styling`, `design_system.paths`, `design_system.status`, `ui_surface.include`, `feature_areas[]`, `orientation_files[]`. +- `id` matches `^[a-z0-9][a-z0-9._-]*$`. +- Body sections appear in order: `## Identity`, `## Topology`, `## Conventions`. No other `##` headings between them. +- If `design_system.token_source` is `external` or `mixed`, `design_system.upstream` is set. + +Common errors regardless of path: + +- Body section out of order (`## Identity` must precede `## Topology` etc.) +- Missing `entry_files` AND `derived_files` under `design_system` (warning — fine if neither exists, but check) +- `token_source: external` without `upstream` set +- `id` not a slug + +## Always + +- Cite real paths the inventory returned. Do not invent files. +- Prefer the array form (`platform: [web, ios]`) over `mixed` when the repo genuinely spans multiple platforms. +- If there is no design system in the repo (a backend-only app, a marketing site without a tokens layer), say so in `## Identity`, set `design_system.status: unclear`, and omit `entry_files`. Don't fabricate a design-system structure. +- For fleet scans, resolve `id` and `repo` from environment variables when the orchestrator passes them (`TARGET_ID`, `TARGET_REPO`). + +## Never + +- Never put prose into frontmatter or structural data into the body — the partition is load-bearing. +- Never duplicate the inventory's content in the body. The body is interpretation, not data. +- Never declare done before `ghost-expression lint map.md` exits 0. diff --git a/packages/ghost-expression/src/skill-bundle/references/profile.md b/packages/ghost-expression/src/skill-bundle/references/profile.md index a46fff5..8f7edb6 100644 --- a/packages/ghost-expression/src/skill-bundle/references/profile.md +++ b/packages/ghost-expression/src/skill-bundle/references/profile.md @@ -1,6 +1,6 @@ --- name: profile -description: Write expression.md from a project's design sources. +description: Interpret a bucket.json into expression.md — the subjective synthesis stage of a scan. handoffs: - label: Compare against another expression command: ghost-drift compare @@ -12,99 +12,216 @@ handoffs: # Recipe: Profile a project into expression.md -**Goal:** produce a valid `expression.md` that captures the project's visual language. Ghost's CLI does not call an LLM for this — you, the host agent, explore the repo and synthesize the result, then hand it to `ghost-expression lint` for validation. +**Goal:** produce a valid `expression.md` that captures the project's design language as an interpretation. **You are the interpreter, not the surveyor.** Read the `bucket.json` as ground truth for what values the project actually ships; write decisions, form the prose body, and fill the structured token blocks. Do not re-extract values from source — that's the surveyor's job and you'd be doing it twice. + +`expression.md` is the terminal artifact in a three-stage scan: topology (`map.md`) → objective (`bucket.json`) → subjective (`expression.md`). Yours is the third stage. + +## Pre-requisites + +Two artifacts must exist before you start: + +- `map.md` — `ghost.map/v1`. Read its frontmatter for repo kind signals (`composition.frameworks`, `composition.styling`, `design_system.token_source`, `platform`, `registry`). Read its body for context on identity / topology / conventions. +- `bucket.json` — `ghost.bucket/v1`. Lint-clean. Carries every concrete value, token, and component the surveyor observed, with occurrence counts and (for tokens) alias chains. + +If either is missing, **stop**. Run topology and survey first. Inventing an expression from incomplete inputs poisons every downstream comparison. + +## How to read the bucket + +A `bucket.json` has three sections: + +- **`values[]`** — concrete literals shipped in source. Group by `kind`: `color` rows feed `palette`; `spacing` rows feed `spacing.scale` / `spacing.baseUnit`; `typography` rows feed `typography.*`; `radius` rows feed `surfaces.borderRadii`; `shadow` rows feed `surfaces.shadowComplexity` (count + complexity, not literal shadows); `breakpoint` / `motion` / `layout-primitive` rows feed Decisions where they're load-bearing. Each row has `occurrences` (total count) and `files_count` (spread). Higher numbers = stronger signal. +- **`tokens[]`** — named declarations with `alias_chain` (path through indirection) and `resolved_value`. Long chains and semantic naming (`--color-brand-primary` → `--color-orange-500`) are evidence of a deliberate token layer. Empty chains everywhere = inline literals = no token discipline. +- **`components[]`** — known components (registry entries or heuristically discovered). Contributes count signal to surface-vocabulary decisions and grounds prose about what the system ships. + +External libraries (icon sets, primitive collections, motion libs) deliberately don't have a bucket section — whether a system uses Radix or hand-rolls primitives doesn't change what its design language *is*. When a library is load-bearing for the design language (icon family choice, font sourcing), cite it as prose evidence under the relevant decision dimension; don't expect it as structured data. + +Read `bucket.json` once, fully. Then keep it open while you write. ## Steps -### 1. Locate design sources +### 1. Detect repo kind from map.md + +Branch the rest of the recipe on signals from `map.md`. Apply rules in order; first match wins: + +1. `design_system.token_source: external` → **consumer mode**. The repo imports tokens from another package; the bucket's `tokens[]` is mostly empty or full of upstream slugs. Don't try to interpret upstream values you didn't observe. +2. `composition.frameworks` includes `style-dictionary`, or there's a `tokens/` directory and no `registry`, and the bucket has long alias chains (3+ steps) → **token-pipeline mode**. Components are graph nodes; layering is a first-class decision. +3. `registry.path` set, or `composition.styling` includes `tailwindcss*` / `css-modules` / similar → **ui-library mode** (default). +4. `platform` is an array spanning native + web with no single dominant build → **multi-platform**: profile as ui-library but expect coarser groupings; the bucket likely has fewer rows per dialect. + +Note the chosen mode in your scratchpad — it shapes Steps 3, 4, and 5. + +### 2. Layer 1 — Observation (holistic prose) + +Subjective. 2–4 sentences capturing what this design language is and how it feels. Read the bucket *and* sample 3–5 high-occurrence files to actually see the surfaces — counts alone don't tell you the visual register. The prose lives under `# Character` in the body. + +Then in frontmatter: + +- `personality`: 3–6 adjectives (`utilitarian`, `editorial`, `dense`, `playful`, `restrained`, …) +- `resembles`: 1–3 well-known references (Linear, Geist, Material 3, …) — only if genuinely close + +Notable absences ("no decorative elements at all", "no shadows anywhere despite a dark theme") are *not* prose to write here — they're rules with `presence_floor: 0` (or a small integer) authored in Step 3, which causes any addition to escalate severity. Codifying absences as enforceable rules beats restating them in prose. -If a `map.md` is present at the repo root, **use it**. Read its frontmatter: +### 3. Layer 2 — Rules (curated, grep-friendly, perceptual-prior-aware) -- `design_system.entry_files` — the canonical token sources. Resolve every variable chain in these files end-to-end. -- `design_system.paths` — the directories the design language lives in (one repo may have several). -- `design_system.token_source` — `inline` / `external` / `mixed`. Drives Step 1.5. -- `registry`, `composition.frameworks`, `composition.styling`, `platform` — also feed Step 1.5. -- `feature_areas[].sub_areas[]` — the surfaces or layers worth sampling. +This is the load-bearing step. **Your job is to propose 5–15 candidate rules, score each by bucket-derived support, and present the ranked list to the human curator.** The curator promotes the sharpest 5–10 to `rules[]`. You do not author final rules unilaterally — design taste is human-curated, agent-proposed. -Map.md eliminates location-discovery on monorepos. If `map.md` is **missing**, prompt the user to run `ghost-map inventory` first. Fallback inline discovery: `tailwind.config.{js,ts}`, `@theme {}` blocks, `styles/globals.css`, `tokens/` dirs, SCSS variable files, TypeScript theme objects, shadcn `:root { --… }`, JSON token files. Use Glob/Grep; read real files. +(Legacy: this stage previously authored `decisions[]` — abstract per-dimension prose. That format is preserved during the v0 transition for backward compatibility, but the canonical authoring surface is now `rules[]`. The emitter prefers `rules[]` when present.) -### 1.5. Detect repo kind +#### 3a. Propose candidate rules -Branch the rest of the recipe on signals from map.md. Apply rules in order; first match wins: +Walk the bucket and pose: *what pattern is this project consistently following that deserves codification?* Each candidate rule has the shape: -1. `design_system.token_source: external` → **consumer mode**. The repo imports tokens from another package; it doesn't declare them. -2. `composition.frameworks` includes `style-dictionary` (or there is a `tokens/` directory and no `registry`) → **token-pipeline mode**. Components are YAML graph nodes, not tsx. -3. `registry.path` set, or `composition.styling` includes `tailwindcss*` / `css-modules` / similar with component files in `ui_surface.include` → **ui-library mode** (default). -4. `platform` is an array spanning native + web with no single dominant build → **multi-platform**: profile as ui-library but expect coarser `feature_areas` and prefer modules named `*UI` / `*View` / `*Screen`; skip `*Fakes` / `*Mocks` / `*Tests`. +```yaml +- id: # stable, slug-style + canonical: # optional but strongly preferred (see vocabulary below) + kind: # optional; drives default match shape + summary: # what the rule says in plain English + rationale: # why the rule exists; cites the bucket + pattern: # what the reviewer greps for + enforce_at: [...] # className / css_var / inline_style / import + support: 0.0–1.0 # computed: bucket conformers / total observed + presence_floor: # optional; default 0 +``` -If signals overlap, **token-pipeline** wins over both **ui-library** (decide by `entry_files`: YAML/JSON graphs → token-pipeline; CSS/code → ui-library) and **multi-platform** (pipelines often span multiple platforms by definition; the pipeline branch already handles per-platform output sinks via `feature_areas`). Note the chosen mode in your scratchpad — Step 4 onward depends on it. +**Pick `canonical` from the controlled vocabulary first.** Twelve dimensions cover the orthogonal axes a designer makes deliberate calls on: -### 2. Resolve variable chains end-to-end +| Slug | Captures | Default tier | +|---|---|---| +| `color-strategy` | hue as decoration vs. communication; default-mono vs. branded | loud | +| `font-sourcing` | bundled vs. consumer-supplied; preferred families | loud | +| `surface-hierarchy` | named-by-intent vs. named-by-shade; surface vocabulary | structural | +| `shape-language` | radius philosophy (pill, uniform, geometric, organic) | structural | +| `typography-voice` | type-as-instrument; editorial vs. utility; scale rhythm | structural | +| `elevation` | shadow vocabulary; named-by-role vs. numeric; dark-mode treatment | structural | +| `interactive-patterns` | focus, hover, active feedback conventions | structural | +| `spatial-system` | spacing scale, base unit, padding philosophy | rhythmic | +| `density` | compact controls vs. spacious containers (paired with spatial, distinct) | rhythmic | +| `motion` | animation as functional vs. decorative; presence vs. absence | rhythmic | +| `theming-architecture` | runtime themability; cascade structure; override patterns | rhythmic | +| `token-architecture` | alias-chain depth; semantic vs. raw; layering discipline | rhythmic | -If a value is a reference, follow it: `--btn-bg: var(--color-primary)` → `--color-primary: var(--brand-500)` → `--brand-500: #0066cc`. Record the resolved concrete value. +The **default tier** is the perceptual weight: loud rules render as Critical, structural as Serious, rhythmic as Nit in the emitted reviewer. Severity is computed by the emitter from `canonical` + `presence_floor`; you don't usually set `severity` directly. -For **token-pipeline** repos, a chain crosses *layers*: component → semantic → base. Walk all three. Surface light/dark pairs together (the same component slot will reference one semantic name that resolves differently per mode). +#### 3b. Score support from the bucket -For **consumer** repos, chains often dead-end at an external import (`import theme from "@market/market-theme"`, `Color.Background.app`). Don't try to resolve to hex — record the upstream slug as the value. The expression describes *what shows up in product code*, not what the upstream declares. +For each candidate rule, compute support — *the fraction of observed cases that already conform*. Concretely: -### 3. Sample for the roles layer +- **`no-off-palette-hex`** (color-strategy) — `support = (bucket color rows with value in palette set) / (total bucket color rows)`. If 31 of 33 colors are in the palette, support is 0.94. +- **`pill-interactives`** (shape-language) — `support = (interactive components using rounded-full) / (interactive components observed)`. Walk `bucket.components` for Button/Input/Badge; check radii. +- **`spacing-on-scale`** (spatial-system) — `support = (spacing rows with value ∈ scale) / (total spacing rows)`. The scale lives in `expression.spacing.scale`. -Sampling depends on repo kind (Step 1.5): +Rule of thumb: **drop candidates with support < 0.85.** Below that threshold, the project hasn't actually committed to the pattern — codifying it generates noise. A `support: 0.6` rule looks aspirational, not enforced. -- **UI-library (default)** — open 6–10 component files: typography primitives, `Button`, `Card`, `Input`, list/table primitives, plus a couple from each `feature_areas[].sub_areas[]` cluster. Populate `roles[]` from imports and class bindings. `feature_areas` are component categories (input / display / feedback / navigation / layout) or AI-element clusters (chat / agent-state / artifacts) — match the registry's `categories` field if present. -- **Token-pipeline** — read 3–5 component-layer YAMLs end-to-end through the layer chain. `roles[]` is populated from semantic-layer mappings (which semantic alias each component slot references), not from imports. Skip product surfaces; `feature_areas` here are token-architecture layers (base / semantic / component) and/or platform output sinks (web / ios / android), 6–15 typical for a multi-layer pipeline. -- **Consumer mode** — sample 6–10 product UI files. Don't resolve tokens to hex; record which upstream slugs (`var(--core-radius-md)`, `Color.Background.app`) appear most often in product code. Where the consumer overrides upstream (custom `@font-face`, a local `theme.css` with primitive tokens), surface that as a Decision. `feature_areas` remain product surfaces — the consumer is an app. +#### 3c. Identify presence-floor candidates -Only record what you directly observed. Empty `roles` is fine. +The perceptual prior escalates rules one tier when the bucket count for the dimension is ≤ `presence_floor`. Use this to capture *negative space* — what the project deliberately *isn't*: -### 4. Form Layer 1 — Observation (holistic) +- Bucket has 0 motion rows → `no-decorative-motion` rule with `presence_floor: 4` (any addition crosses zero, escalates to critical). +- Bucket has 0 gradient values → `no-gradients` with `presence_floor: 0`. +- Bucket has 0 bundled fonts → `no-foreign-fonts` with `presence_floor: 0`. -Write subjectively. 2–4 sentences capturing what this design language is and how it feels. Then: +Don't set a presence floor when the dimension is well-populated — the escalation will never trigger and the field becomes noise. -- `personality`: 3–6 adjectives (`utilitarian`, `editorial`, `dense`, `playful`, …) -- `distinctiveTraits`: what makes this expression *visually recognizable* — include notable absences ("no decorative elements at all") -- `resembles`: 1–3 well-known references (Linear, Geist, Material 3, …) +#### 3d. Present the ranked list to the curator -### 5. Derive Layer 2 — Design Decisions (abstract) +Sort candidates by support, descending. Present each as: id + canonical + summary + support % + 1-line rationale. Mark presence-floor escalations explicitly. Recommend cuts: anything below 0.85, anything redundant with another rule, anything where the pattern is too fuzzy to enforce. -Name the pattern, not the token: +The curator picks 5–10. **Do not paste your full candidate list into `rules[]`.** Wait for the human to promote. -- ✗ Weak: "Spacing follows a 4px base grid with Tailwind defaults." (restates a fact visible in the tokens) -- ✓ Strong: "Prefer explicit component-height tokens over padding arithmetic, so button/input sizing is decoupled from surrounding layout." (names the pattern and its consequence) +#### Mode-specific framing -Surface whatever dimensions fit. Common ones: `color-strategy`, `spatial-system`, `typography-voice`, `surface-hierarchy`, `density`, `motion`, `elevation`, `interactive-patterns`. **Absences are decisions** — "No animation — interactions are immediate and non-kinetic" is valid. In **consumer** mode, override patterns are decisions ("App ships its own `@font-face` instead of inheriting upstream sans"); in **token-pipeline** mode, layering choices are decisions ("Component layer never references base tokens directly — always via semantic"). +- **Consumer** — overrides are rules. App-side `@font-face` that differs from upstream → a `font-sourcing` rule with `presence_floor: 0` against the upstream's font set. +- **Token-pipeline** — layering posture is a rule. "Component layer never references base tokens directly" → a `token-architecture` rule whose pattern catches `--component-* references --base-*`. +- **Ui-library** — registry shape is a rule. "Components have no theme variants" → an `interactive-patterns` rule against `data-theme=` attributes. +- **Multi-platform** — divergence is rules. "iOS reuses system colors but web doesn't" → two color-strategy rules, one per dialect, each with its own `enforce_at`. -For each: `dimension` (slug), `decision` (prose, body), `evidence` (concrete citations — token definitions like `"--radius-pill: 999px"` preferred; `file:line` for behavioral observations). +#### Absences are rules — codify them with `presence_floor` -**Evidence belongs in the body markdown under `**Evidence:**` bullets per dimension. Do NOT put `evidence:` arrays in frontmatter — the schema is `.strict()` and will reject.** Each `### ` body block should end with a `**Evidence:**` line followed by bullet citations; the parser pulls those back onto `decisions[].evidence` in memory. +Don't try to express "this project has no animation" as prose. Express it as: a motion rule whose `presence_floor` causes any addition to escalate to critical. The emitted reviewer will catch the addition without the prose. -### 6. Extract Layer 3 — Concrete tokens +### 4. Layer 3 — Concrete tokens (read from bucket; do not invent) -Populate the structured fields: `palette.dominant`, `palette.neutrals`, `palette.semantic`, `palette.saturationProfile`, `palette.contrast`, `spacing.scale`, `spacing.regularity`, `spacing.baseUnit`, `typography.families`, `typography.sizeRamp`, `typography.weightDistribution`, `typography.lineHeightPattern`, `surfaces.borderRadii`, `surfaces.shadowComplexity`, `surfaces.borderUsage`. +Populate the structured frontmatter fields **from bucket rows**: -- Convert rem/em to px (1rem = 16px). Output colors as hex (`#1a1a1a`); the CLI computes oklch automatically. -- Every `palette` entry must be cited in at least one decision's `evidence`, or dropped. Uncited neutrals are noise. -- In **consumer** mode, fields you can only see as upstream slugs may be left empty rather than fabricated. A missing field beats an invented one. +- `palette.dominant` — top color rows by `occurrences`, with role assigned. Use bucket `role_hypothesis` when present and you agree; override when you don't. Cite the role. +- `palette.neutrals` — neutral-saturation color rows (low chroma — check the `spec.hsl.s` if present, or judge from the hex). `count` is the row count, `steps` is the literal hex array. +- `palette.semantic` — color rows whose `role_hypothesis` or naming suggests success/warning/error/info. Empty array if none. +- `palette.saturationProfile` — `muted` / `vibrant` / `mixed`. Judge from the chroma distribution of dominant colors. +- `palette.contrast` — `low` / `medium` / `high`. Judge from neutrals' lightness range. +- `spacing.scale` — sorted distinct scalar values from `kind: spacing` rows. Convert rem/em to px (1rem = 16px) before recording. +- `spacing.baseUnit` — the GCD of scale entries, or the smallest scalar that divides most others. +- `spacing.regularity` — 1.0 if the scale is a clean modular sequence (4, 8, 16, 24, …), lower as it diverges. +- `typography.families` — distinct `family` values from `kind: typography` rows. +- `typography.sizeRamp` — distinct font sizes (in px) from `kind: typography` rows. +- `typography.weightDistribution` — map of weight → relative frequency from `kind: typography` rows. +- `typography.lineHeightPattern` — `tight` / `normal` / `loose` / `mixed`, judged from `line_height` values. +- `surfaces.borderRadii` — distinct scalars from `kind: radius` rows. +- `surfaces.shadowComplexity` — `deliberate-none` (zero shadow rows + you confirmed it's intentional), `simple` (1–2 distinct shadow specs), `layered` (3+), `expressive` (varied + non-default). +- `surfaces.borderUsage` — `none` / `minimal` / `prominent`, judged from how often borders appear in samples (the bucket may not surface this directly — read 2–3 component files if in doubt). -### 7. Write the file +**Hard rule:** every value you put in `palette` / `spacing` / `typography` / `surfaces` must trace to a row in `bucket.json`. If it isn't in the bucket, it isn't in the expression. A missing field beats a fabricated one. If the bucket is sparse for a dimension (e.g. only one shadow), reflect that — `shadowComplexity: simple` with one shadow is honest; making up a layered system is a lie. + +**Hard rule:** every `palette` entry must be cited in at least one decision's `evidence`, or dropped. Uncited tokens are noise. + +### 5. Write the file Copy [../assets/expression.template.md](../assets/expression.template.md). Fill in: -- **Frontmatter:** all structured fields (identity, `observation.personality`/`.resembles`, `decisions[].dimension`/`.evidence`, `palette`, `spacing`, `typography`, `surfaces`, `roles`). -- **Body:** `# Character` (observation summary), `# Signature` (distinctiveTraits bullets), `# Decisions` (one `### ` block per decision). +- **Frontmatter:** all structured fields (identity, `observation.personality`/`.resembles`, `decisions[].dimension`, `palette`, `spacing`, `typography`, `surfaces`). +- **Body:** `# Character` (observation summary), `# Decisions` (one `### ` block per decision, each ending with `**Evidence:**` bullets citing bucket rows). Partition matters. See [schema.md](schema.md) for which field lives where. -### 8. Validate +### 6. Validate + +**Preferred (CLI present):** ghost-expression lint expression.md -Fix any errors. Common ones: prose in frontmatter → move to body; `### dim` with no matching `decisions[]` entry → remove the orphan; palette entry not cited in any evidence → cite it or drop it. +Fix any errors. + +**Prose fallback (no CLI):** + +Walk the file against the schema in [schema.md](schema.md). Required checks: + +- Frontmatter parses as valid YAML. +- Required fields: `id`, `source`, `timestamp`, `palette`, `spacing`, `typography`, `surfaces`. +- Body sections appear in order: `# Character`, `# Decisions` (when decisions are present). No prose in frontmatter. +- For any `### dim` block in the body, a matching `decisions[].dimension` entry exists in frontmatter (and vice versa). +- For any `rules[]` entry: `id` is unique, `pattern` is non-empty, optional `severity` ∈ `{critical, serious, nit}`, optional `match` ∈ `{exact, band, percent, structural}`, optional `support` ∈ `[0, 1]`. + +Common errors regardless of path: + +- Prose in frontmatter → move to body. +- `### dim` with no matching `decisions[]` entry → remove the orphan. +- Palette entry not cited in any evidence → cite it (from a bucket row) or drop it. +- Typography size not in the bucket → drop it; the surveyor missed it or it's not real. + +### 7. Provenance check + +For every value in your expression's frontmatter, confirm it appears in `bucket.json`. Quick sanity: + + jq -r '.values[] | select(.kind=="color") | .value' bucket.json | sort -u + # compare against your palette entries + +Any expression value that doesn't trace back is a hallucination. Remove it. + +### 8. Self-distance sanity + +**Preferred (CLI present):** + + ghost-drift compare expression.md expression.md + +Self-distance must be 0. Anything else means the file isn't deterministically loadable. + +**Prose fallback (no CLI / no ghost-drift):** + +Re-load the file mentally: parse the frontmatter, normalize whitespace in the body, then verify the file would round-trip through a YAML parser without info loss. If you can't be sure, run the CLI (it's the calculator that exists for exactly this question). The self-distance check is genuinely a "machine math" answer — prose verification is best-effort, not authoritative. -### 9. Sanity check +## When the bucket is incomplete - ghost-drift compare expression.md expression.md # self-distance should be 0 +If the surveyor's `bucket.json` has known gaps (a `# Coverage` note in the survey scratchpad, or thin coverage for a dialect), surface them in the expression's `# Character` body or as a Decision (e.g. `### scan-coverage` with evidence "iOS dialect under-sampled — only 23 color sites recorded; web dialect is the dominant signal in this expression"). Do not paper over gaps with invented values. ## When you cannot profile -If the project has no styling (backend-only, no UI), say so. Do not fabricate an expression. A placeholder expression poisons every downstream comparison. +If `bucket.json` is empty (a backend-only repo, no UI) and `map.md` confirms no design system, say so in `# Character` and emit a minimal expression with empty palette/spacing/typography/surfaces. Do not fabricate. A placeholder expression poisons every downstream comparison. diff --git a/packages/ghost-expression/src/skill-bundle/references/scan.md b/packages/ghost-expression/src/skill-bundle/references/scan.md new file mode 100644 index 0000000..2746971 --- /dev/null +++ b/packages/ghost-expression/src/skill-bundle/references/scan.md @@ -0,0 +1,120 @@ +--- +name: scan +description: Drive a full three-stage scan of a target — topology, objective, subjective — to produce map.md + bucket.json + expression.md. +handoffs: + - label: Compare against another expression + command: ghost-drift compare + prompt: Compare the expression.md I just wrote against another expression + - label: Inspect what stage to run next + command: ghost-expression scan-status + prompt: What scan stage should I run next in this directory? +--- + +# Recipe: Scan a target end-to-end + +**Goal:** drive a target through all three scan stages — topology → objective → subjective — and end with three valid artifacts in the scan directory: `map.md` + `bucket.json` + `expression.md`. This is the meta-recipe; each stage has its own deeper recipe (see [map.md](map.md), [survey.md](survey.md), [profile.md](profile.md)) that you dispatch into. + +You don't run a single CLI verb here. You orchestrate stages, validate after each, and stop when `scan-status` reports complete. + +## Overview + +``` +┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ +│ Stage 1 │ → │ Stage 2 │ → │ Stage 3 │ +│ topology │ │ objective │ │ subjective │ +│ map.md │ │ bucket.json │ │ expression.md │ +└──────────────┘ └──────────────┘ └──────────────────┘ + recipe: recipe: recipe: + map.md survey.md profile.md +``` + +Each stage's output is the next stage's input. Stage 3 is terminal — it's what other ghost tools (drift, fleet) consume. + +## Steps + +### 1. Locate the scan directory + +The scan directory is wherever the three artifacts will live. For a local repo, this is usually the repo root. For a fleet member managed centrally, the scan directory lives in the central repo (e.g. `/fleet/members//`) and the *target* (the source repo being scanned) is a separate location. + +Throughout this recipe, "scan dir" = where artifacts land; "target" = where source code lives. They may or may not be the same path. + +### 2. Check status + +**Preferred (CLI present):** + + ghost-expression scan-status [scan-dir] + +Reports per-stage state (`present` / `missing`) and the recommended next stage. If every stage is `present`, you're done. Otherwise, dispatch to the recipe for the recommended stage. + +Use `--format json` if you want to consume the result programmatically: + + ghost-expression scan-status . --format json + +**Prose fallback (no CLI):** + +Check three paths and report what's missing in this order: + +1. `/map.md` — if missing, recommended_next = `topology`. Stop checking. +2. `/bucket.json` — if missing, recommended_next = `objective`. Stop checking. +3. `/expression.md` — if missing, recommended_next = `subjective`. If present, recommended_next = `null` (scan complete). + +Use `Read` (or `Bash: ls `) to verify each file exists. The first missing artifact is the next stage to run. + +### 3. Stage 1 — Topology (`map.md`) + +Run when `scan-status` reports `topology: missing`. + +Recipe: [map.md](map.md). The agent reads `ghost-expression inventory ` for raw signals + the recipe's guidance, then writes `map.md` to the scan directory and validates with `ghost-expression lint map.md`. + +After validation, re-run `scan-status` and proceed. + +### 4. Stage 2 — Objective (`bucket.json`) + +Run when `scan-status` reports `topology: present` and `objective: missing`. + +Recipe: [survey.md](survey.md). The agent reads `map.md` to recognize the dialect, runs LLM-driven extraction (its own greps/regexes), records rows with empty `id` fields, finalizes IDs with `ghost-expression bucket fix-ids bucket.json -o bucket.json`, then validates with `ghost-expression lint bucket.json`. + +The survey is the longest stage and the one with the most discipline (exhaustiveness, saturation, cross-checking counts). Don't shortcut it — the interpreter downstream cannot fabricate values that aren't in the bucket, so missed values become missed expression fields permanently. + +After validation, re-run `scan-status` and proceed. + +### 5. Stage 3 — Subjective (`expression.md`) + +Run when `scan-status` reports both prior stages `present` and `subjective: missing`. + +Recipe: [profile.md](profile.md). The agent reads `map.md` (for repo-kind signals) and `bucket.json` (for ground truth) and writes `expression.md` purely as interpretation: names decisions, writes the prose body, fills frontmatter from bucket rows. Cannot invent values not in the bucket. Validates with `ghost-expression lint expression.md` and a self-distance sanity check (`ghost-drift compare expression.md expression.md` returns 0). + +### 6. Confirm complete + +Re-run `scan-status`. If `recommended_next` is `null`, the scan is done. + +## Resumability + +Each stage is resumable independently because `scan-status` checks artifact presence at the start. To force a stage rerun, delete its artifact and call `scan-status` again — the recommended_next will surface that stage. Same idiom orchestrators like design-world-model already use. + +## When a stage fails + +If a stage's lint fails, fix the issue in the recipe pass and re-validate. **Do not move to the next stage on a failed lint** — the next stage's recipe assumes a valid input. A malformed `map.md` poisons the survey; a malformed `bucket.json` poisons the interpretation. + +If you cannot make a stage pass (e.g. the target genuinely has no design system), the recipe for that stage tells you what to do — usually: write a minimal valid artifact that surfaces the gap (e.g. `expression.md` with empty palette and a `# Character` note explaining the absence), so downstream tools see honest "no signal" rather than a hallucinated one. + +## When the bucket should be merged across multiple targets + +Modular targets (one repo with N feature modules profiled separately) and fleet cohorts (N members merged into a cohort bucket) both run the survey stage per unit, then call: + + ghost-expression bucket merge -o merged.json + +Then run the interpreter recipe (Stage 3) against `merged.json` instead of a single-source bucket. The interpreter recipe handles merged buckets the same way as single-source ones — every row still has provenance via `source`, and the prose interpretation is grounded in counts that span sources. This composition lives in the orchestrator (`design-world-model`'s pipeline scripts), not in any ghost CLI verb. + +## Always + +- Run `scan-status` between stages. Don't assume; check. +- Validate after each stage. Lint passing is the success gate. +- Resolve token alias chains end-to-end in stage 2 (the bucket records the chain). +- Cite bucket rows as evidence in stage 3 decisions. + +## Never + +- Never skip a stage. The recipe semantics depend on each stage running in order. +- Never edit a downstream artifact (`expression.md`) to fix a missing value — go upstream and re-run the relevant stage. +- Never invent values absent from `bucket.json` when authoring `expression.md`. If a value is missing from the bucket, either re-run survey (it was missed) or accept the absence. diff --git a/packages/ghost-expression/src/skill-bundle/references/schema.md b/packages/ghost-expression/src/skill-bundle/references/schema.md index d3a732f..3d0a579 100644 --- a/packages/ghost-expression/src/skill-bundle/references/schema.md +++ b/packages/ghost-expression/src/skill-bundle/references/schema.md @@ -58,35 +58,6 @@ surfaces: shadowComplexity: subtle # deliberate-none | subtle | layered borderUsage: moderate # minimal | moderate | heavy -# slot → token bindings (optional but strongly recommended) -# -# `roles[].tokens.palette` is an open record — slot keys are free-form. -# Reach for the conventional vocabulary first: `background`, `foreground`, -# `surface`, `border`, `accent`, `muted`, `link`. Add others (`ring`, -# `popover`, `separator`, …) when they're load-bearing in your codebase. -# -# Slot values are either: -# - raw hex literals — `"#1a1a1a"` -# - local refs — `"{palette.dominant.}"` / `"{palette.semantic.}"` -# - opaque external refs — `"{base.color.brand.x}"` for token-pipeline -# consumers; the linter accepts these as deliberate passthroughs and -# does not try to resolve them -# -# Other dimensions (typography, spacing, surfaces) inline raw values. -roles: - - name: h1 - tokens: - typography: { family: "Geist", size: 52, weight: 500 } - evidence: ["src/components/h1.tsx:4"] - - name: button - tokens: - surfaces: { borderRadius: 8 } - palette: - background: "{palette.dominant.accent}" - foreground: "{palette.dominant.surface}" - border: "{palette.semantic.border-default}" - evidence: ["src/components/button.tsx:12"] - # extension bag (optional, opaque to comparisons) metadata: tone: editorial @@ -100,11 +71,6 @@ metadata: 2-4 sentences capturing the holistic personality of this design language. This is `observation.summary`. -# Signature - -- What makes this expression visually distinctive (becomes `observation.distinctiveTraits`). -- One bullet per trait. Include notable *absences* if they are load-bearing. - # Decisions ### color-strategy @@ -136,11 +102,10 @@ Every field lives in exactly one layer: | `id`, `source`, `timestamp`, `sources` | Frontmatter | | `observation.personality`, `observation.resembles` | Frontmatter | | `observation.summary` | **Body** (`# Character`) | -| `observation.distinctiveTraits` | **Body** (`# Signature` bullets) | | `decisions[].dimension` | Frontmatter | | `decisions[].decision` (prose) | **Body** (`### ` block) | | `decisions[].evidence` | **Body** (`**Evidence:**` bullets under `### `) | -| `palette`, `spacing`, `typography`, `surfaces`, `roles` | Frontmatter | +| `palette`, `spacing`, `typography`, `surfaces` | Frontmatter | | `embedding` | Sibling `embedding.md` | Putting prose into frontmatter is a schema error. The writer and reader both enforce this. When in doubt: structured data → frontmatter; narrative → body. @@ -149,4 +114,4 @@ Putting prose into frontmatter is a schema error. The writer and reader both enf ghost-expression lint expression.md -This catches schema violations, missing required fields, prose-in-frontmatter, orphaned decision blocks (body `### dim` with no matching frontmatter entry, or vice versa), and uncited palette entries. +This catches schema violations, missing required fields, prose-in-frontmatter, orphaned decision blocks (body `### dim` with no matching frontmatter entry, or vice versa), and uncited palette entries (info-level — palette colors not cited in any decision evidence/prose). diff --git a/packages/ghost-expression/src/skill-bundle/references/survey.md b/packages/ghost-expression/src/skill-bundle/references/survey.md new file mode 100644 index 0000000..09bf4a5 --- /dev/null +++ b/packages/ghost-expression/src/skill-bundle/references/survey.md @@ -0,0 +1,178 @@ +--- +name: survey +description: Scan a target and produce a bucket.json — the objective catalogue of design values, with no interpretation. +handoffs: + - label: Interpret the bucket into expression.md + command: (next stage — interpreter recipe) + prompt: Interpret the bucket I just wrote into expression.md + - label: Validate the bucket + command: ghost-expression lint bucket.json + prompt: Lint the bucket I just wrote +--- + +# Recipe: Survey a target into bucket.json + +**Goal:** produce a valid `bucket.json` (`ghost.bucket/v1`) that catalogues every concrete design value the target ships, with structured specs and per-value occurrence counts. **You are the surveyor, not the interpreter.** Record what is there. Do not assign meaning. Do not write prose. Do not invent. + +`bucket.json` is the middle artifact in a three-stage scan: topology (`map.md`) → objective (`bucket.json`) → subjective (`expression.md`). The interpreter (next stage) reads your bucket as ground truth and writes the prose. If you skip values or fabricate them here, the expression downstream is wrong. + +## Pre-requisite + +A `map.md` for the target must exist (Phase 0 — see `references/map.md`). It tells you where the design system lives — `design_system.entry_files`, `design_system.paths`, `feature_areas[].paths`, `composition.styling`, `composition.frameworks`. Without it you waste cycles re-discovering what the topology already specifies. **If `map.md` is missing, stop and run topology first.** + +## Bucket schema + +A `bucket.json` is `ghost.bucket/v1`: + +```json +{ + "schema": "ghost.bucket/v1", + "sources": [{ "target": "...", "commit": "...", "scanned_at": "..." }], + "values": [...], + "tokens": [...], + "components": [...] +} +``` + +Each row carries an `id` (deterministic SHA-256 prefix you do **not** compute by hand — see Step 6) and a `source` object (denormalize the same source entry you put in `sources[]`). Sections: + +- **`values[]`** — every concrete literal that ships in the design language. `kind` is open; recommended values: `color`, `spacing`, `typography`, `radius`, `shadow`, `breakpoint`, `motion`, `layout-primitive`. Other kinds (`z-index`, `opacity`, `cursor`, `gradient`, `iconography`, `aspect-ratio`) get a `value-kind-unknown` warning but are accepted — emit them when they matter. +- **`tokens[]`** — every named token declared in source (CSS variables, theme keys, design-token entries). Each row has `name`, `alias_chain` (path through any indirection — `["--button-bg", "--color-brand-primary"]` for a two-step chain; `[]` for a leaf defined inline), `resolved_value` (end-of-chain literal), optional `by_theme` for light/dark variants. +- **`components[]`** — every named component you can confidently identify (registry entries, exported PascalCase components with variants/sizes). Loose schema: `name`, `discovered_via` (`registry.json` / `heuristic` / etc.), optional `variants[]` and `sizes[]`. + +External libraries (icon sets, primitive collections, motion libs, charting, etc.) are intentionally *not* a bucket section. Whether a system uses Radix or hand-rolls primitives doesn't change what its design language *is*. When a library is load-bearing (icon family, font sourcing), surface it in the interpreter stage as prose evidence under the relevant decision dimension instead. + +Every row needs `occurrences` (total count across the scan) and (for values) `files_count` (distinct files that contain the value). Optional `usage` breaks down by context: `{className: 30, css_var: 17}`. Optional `role_hypothesis` is a single tentative role tag (`brand-primary`, `surface-elevated`); **leave it empty if you are not sure** — the interpreter does role assignment, not you. + +## Steps + +### 1. Read map.md and orient + +Open `map.md`. Note: + +- `composition.styling` — Tailwind, CSS modules, styled-components, scss, swift-tokens, etc. Drives your extraction strategy. +- `composition.frameworks` — react, next, swiftui, compose, … +- `design_system.entry_files` — start here. These declare the canonical token set. +- `design_system.paths` — directories where the design system lives. +- `feature_areas[].paths` — surfaces worth sampling for usage counts. + +Decide your extraction strategy from these signals — see Step 2. + +## The exhaustiveness rule + +Recall is the failure mode and the only one. A bucket missing 90% of a section's rows is a failed scan, even if every row that *is* there is well-formed — the interpreter downstream cannot recover what you didn't record. + +For every section (`values[]`, `tokens[]`, `components[]`): + +1. **Identify the canonical signal in this repo.** Where does the source of truth for this kind of thing actually live? It will be different in every repo — a manifest, a registry, a barrel export, a CSS declaration block, a naming convention. Use the strongest signal the repo offers. +2. **Enumerate, don't sample.** If you can count entries from the canonical signal independently, your row count must match. 6 components when the canonical signal lists 100 is a lie. +3. **Cross-check by a second method when one exists** (e.g., file count by glob vs. enumerated entries vs. import graph). If the two counts disagree by more than ~10%, you're missing entries — investigate before recording. +4. **Honest absence beats partial truth.** If the section has no canonical signal in this repo, leave the array empty rather than recording a sample. Empty is honest; sampled is misleading. + +This applies regardless of dialect. The recipe doesn't tell you what the canonical signal is — it depends on the repo. Your job is to find it and enumerate it. + +### 2. Choose your extraction strategy per dialect + +**You write your own greps and regexes. There is no pre-built parser.** Adapt to what's actually in the repo: + +- **Tailwind (Tailwind v3 / v4 with `@theme`)** — `rg -oN '\b(bg|text|border|fill|stroke|ring|outline|from|to|via|p[lrtbxy]?|m[lrtbxy]?|w|h|gap|space-[xy]|rounded(-[lrtb][lr]?)?|shadow|z|opacity)-[a-z0-9-]+(\[[^\]]+\])?' -g '*.{tsx,jsx,ts,js,html,vue,svelte}'` for class atoms. Then read `tailwind.config.{ts,js}` and any `@theme {}` block in CSS to map class atoms to literal values. +- **CSS / SCSS / CSS modules** — `rg -oN '#[0-9a-fA-F]{3,8}\b' -g '*.{css,scss,sass}'` for hex; `rg -oN '\b(rgba?|hsla?|oklch|color)\([^)]+\)' -g '*.{css,scss}'` for color functions; `rg -oN '\b[0-9]+(\.[0-9]+)?(px|rem|em|%|vh|vw|fr|ch|svh|dvh)\b' -g '*.{css,scss}'` for scalars; `rg -oN -- '--[a-z0-9-]+\s*:' -g '*.{css,scss}'` for custom properties. +- **CSS-in-JS (styled-components, emotion, vanilla-extract)** — same regex set but expand `-g '*.{ts,tsx,js,jsx}'`. Watch for template literals split across lines. +- **iOS / Swift** — `rg -oN 'Color\([^)]+\)|UIColor\([^)]+\)|\.(red|blue|green|orange|brand[A-Za-z]*)\b' -g '*.swift'` for color sites; `rg -oN '\b[0-9]+(\.[0-9]+)?\b' -g '*.swift' | sort | uniq -c | sort -rn | head -50` for likely scalars (lots of noise; keep top-N by frequency). +- **Android / Compose** — `rg -oN 'Color\(0x[0-9a-fA-F]+\)|colorResource\(R\.color\.[a-z_]+\)' -g '*.kt'`; same scalar approach. +- **Token JSON / YAML** — read directly with `cat`/Read tool. Token files are usually small and structured — parse them as data, don't grep. + +If the repo mixes dialects (e.g. `swiftui` + `arcade`), run extraction per dialect and merge into one bucket. + +### 3. Run extraction passes — apply the exhaustiveness rule per section + +The exhaustiveness rule (above) governs every section. The dialect-specific tactics here are how you implement it for `values[]` and `tokens[]`. For `components[]`, the rule is the same: find the canonical signal in *this* repo and enumerate it. + +For values + tokens, sloppy grep undercounts silently. Discipline: + +- **Multiple passes per kind.** Don't trust your first regex. After your color pass, run a second pass with a slightly different pattern and check the delta. New rows = your first pass missed. +- **Cross-check counts.** When you record a row with `occurrences: 47`, run `rg -c '\b#f97316\b' .` against the full repo and verify. If the count differs by more than ~10%, your regex is missing something — refine and re-pass. +- **Frequency clustering.** After the first sweep, list candidate values by frequency: `rg -oN '#[0-9a-fA-F]{6}' -g '*.css' | sort | uniq -c | sort -rn`. The top values are almost always real palette entries. Long-tail values are often comments, hashes, or test fixtures — verify before recording. +- **Spread check.** If a value appears in `files_count: 1`, it's likely incidental, not part of the design language. Note the count but don't promote with `role_hypothesis`. +- **Resolve aliases exhaustively.** Every named token declared in the canonical token source becomes a `tokens[]` row. Don't sample tokens — count the declarations and match the row count. When a token's value is `var(--other)`, follow the chain to the literal; record the **token row** with the chain, and the **value row** for the resolved literal. + +For components: + +- **Components are countable.** Count them by whatever signal the repo offers (manifest entries, barrel exports, naming pattern under a known directory). If you can count to 50 and your bucket has 6 rows, you've sampled — go back and enumerate. + +### 4. Sample feature areas for usage counts + +For each `feature_areas[]` entry in `map.md`, walk a few files to measure how the values you found in `entry_files` actually get used. This produces the `occurrences` and `files_count` numbers. Don't sample exhaustively — 3–5 files per feature area is usually enough; the goal is a representative count, not a perfect one. + +Update the `usage` breakdown when context matters. Examples: `{className: 30, css_var: 17}` for a hex used in both Tailwind classes and CSS variables; `{token-resolution: 1, inline: 46}` for a hex defined once and copy-pasted everywhere (a smell worth flagging via `role_hypothesis: "ad-hoc"` or similar). + +### 5. Write rows with empty IDs + +Build the bucket file. For every row, leave `id` as an empty string `""`. You don't compute SHA-256 hashes by hand. Example value row: + +```json +{ + "id": "", + "source": { "target": "github:block/ghost", "commit": "abc123", "scanned_at": "2026-04-29T12:00:00Z" }, + "kind": "color", + "value": "#f97316", + "raw": "bg-orange-500", + "spec": { "space": "srgb", "hex": "#f97316" }, + "occurrences": 47, + "files_count": 12, + "usage": { "className": 30, "css_var": 17 } +} +``` + +Same shape per token and component row, just different content fields. **Every row gets the same `source` object** (denormalized so the row survives merges with its origin attribution). Fill `sources[]` at the top of the bucket with the same single source. + +### 6. Populate IDs + +Run: + + ghost-expression bucket fix-ids bucket.json -o bucket.json + +This recomputes every row's `id` from its content fields. Idempotent — running it again does nothing. + +### 7. Validate + + ghost-expression lint bucket.json + +Fix everything `lint` flags as an error. Warnings (unknown `kind`, `id-mismatch` if you skipped Step 6, etc.) are signals — investigate them, but they don't block. + +### 8. Coverage check (gate before declaring done) + +Before declaring the bucket done, walk each section and confirm exhaustiveness: + +- **`components[]`** — what's the canonical signal in this repo? Count it independently. If your row count is below that count, you've under-recorded. Either add the missing rows or, if the section truly isn't enumerable here, leave the array empty. +- **`tokens[]`** — count the named-token declarations in the canonical token source(s) named in `map.md`. Your row count should match. +- **`values[]`** — frequency-cluster again with a fresh grep. New top-N entries that aren't in your bucket = missed. +The bucket is **saturated** when another exhaustiveness pass adds fewer than ~2 new rows across all sections AND your component/token row counts match (or come very close to) an independent count of the canonical signal. If exhaustiveness disagrees with what you have, exhaustiveness wins — re-pass. + +Hard stop conditions: + +- ~100 files read total, OR +- ~20 minutes wall, OR +- ~200k tokens consumed. + +If you hit a hard stop with exhaustiveness *not* met, write a `# Coverage` note in your scratchpad listing exactly which sections fall short and by how much. Surface it to the interpreter — it tells them which decisions are well-grounded and which aren't. **Do not pad the bucket with sampled rows to look exhaustive.** + +## Always + +- Use `bucket.json` as the canonical filename. +- Every value/token row carries `source`, `occurrences`, and (for values) `files_count`. +- Resolve token alias chains end-to-end. The `alias_chain` array captures the path. +- Validate with `ghost-expression lint bucket.json` before declaring success. +- After authoring rows with empty IDs, run `bucket fix-ids` exactly once. +- **Cross-check your component and token counts against an independent count of the canonical signal in this repo.** Disagreement = re-pass. + +## Never + +- **Never write prose.** No `description`, no rationale fields. Prose is the interpreter's job. +- **Never invent values.** If you didn't observe it in source, it doesn't go in the bucket. +- **Never sample.** Either enumerate exhaustively or leave the section empty. A bucket with 6 components when the canonical signal has 100 is worse than no `components[]` at all. +- **Never assign roles confidently.** `role_hypothesis` is a *hint*, optional, and tentative. The interpreter has the final word. If you're not sure, leave it empty. +- **Never undercount silently.** If your coverage is weak (mobile dialects, custom DSLs, no canonical signal in this repo), surface it in a `# Coverage` scratchpad note and tell the interpreter. +- **Never compute IDs by hand.** Use `bucket fix-ids`. +- **Never use placeholder/glob names.** A component row with `name: "*Button"` or `name: ""` is sampling-disguised-as-a-row. Enumerate concretely. +- **Never edit a bucket after the interpreter has used it.** If you find a missed value later, re-run survey end-to-end. The bucket is the frozen ground truth between scan and interpretation. diff --git a/packages/ghost-expression/test/cli.test.ts b/packages/ghost-expression/test/cli.test.ts index 1efbfd1..e05e5d2 100644 --- a/packages/ghost-expression/test/cli.test.ts +++ b/packages/ghost-expression/test/cli.test.ts @@ -1,6 +1,8 @@ -import { mkdir, rm, writeFile } from "node:fs/promises"; +import { mkdir, readFile, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; +import type { Bucket, BucketSource } from "@ghost/core"; +import { tokenRowId, valueRowId } from "@ghost/core"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { buildCli } from "../src/cli.js"; @@ -31,10 +33,6 @@ surfaces: Quiet and direct. -# Signature - -- Small, plain surfaces - # Decisions ### shape-language @@ -165,3 +163,231 @@ describe("ghost-expression CLI defaults", () => { expect(result.code).toBe(1); }); }); + +const SOURCE_A: BucketSource = { + target: "github:block/ghost", + commit: "abc123", + scanned_at: "2026-04-29T12:00:00Z", +}; + +const SOURCE_B: BucketSource = { + target: "github:block/other", + commit: "def456", + scanned_at: "2026-04-29T12:00:00Z", +}; + +function makeBucket(source: BucketSource, hex = "#f97316"): Bucket { + return { + schema: "ghost.bucket/v1", + sources: [source], + values: [ + { + id: valueRowId(source, "color", hex, hex), + source, + kind: "color", + value: hex, + raw: hex, + occurrences: 1, + files_count: 1, + }, + ], + tokens: [ + { + id: tokenRowId(source, "--brand-primary"), + source, + name: "--brand-primary", + alias_chain: [], + resolved_value: hex, + occurrences: 1, + }, + ], + components: [], + }; +} + +describe("ghost-expression lint dispatches by file kind", () => { + let dir: string; + + beforeEach(async () => { + dir = join( + tmpdir(), + `ghost-expression-cli-${Date.now()}-${Math.random().toString(36).slice(2)}`, + ); + await mkdir(dir, { recursive: true }); + }); + + afterEach(async () => { + await rm(dir, { recursive: true, force: true }); + }); + + it("lints a well-formed bucket.json with exit 0", async () => { + await writeFile( + join(dir, "bucket.json"), + JSON.stringify(makeBucket(SOURCE_A), null, 2), + ); + + const result = await runCli(["lint", "bucket.json"], dir); + + expect(result.code).toBe(0); + expect(result.stdout).toContain("0 error(s)"); + }); + + it("lints a malformed bucket.json with exit 1", async () => { + await writeFile( + join(dir, "bucket.json"), + JSON.stringify({ schema: "ghost.bucket/v0" }, null, 2), + ); + + const result = await runCli(["lint", "bucket.json"], dir); + + expect(result.code).toBe(1); + }); + + it("auto-detects bucket-by-content when path lacks .json extension", async () => { + await writeFile( + join(dir, "bucket.txt"), + JSON.stringify(makeBucket(SOURCE_A), null, 2), + ); + + const result = await runCli(["lint", "bucket.txt"], dir); + + expect(result.code).toBe(0); + expect(result.stdout).toContain("0 error(s)"); + }); +}); + +describe("ghost-expression bucket merge", () => { + let dir: string; + + beforeEach(async () => { + dir = join( + tmpdir(), + `ghost-expression-merge-${Date.now()}-${Math.random().toString(36).slice(2)}`, + ); + await mkdir(dir, { recursive: true }); + }); + + afterEach(async () => { + await rm(dir, { recursive: true, force: true }); + }); + + it("merges two buckets with distinct sources into one", async () => { + await writeFile(join(dir, "a.json"), JSON.stringify(makeBucket(SOURCE_A))); + await writeFile(join(dir, "b.json"), JSON.stringify(makeBucket(SOURCE_B))); + + const result = await runCli( + ["bucket", "merge", "a.json", "b.json", "-o", "merged.json"], + dir, + ); + + expect(result.code).toBe(0); + const merged = JSON.parse( + await readFile(join(dir, "merged.json"), "utf-8"), + ); + expect(merged.schema).toBe("ghost.bucket/v1"); + expect(merged.sources).toHaveLength(2); + expect(merged.values).toHaveLength(2); + expect(merged.tokens).toHaveLength(2); + }); + + it("dedupes rows with identical IDs (same source, same content)", async () => { + await writeFile(join(dir, "a.json"), JSON.stringify(makeBucket(SOURCE_A))); + await writeFile(join(dir, "a2.json"), JSON.stringify(makeBucket(SOURCE_A))); + + const result = await runCli( + ["bucket", "merge", "a.json", "a2.json", "-o", "merged.json"], + dir, + ); + + expect(result.code).toBe(0); + const merged = JSON.parse( + await readFile(join(dir, "merged.json"), "utf-8"), + ); + expect(merged.sources).toHaveLength(1); + expect(merged.values).toHaveLength(1); + expect(merged.tokens).toHaveLength(1); + }); + + it("writes to stdout when -o is omitted", async () => { + await writeFile(join(dir, "a.json"), JSON.stringify(makeBucket(SOURCE_A))); + + const result = await runCli(["bucket", "merge", "a.json"], dir); + + expect(result.code).toBe(0); + const merged = JSON.parse(result.stdout); + expect(merged.schema).toBe("ghost.bucket/v1"); + expect(merged.values).toHaveLength(1); + }); + + it("fails when an input bucket has lint errors", async () => { + await writeFile( + join(dir, "bad.json"), + JSON.stringify({ schema: "ghost.bucket/v0" }), + ); + + const result = await runCli( + ["bucket", "merge", "bad.json", "-o", "merged.json"], + dir, + ); + + expect(result.code).toBe(1); + expect(result.stderr).toContain("failed bucket lint"); + }); +}); + +describe("ghost-expression bucket fix-ids", () => { + let dir: string; + + beforeEach(async () => { + dir = join( + tmpdir(), + `ghost-expression-fixids-${Date.now()}-${Math.random().toString(36).slice(2)}`, + ); + await mkdir(dir, { recursive: true }); + }); + + afterEach(async () => { + await rm(dir, { recursive: true, force: true }); + }); + + it("populates empty IDs and writes a lint-clean bucket", async () => { + const draft: Bucket = { + schema: "ghost.bucket/v1", + sources: [SOURCE_A], + values: [ + { + id: "", + source: SOURCE_A, + kind: "color", + value: "#f97316", + raw: "#f97316", + occurrences: 1, + files_count: 1, + }, + ], + tokens: [], + components: [], + }; + await writeFile(join(dir, "draft.json"), JSON.stringify(draft)); + + const fix = await runCli( + ["bucket", "fix-ids", "draft.json", "-o", "fixed.json"], + dir, + ); + expect(fix.code).toBe(0); + + const lint = await runCli(["lint", "fixed.json"], dir); + expect(lint.code).toBe(0); + expect(lint.stdout).toContain("0 error(s)"); + }); + + it("rejects more than one input file", async () => { + await writeFile(join(dir, "a.json"), JSON.stringify(makeBucket(SOURCE_A))); + await writeFile(join(dir, "b.json"), JSON.stringify(makeBucket(SOURCE_B))); + + const result = await runCli(["bucket", "fix-ids", "a.json", "b.json"], dir); + + expect(result.code).toBe(2); + expect(result.stderr).toContain("exactly one input file"); + }); +}); diff --git a/packages/ghost-expression/test/context/__snapshots__/review-command.test.ts.snap b/packages/ghost-expression/test/context/__snapshots__/review-command.test.ts.snap index d689ca5..87080e3 100644 --- a/packages/ghost-expression/test/context/__snapshots__/review-command.test.ts.snap +++ b/packages/ghost-expression/test/context/__snapshots__/review-command.test.ts.snap @@ -18,100 +18,84 @@ Your job: check code for **drift** from the values below — hardcoded hexes, of If \`$ARGUMENTS\` is provided, analyze that specific file. If \`$ARGUMENTS\` is empty, ask the user which file(s) to review, or offer to scan recently changed components. -## 1. Palette drift +## Critical (2) -> Treat hue as opt-in communication, not ambient decoration — the default theme is pure achromatic, so every bit of chromatic color that appears carries semantic meaning (danger, success, info, warning, chart). Brand personality is expressed through luminance contrast and shape, which makes the system maximally themeable without color conflicts. +### \`no-off-palette-hex\` — color-strategy -**Allowed colors** (21 total — prefer semantic tokens over raw hex): +Hex literals must come from the documented palette -- Dominant: \`#1a1a1a\` (primary), \`#ffffff\` (background), \`#000000\` (inverse) -- Neutrals (ramp): \`#ffffff\`, \`#f5f5f5\`, \`#f0f0f0\`, \`#e8e8e8\`, \`#e5e5e5\`, \`#cccccc\`, \`#999999\`, \`#666666\`, \`#333333\`, \`#232323\`, \`#1a1a1a\`, \`#000000\` -- Semantic hues: \`#f94b4b\` (danger), \`#91cb80\` (success), \`#5c98f9\` (info), \`#fbcd44\` (warning) +> Default theme is achromatic — chromatic colors are reserved for semantic states (danger, success, info, warning) and chart data. Any new hex literal is drift unless it lands in the palette. -### Critical +**Pattern:** \`#[0-9a-fA-F]{3,8}\` +**Match:** \`exact\` +**Enforce at:** \`className\`, \`css_var\`, \`inline_style\` +**Support:** 94% -| Check | Allowed | What to look for | -|-------|---------|------------------| -| Off-palette hex in JSX/CSS | \`#1a1a1a\`, \`#ffffff\`, \`#000000\`, \`#f5f5f5\`, \`#f0f0f0\`, \`#e8e8e8\`, \`#e5e5e5\`, \`#cccccc\`, \`#999999\`, \`#666666\`, \`#333333\`, … (see list above) | Any \`#[0-9a-fA-F]{3,8}\` literal not in the allowed list | -| Tailwind arbitrary color | use semantic tokens | \`bg-[#...]\`, \`text-[#...]\`, \`border-[#...]\` with arbitrary hex | -| Named Tailwind color for semantic role | use semantic token | \`text-red-500\`, \`bg-green-600\`, etc. when a matching semantic token exists | +### \`no-foreign-fonts\` — font-sourcing -### Serious +Do not bundle additional typefaces -| Check | Allowed | What to look for | -|-------|---------|------------------| -| danger must use the semantic token | \`#f94b4b\` | Raw \`#f94b4b\` or near-equivalent hardcoded; prefer the \`danger\` token | -| success must use the semantic token | \`#91cb80\` | Raw \`#91cb80\` or near-equivalent hardcoded; prefer the \`success\` token | -| info must use the semantic token | \`#5c98f9\` | Raw \`#5c98f9\` or near-equivalent hardcoded; prefer the \`info\` token | -| warning must use the semantic token | \`#fbcd44\` | Raw \`#fbcd44\` or near-equivalent hardcoded; prefer the \`warning\` token | +> Library ships no bundled fonts — system-ui sans, Geist Mono, and a generic serif fallback. Adding @font-face or importing a webfont crosses the font-sourcing decision. -## 2. Shape language (radius) +**Pattern:** \`@import\\s+url\\([^)]*fonts\` +**Match:** \`exact\` +**Enforce at:** \`css_var\`, \`inline_style\` +**Support:** 100% -> Apply a pill-first radius philosophy that visually separates interactive from structural surfaces — buttons, inputs, and badges fully round to 999px, while cards, modals, and dropdowns use moderate radii (10–24px). Users intuit what is tappable versus what is container. +## Serious (3) -**Allowed radii**: \`10px\`, \`14px\`, \`16px\`, \`20px\`, \`24px\`, \`999px (pill)\` +### \`pill-interactives\` — shape-language -### Critical +Buttons, inputs, and badges must be fully rounded -| Check | Allowed | What to look for | -|-------|---------|------------------| -| Custom radius value | \`10px\`, \`14px\`, \`16px\`, \`20px\`, \`24px\`, \`999px (pill)\` | \`rounded-[Npx]\`, \`border-radius: Npx\`, or \`--radius: Npx\` outside the set | -| Interactive element not pill | \`rounded-full\` / \`rounded-pill\` | \`