Project log

Changelog

What shipped on UI Anatomy and when. Subscribe via the RSS feed.

  1. Versioning surface parked

    • schema
    • mcp
    • site

    The get_changelog MCP tool and the version/deprecation render were removed after 0/41 components used them. Schema fields stay dormant.

    The opt-in versioning metadata from 2026-05-01 (ADR-023) shipped end-to-end — schema fields, an MCP tool, and a render surface — but in the month since, no canonical component populated since, changelog, deprecated, or axes.variantDeprecations. Carrying an unexercised lifecycle costs every schema and review pass, so the live surface is parked.

    Removed. The get_changelog MCP tool (it returned null for all 41 components), and the render: the hero since pill, the view-agnostic changelog section, and the per-slot / per-variant / per-property deprecation badges in the anatomy and axes tables.

    Kept. The schema fields stay (optional, dormant), and ADR-023 plus the Versioning section of the schema docs remain the design record. The tool and render return the day a component lands its first real published change.

  2. SEO + AI-discovery hardening

    • seo
    • ai-discovery

    FAQPage JSON-LD, Schema.org Dataset, Organization sameAs, AI-bot allowlists in robots.txt, top-level DefinedTermSet, IndexNow ping at deploy, public /methodology page.

    A coordinated push to surface UI Anatomy to both classical search engines and AI answer engines (Perplexity, ChatGPT, Claude, Gemini).

    Citation surface. Every component page now emits a FAQPage graph alongside TechArticle JSON-LD, with question/answer pairs derived from the canonical whenToUse prose, related-component differentiators, and documented common mistakes. Modal alone exposes ten Q/A pairs that AI extractors can quote verbatim.

    Dataset positioning. The site root now declares a Dataset graph node with three DataDownload distributions — the JSON catalogue at /api/components.json, the full Markdown corpus at /llms-full.txt, and the index at /llms.txt. Authority signals ship via Organization.sameAs and Person.sameAs pointing at the public GitHub repo.

    Crawler differentiation. robots.txt keeps the duplicate-suppressing Disallow set for general crawlers but explicitly allows PerplexityBot, GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, Claude-Web, Google-Extended, and CCBot to crawl /api/, /llms.txt, /llms-full.txt, and the per-page Markdown sidecars — exactly the surfaces designed for AI ingestion.

    Ontology resolution. The DefinedTermSet is now a top-level graph node with hasDefinedTerm[] listing every canonical component, sorted alphabetically. Component pages reference the set via @id back-link instead of an inline stub, so the canon resolves as a controlled vocabulary rather than 23 disconnected definitions.

    Active discovery. Deploys now ping IndexNow with the full sitemap URL list, accelerating index updates on Bing, Yandex, Seznam, and Naver.

    E-E-A-T surface. /methodology is now a public page rendering docs/methodology.md directly — the same canonical prose that contributors read internally. Author and publisher are emitted as AboutPage + TechArticle JSON-LD with sameAs links.

  3. Opt-in versioning metadata

    • schema
    • mcp

    Component-level since + changelog. Per-slot, per-property since + deprecated. Sparse axes.variantDeprecations. New MCP get_changelog tool.

    The canonical schema gains lightweight versioning metadata so the canon can record additions and retirements honestly instead of through silent edits.

    Component-level fields. A component may declare since: <semver> to record the version it first entered the canon, plus changelog: Array<{ version, date, summary }> with unique versions. Both fields are optional and additive.

    Per-slot and per-property deprecation. anatomySlotSchema and both arms of propertySchema now accept since?: <semver> and deprecated?: { since, reason, replacement? }. The deprecation object is .strict(); reason is required prose, replacement is optional.

    Sparse variant deprecations. Variants stay as bare strings (no shape break across the 23-component roster). Deprecation metadata lives in a parallel axes.variantDeprecations: Array<{ name, since, reason, replacement? }> with a cross-field refine that validates each name against axes.variants and rejects duplicates.

    Render. Deprecation pills (warm-accent, line-through) appear on AnatomyTable, FigmaSlotTable, CodeSlotTable, and AxesTable. The component hero shows a since pill next to the title. A new view-agnostic ChangelogSection renders below the view content when either since or changelog is set.

    MCP. New get_changelog tool returns { since, changelog } | null. Tool count is now 19. The full ADR is at docs/adr/023-versioning.md.

  4. Side-by-side /compare view

    • site
    • ux

    Pure-derivational diff between two canonical components. Anatomy slots, variants, properties, states, schema sections, axe rules, vsRelated prose.

    Closes the “designer + DS-maintainer” gap from docs/personas.md: comparing two canonical components used to require flipping between two browser tabs.

    /compare?a=card&b=tile now renders an eight-section diff: required-slot deltas, variant set-diff, property kind classification, interactive + data state diff, schema-section presence matrix, axe-rule overlap, plus the canon-pinned vsRelated.difference prose when either side cross-references the other.

    The diff itself is pure-derivational — no schema change. shared/src/compare.ts exports computeCompareDiff(a, b) → ComponentDiff as a deterministic function. The page renders an initial pair server-side, then swaps the diff client-side via URLSearchParams + fetch('/api/components/<id>.json') calls. Anatomy SVGs link out to component pages instead of duplicating the build-time generator.

    Header gains a third icon (between view-switcher and search) for the new route.

  5. MCP get_implementations + list_implementations

    • mcp
    • phase-2

    Phase-2 library audit data now reachable through the MCP server. Three Modal audits today (Radix React, Headless UI Vue, Angular CDK Dialog).

    The MCP server can now answer “how does Radix’ Dialog diverge from canonical Modal?” without falling back to the JSON API.

    Two new tools land:

    • list_implementations returns one row per {libraryId, componentId} pair with divergenceCount + lastReviewed, sorted by library then component.
    • get_implementations(componentId) returns every library audit for that canonical component as full Implementation records — componentId, libraryId, componentName, exampleCode, divergence list, rationale, lastReviewed. Empty array when no library has audited the component yet.

    The implementations bundle ships at build time as shared/dist/implementations-bundle.json (~150KB across the three current Modal audits). Worker cold-start re-validates it through the same Zod schema the site uses, so the MCP and HTML pages can never disagree on shape.

    Tool count moves to 17. New docs/personas.md documents the four user personas (designer / developer / DS maintainer / AI agent) and tracks gap-closure across feature work going forward.

  6. SEO baseline — JSON-LD, canonical links, sitemap freshness

    • seo

    Per-page TechArticle structured data, view-aware titles + descriptions, sitemap lastmod from canonical lastReviewed dates, robots.txt indexing hygiene, trailing-slash convention.

    A three-phase baseline so search engines see UI Anatomy as the same content from multiple angles, not three near-duplicate pages per component.

    JSON-LD across the site. New site/src/lib/jsonLd.ts builds WebSite + Organization + WebAPI + SoftwareApplication for the home graph and TechArticle per component, with view-aware headlines, breadcrumbs, and DefinedTerm mainEntity. Base.astro emits <link rel="canonical">, og:site_name, og:locale, og:url, article:modified_time, and the JSON-LD script.

    Sitemap freshness. Sitemap entries now carry per-component lastmod from canonical lastReviewed, not the build clock. Different components can show different freshness signals.

    Robots hygiene. /api/, /og/, /pagefind/, /llms.txt, /llms-full.txt, and *.md$ are disallowed from general indexing — they’re duplicates of the HTML content from a search-engine perspective and would dilute ranking signal. AI agents reach them anyway via Content-Signal: ai-input=yes.

    Trailing-slash discipline. Switched Workers Static Assets to drop-trailing-slash so /components/modal serves directly with 200 instead of redirecting through /components/modal/. Canonical link form, sitemap, and JSON-LD all agree on the no-slash form.

  7. MCP validate_implementation tool

    • mcp
    • agent

    Heuristic structural conformance check for AI-generated UI code against canonical anatomy / axes / events. Framework-aware event detection.

    Closes the developer + AI-agent gap from docs/personas.md: agents now have a way to self-verify generated UI code against the canon without standing up a full test runner.

    validate_implementation({ componentId, code, framework }) does framework-aware substring detection — on<PascalCase> for React, @event / v-on: / emit('event') for Vue, (event) for Angular, bare names for web components — and reports which canonical required slots, variants, properties, and events appear in the supplied code (and which are missing).

    This is not a substitute for behavioural assertions. The tool ships with an explicit caveat in its schema: substring search produces false negatives on aliased or minified identifiers. Pair with the per-component a11y-fixture endpoint and a real Playwright + axe-core run.

    Tool count moves to 18. New backing utility at shared/src/validate.ts exported via @uianatomy/shared/validate.

  8. Cloudflare Workers Static Assets migration

    • hosting

    Pages auto-deploy started failing on a converging Pages-config surface. Direct migration to Workers Static Assets via wrangler.jsonc + a single Worker handler.

    Cloudflare Pages began rejecting our auto-deploys with a wrangler-suggestion-template error after recent feature commits. The platform is converging Pages onto Workers Static Assets; rather than chase a moving Pages-config surface, we moved directly to the destination.

    wrangler.jsonc at the repo root now declares the project (name, compatibility_date, compatibility_flags: ["nodejs_compat"], assets: { directory: ./site/dist, binding: ASSETS, run_worker_first: true, html_handling: drop-trailing-slash, not_found_handling: 404-page }).

    worker/index.ts consolidates the previous two functions/ files into a single dispatch handler — /mcp → MCP Streamable HTTP transport; otherwise check Accept: text/markdown and serve the .md sidecar with x-markdown-tokens + Vary: Accept; otherwise pass through to env.ASSETS.fetch(request).

    run_worker_first: true is required so the Worker can inspect the Accept header on requests for matched assets — without it, content negotiation can’t see HTML page requests.

    Wrangler bumped 3.114 → 4.87. Custom 404 page renders for unmatched URLs with a Levenshtein-distance “did you mean?” suggestion when the path looks like a typoed component slug.