Skip to content

Latest commit

 

History

History
695 lines (567 loc) · 22.3 KB

File metadata and controls

695 lines (567 loc) · 22.3 KB

Curations

Curations are Tessellate's multi-brand publishing contract. A curation is a complete design treatment, not only a color preset. It can carry token values, CSS variables, implementation mappings, preview paths, and page-kit metadata for one brand interpretation.

Tessellate keeps this contract configurable. Curation slugs, CSS variable prefixes, selector attributes, component package names, mapping namespaces, and preview paths belong to the connected design-system repo.

Numbered Kit Convention

The default curation convention recognizes page prefixes 00 through 13. Headless publish can accept any requested range, including 00..19; prefixes beyond the built-in set are inferred from the Figma page name and written into the curation manifest. Repos can use another convention if their design system needs a smaller or larger kit.

Prefix Default surface
00 Cover and tokens
01 Storefront hero
02 Storefront service detail
03 Storefront booking flow
04 Email booking confirmed
05 Email appointment reminder
06 Email receipt
07 Social announcement square
08 Social quote story
09 Receipt microsite
10 Check-in card
11 Loyalty card
12 Wallet pass
13 Refusal notes

The leading number is the durable parser key. The human-readable page label can vary by team.

Headless Figma REST Publish

Curations can be published from CI when a job has a Figma personal access token and a Figma file key. Token variables can come from either Figma REST or a plugin-exported variables JSON file:

Do not paste token values inline with commands or into shell prompts. Export FIGMA_TOKEN from a secret manager, CI secret, or a local env file kept outside the repo/release root before running Tessellate; examples below assume FIGMA_TOKEN is already set. Use --figma-token-env <NAME> when the token is stored under another environment variable.

npm install --global github:kfrrst/tessellate#v0.3.1

tessellate curation publish \
  --file <figma-file-key> \
  --slug <curation-slug> \
  --pages "00..19" \
  --variables-file ./variables.reference.json \
  --surfaces-file ./tessellate-surfaces.json \
  --preview-host "https://cdn.example.com/tessellate" \
  --mappings-file ./component-mappings.json \
  --component-import-root "@example/design-system" \
  --out ./public-mirror

The command is noninteractive and uses Figma REST APIs to read the file tree and export page PNG previews. It reads local Variables through REST unless --variables-file is supplied. Optional component-mapping registries are carried into the generated mapping artifacts. It reads FIGMA_TOKEN by default; use --figma-token-env <NAME> when CI stores the token under another secret name.

Use --variables-file <path> when the Figma account cannot use the Variables REST API. The full Tessellate plugin emits variables.{curationSlug}.json from Figma's in-editor local variables API. When that file is provided, the CLI skips GET /v1/files/:file_key/variables/local entirely; file tree reads and page PNG exports still use Figma REST.

CI modes:

  • --dry-run resolves Figma data and computes artifacts without writing files.
  • --check exits non-zero when any output artifact is missing or different.
  • --no-figma-previews writes preview manifest entries while deferring PNG generation to tessellate curation render-previews.

Variables file shape:

{
  "version": 1,
  "source": { "adapter": "figma-plugin" },
  "meta": {
    "variableCollections": {
      "collection-id": {
        "id": "collection-id",
        "name": "Reference",
        "defaultModeId": "mode-light",
        "modes": [{ "modeId": "mode-light", "name": "Light" }]
      }
    },
    "variables": {
      "variable-id": {
        "id": "variable-id",
        "name": "color/core",
        "variableCollectionId": "collection-id",
        "resolvedType": "COLOR",
        "valuesByMode": {
          "mode-light": { "r": 0.1098, "g": 0.4863, "b": 0.3294, "a": 1 }
        }
      }
    }
  }
}

Use --surfaces-file <path> when your numbered kit uses names that differ from the built-in default surface labels. The file is a JSON array keyed by page prefix. Matching prefixes override the default surface name, manifest label, required flag, and preview filename; omitted prefixes continue to use the built-in convention or the Figma page name fallback.

[
  {
    "pagePrefix": "00",
    "surface": "cover",
    "label": "Cover + tokens",
    "required": true
  },
  {
    "pagePrefix": "01",
    "surface": "storefront_hero",
    "label": "Storefront - hero",
    "required": true
  },
  {
    "pagePrefix": "02",
    "surface": "storefront_team_roster",
    "label": "Storefront - team roster",
    "required": true
  }
]

Copy Truthfulness Check

Use tessellate copy-check before publishing a curation kit when design copy contains availability, quantity, review, fee, deposit, or vertical-specific language that must stay truthful:

tessellate copy-check <figma-file-key> \
  --surfaces-file ./tessellate-surfaces.json \
  --terminology ./terminology.json \
  --claims ./claims.json \
  --pages "00..13" \
  --threshold strict \
  --out ./copy-report.json

The command reads GET /v1/files/:fileKey through Figma REST, scans text layers on each configured curation surface, and writes deterministic JSON for CI, reviewers, MCP hosts, and agents. It reads FIGMA_TOKEN by default; pass --figma-token-env <NAME> when the token is stored under another environment variable. Tests and private API proxies can point the command at a different Figma-compatible endpoint with --figma-api-base-url <url>.

The check is generic. It does not hardcode a sponsor, customer, or vertical. Repos decide which surface names, nouns, and claim patterns are acceptable.

Flag classes:

Class Meaning
fabricated-availability Copy asserts availability or booking certainty that the repo has not allowed
fabricated-count Copy asserts exact counts, inventory, people, locations, or number words that are not allowed
fabricated-review-source Copy cites reviews, ratings, publications, or social proof sources that are not allowed
fabricated-fee-claim Copy asserts fee, deposit, cancellation, or no-fee claims that are not allowed
vertical-locked-noun Copy uses a vertical-specific noun where the kit should stay generic or tokenized

Terminology can be a noun replacement map:

{
  "nouns": {
    "chair": "{seat}",
    "stylist": "{provider}"
  }
}

Repos that already maintain term dictionaries can also use a terms-style shape:

{
  "terms": [
    { "noun": "chair", "token": "{seat}" },
    { "term": "stylist", "replacement": "{provider}" }
  ]
}

claims.json should describe the deterministic patterns the repo wants to gate:

{
  "availability": {
    "allow": ["available by appointment"],
    "patterns": ["\\bavailable today\\b", "\\bbook now\\b"]
  },
  "counts": {
    "nouns": ["locations", "providers", "appointments"],
    "numberWords": ["dozens", "hundreds", "thousands"]
  },
  "reviewSources": {
    "allowed": ["internal testimonials"],
    "patterns": ["\\bGoogle reviews\\b", "\\bYelp\\b", "\\b5-star\\b"]
  },
  "fees": {
    "patterns": ["\\bno deposit\\b", "\\bno hidden fees\\b", "\\bfree cancellation\\b"]
  }
}

Use --threshold strict to exit non-zero when any fail-severity finding is reported. Use --threshold warn when CI should attach copy-report.json without blocking the run. MCP parity for this feature means stable CLI invocation plus JSON input and output; no special host runtime is required.

Materiality Pass

Use tessellate materiality before publishing or reviewing a curation surface when the kit must avoid banned material treatments while still allowing explicit, profile-approved tactile cues:

tessellate materiality <fileKey:nodeId> \
  --profile ./materiality-profile.json \
  --threshold strict \
  --out ./materiality-report.json

The command reads GET /v1/files/:fileKey through Figma REST and inspects the requested node subtree. It reads FIGMA_TOKEN by default; pass --figma-token-env <NAME> when the token is stored under another environment variable. Tests and private API proxies can point the command at a different Figma-compatible endpoint with --figma-api-base-url <url>.

The check is deterministic and generic. Repos decide which bans are enabled and which cue roles are allowed in their profile. Repos can use the built-in detectors below or add custom banned IDs with a detect object over node type, name/text predicates, fill opacity, gradient-stop counts, image scale modes, and simple has tokens. Unsupported banned IDs without a supported detector are reported as materiality-unsupported-detector warnings.

Banned classes:

Class Meaning
glassmorphism Detects frosted glass, blur-heavy translucent panels, and glass-card treatments when the profile enables the ban.
neumorphism Detects soft extruded light/dark shadow pairings and recessed controls when the profile enables the ban.
gradient-mesh Detects mesh-like multi-stop gradient material treatments when the profile enables the ban.
fake-paper Detects artificial paper/card texture treatments when the profile enables the ban.

Permitted cues are profile-defined candidates, not automatic writes:

Cue Meaning
groundTint A profile-approved surface tint targeted by role names and appliesTo rules.
grain A profile-approved grain cue targeted by role names and appliesTo rules.
paperDepthShadow A profile-approved depth shadow targeted by role names and appliesTo rules.

Use --threshold strict to exit non-zero when any fail-severity banned material finding is reported. Use --threshold warn when CI should attach materiality-report.json without blocking the run.

Passing --apply true marks permitted-cue candidates as apply-requested in the JSON report:

tessellate materiality <fileKey:nodeId> \
  --profile ./materiality-profile.json \
  --apply true \
  --threshold warn \
  --out ./materiality-report.json

The CLI remains read-only and does not mutate Figma through REST. Plugin, MCP, or write-capable bridge consumers can read the report and perform write-back under their own permissions. MCP parity means stable CLI invocation plus JSON input and output; write-back requires that separate write-capable bridge.

Token Output

Curations publish standard token JSON per curation. Token leaves may use the W3C-style $value and $type fields. Existing token tools that use value and type can be bridged without changing the curation contract.

When the plugin exports a variables snapshot, the default path is:

variables.{curationSlug}.json

Default token path:

tokens/curations/{curationSlug}.tokens.json

Example token leaf:

{
  "color": {
    "core": {
      "$value": "#1c7c54",
      "$type": "color"
    }
  }
}

CSS Variable Layer

Each curation also publishes a CSS variable layer. The selector attribute and custom-property prefix are configurable.

Default CSS path:

tokens/curations/{curationSlug}.vars.css

Example output:

[data-curation="reference"] {
  --curation-color-core: #1c7c54;
  --curation-typography-headline-font-family: "Example Serif", serif;
  --curation-typography-headline-font-size: 80px;
}

Teams that already use a different attribute or prefix can configure their own values, such as data-brand and --brand-*.

Mapping Output

Tessellate supports two curation mapping shapes:

Mode Output
Aware One mapping file that accepts a runtime curation context
Compiled One mapping file per curation slug
Both Both shapes, useful during migration

Default paths:

mappings/curation-aware-mappings.json
mappings/curations/{curationSlug}/component-mappings.json

The mapping entries keep component imports and props configurable. Tessellate does not assume a specific component package or tenant runtime.

Preview Output

Preview manifests describe per-surface preview images for a curation. Preview assets can be generated by the connected pipeline or referenced by public URLs owned by the repo.

Default preview paths:

previews/{curationSlug}/manifest.json
previews/{curationSlug}/{NN}-{surface}.png

Preview manifests are suitable for curation galleries, side-by-side review, and release validation.

Licensed Font Preview Rendering

Host-route previews can load customer-owned licensed fonts before screenshots are captured. This avoids judging brand surfaces in fallback fonts while keeping font binaries outside Tessellate.

tessellate render-previews \
  --curation-dir ./public-mirror \
  --slug reference \
  --url-template "https://preview.example.com/{surface}?curation={slug}" \
  --fontset ./fontset.json \
  --font-coverage-source ./figma-tree.json \
  --coverage-report ./font-coverage.json \
  --font-policy strict

tessellate curation render-previews remains supported as a compatibility alias. When --fontset is provided, Tessellate injects generated @font-face CSS into the browser page and waits for document.fonts.ready before taking the screenshot. Teams can still pass --screenshot-command for repo-owned render tooling; the command template can consume {fontCssPath}, {fontsetPath}, {coverageReport}, and {fontPolicy}.

fontset.json is a JSON array:

[
  {
    "family": "Example Serif",
    "style": "Regular",
    "weight": 400,
    "woff2Url": "https://cdn.example.com/fonts/example-serif-regular.woff2",
    "licenseNote": "Customer-hosted licensed font"
  }
]

At least one browser-loadable URL is required per entry: woff2Url, woffUrl, otfUrl, or ttfUrl. Local desktop paths such as otfPath belong to Figma/Desktop companion flows and are not used by the headless host-route renderer.

--font-coverage-source should point to a Figma-like JSON tree containing text nodes with style.fontFamily and style.fontStyle metadata. The generated coverage report flags every text node whose family/style is absent from the fontset. --font-policy strict fails on missing coverage; --font-policy warn keeps rendering and emits warnings for review.

Per-Surface Render Targets

Use --targets <targets.json> when a kit contains surfaces that should not all render through the same host URL template. Target maps are keyed by curation manifest surface name.

tessellate render-previews \
  --curation-dir ./public-mirror \
  --slug reference \
  --targets ./targets.json \
  --report ./render-coverage.json

Example target map:

{
  "storefront_hero": {
    "type": "host-route",
    "urlTemplate": "https://preview.example.com/{surface}?curation={slug}"
  },
  "wallet_pass": {
    "type": "artifact",
    "render": "image",
    "command": "node ./scripts/render-pass.mjs --out {output}"
  },
  "social_card": {
    "type": "image-composition",
    "sourcePath": "./artifacts/social-card.png",
    "aspect": "1:1"
  }
}

Supported target types:

Type Required fields Behavior
host-route urlTemplate Captures a browser screenshot through Playwright or --screenshot-command.
artifact command Runs a customer-owned command, useful for wallet/pass/PDF renderers.
image-composition sourcePath or command Copies a prepared image or runs a composition command for fixed-aspect outputs.

--report writes render coverage using the shared quality-report envelope plus covered, uncovered, and perSurface. Missing required targets fail the run. Missing optional targets stay visible as warnings. Failed renders include surface, path, exit code, and stderr evidence.

Email Preview Rendering

Use tessellate render-previews --email when a curation kit includes email surfaces that need proof across multiple configured email clients. Tessellate classifies email surfaces from the curation manifest, reads the HTML source map, and dispatches each source to repo-owned client adapters.

tessellate render-previews \
  --email \
  --curation-dir ./public-mirror \
  --clients ./clients.json \
  --email-source ./email-source.json \
  --slug reference \
  --dark-mode true \
  --report ./email-render-report.json

clients.json contains adapter commands. The command template receives the HTML source path, output path, and warnings path:

{
  "clients": [
    {
      "id": "desktop-a",
      "command": "node render-client.mjs {htmlPath} {output} {warningsPath}"
    }
  ]
}

Tessellate shell-quotes placeholder values before executing adapter commands, so paths with spaces are safe when passed through {htmlPath}, {output}, or {warningsPath}. Quote any literal executable or script paths you write directly inside the command string.

A bare array of client objects is also accepted by implementations that expose that compatibility shape.

email-source.json maps curation surface ids or names to HTML source files:

{
  "email_booking_confirmed": "./booking-confirmed.html"
}

With --dark-mode true, Tessellate asks adapters to produce both light and dark renders when the adapter supports that mode. The report includes per-client artifacts, warnings, and failures. Any failed configured client render exits non-zero so CI and MCP hosts can treat the JSON report as the stable contract.

Before/After Parity Diff

Use tessellate parity to compare two PNG renders of the same surface:

tessellate parity \
  --a ./reference/01-storefront-hero.png \
  --b ./previews/reference/01-storefront-hero.png \
  --mode design-vs-impl \
  --threshold 0.02 \
  --out ./parity-report.json

Modes:

Mode Use
design-vs-impl Figma/reference export vs host-route implementation render.
cascade-on-vs-off Two current renders in different cascade or feature-flag states.

The report uses the shared quality-report envelope and includes overallDivergence, regions, changedPixels, comparedPixels, and ignoredPixels. Divergent regions are classified as fallback-palette-leak in cascade mode, or as color-shift / position-shift in design-vs-implementation mode. --ignore-regions <regions.json> excludes intentionally dynamic content:

[
  { "bbox": [0, 0, 320, 80], "reason": "rotating announcement" }
]

The command exits non-zero when divergence exceeds --threshold.

Structural Distinctness Diff

Use tessellate curations diff to compare two or more numbered curation kits and detect when a new kit is structurally too close to another kit. The command reads Figma file trees through REST, applies the configured surface map, builds a color-blind skeleton for each surface, and emits a deterministic JSON report.

tessellate curations diff \
  <file-key-a> <file-key-b> <file-key-c> \
  --surfaces-file ./tessellate-surfaces.json \
  --registry-file ./component-mappings.json \
  --pages "00..13" \
  --threshold 0.85 \
  --out ./curation-structure-report.json

The skeleton intentionally ignores color, fills, strokes, token values, text content, image URLs, and other content-specific values. It compares node type, child topology, layout mode, nesting depth, approximate size buckets, surface prefix, and mapped component names when a registry is provided.

Similarity is scored per surface:

similarity =
  0.40 * nodeTreeEditDistance +
  0.35 * layoutTopology +
  0.25 * sharedComponentSet

Verdicts:

Verdict Meaning
near-identical Surface score is at or above --threshold
similar Surface score is at or above max(0.65, threshold - 0.2)
distinct Surface score is below the similar threshold

The command exits non-zero when any surface is near-identical, so CI can fail a kit that appears to be a recolored copy of an existing structure. Use --baseline <fileKey> to compare every candidate against a known kit while still producing pairwise comparisons among the provided candidates.

Report shape:

{
  "version": 1,
  "command": "curations diff",
  "status": "failed",
  "threshold": 0.85,
  "pairs": [
    {
      "a": { "fileKey": "reference", "name": "Reference Kit" },
      "b": { "fileKey": "candidate", "name": "Candidate Kit" },
      "overallSimilarity": 0.91,
      "verdict": "near-identical",
      "surfaces": [
        {
          "pagePrefix": "01",
          "surface": "storefront_hero",
          "similarity": 0.94,
          "signals": {
            "sharedComponentSet": 1,
            "nodeTreeEditDistance": 0.92,
            "layoutTopology": 0.93
          },
          "verdict": "near-identical"
        }
      ]
    }
  ],
  "flags": [
    {
      "a": "reference",
      "b": "candidate",
      "surface": "storefront_hero",
      "similarity": 0.94,
      "verdict": "near-identical"
    }
  ]
}

MCP and agent hosts can call this command directly and read the --out JSON file. The command does not require a plugin runtime and uses the same unauthenticated local inputs (--surfaces-file, optional --registry-file) plus the configured Figma token environment.

Kit Completeness Lint

Use tessellate curations lint before publish to fail fast when a numbered kit is incomplete or when a required surface has no mapped component match.

tessellate curations lint \
  <file-key> \
  --surfaces-file ./tessellate-surfaces.json \
  --registry-file ./component-mappings.json \
  --out ./curation-lint-report.json

The lint report includes:

  • status: passed or failed
  • missingSurfaces: required surface prefixes not found in the Figma file
  • unmappedSurfaces: required surfaces that do not contain a registry match
  • diagnostics: parse and validation findings

If --pages is omitted, both analysis commands derive the checked prefixes from --surfaces-file. Use --pages to narrow the analysis to a subset of the map.

Repo Setup

Repos that use curations should publish:

  • A source curation JSON per curation or a build job that produces one.
  • Per-curation token output.
  • Per-curation CSS variable output.
  • Curation-aware or compiled mapping output.
  • Preview manifests and assets.
  • A status or release note that tells consuming apps which curation slugs are ready for runtime use.

The curation contract is additive. Existing single-brand Tessellate users can continue using normal mapping registries, Surface context, and Inlay profiles.