Skip to main content
Glama

yardstick

Measure a web page from the DOM and compare the numbers with two or three sites you admire.

CI Python 3.12+ MIT MCP

A critique run: side-by-side table against stripe.com and linear.app, one contrast finding with a region

Agent-built pages look generic and nobody can say why in terms the agent can act on. "Make it feel more like Stripe" is not a fix. "Your h1 is 36px over 18px body, Stripe holds 3.0 and Linear 4.9, set the h1 to 60px" is. Yardstick produces the second kind of sentence, and it never calls a model: fonts, type scale, spacing grid, palette, contrast pairs, hero geometry, and layout come from computed styles in headless Chromium, and every standard comes from the references you name, not from anyone's memory of what good looks like.

yardstick critique http://localhost:3000 --vs stripe.com,linear.app

What it prints

A landing page I built with a coding agent, critiqued against stripe.com and linear.app, both captured live:

counts:
  sev3: 1
  sev2: 2
  sev1: 0
first_change: "K1: Darken the text or lighten the background until the pair reaches 4.5:1; keep the hue, move lightness"
table[23]{metric,mine,stripe.com,linear.app}:
  mode,light,light,dark
  heading font,Bricolage Grotesque,sohne-var,Inter Variable
  body font,Public Sans,sohne-var,Inter Variable
  h1 px,58,48,64
  body px,17,16,13
  h1/body,3.41,3,4.92
  h1 line-height,1.03,1.15,1
  body line-height,1.62,1.4,1.54
  measure ch,63.75,50.67,58.5
  on-grid %,39.5,86.2,80.6
  accent,#c4400a,#533afd,#e4f222
  gradient hero,false,false,true
  contrast fails,1,3,1
  radii,0/8/999,0/4/5/6/10,0/6/8/9/12/16/22/9999
  hero align,left,left,left
  hero CTAs,1,2,3
  ...
findings[3]:
  - id: K1
    severity: 3
    standard: "WCAG 1.4.3 needs 4.5:1 for body text and 3:1 for large text"
    gap: "#64584c on #16110d measures 2.72:1 (\"Illustrative ledger, shortened for the page. Every entry is \")"
    fix: "Darken the text or lighten the background until the pair reaches 4.5:1; keep the hue, move lightness"
    region[4]: 295,831,850,21
  - id: T2
    severity: 2
    standard: "Two families at most (stripe.com: 1, linear.app: 2)"
    gap: "3 families carry text: Public Sans, IBM Plex Mono, Bricolage Grotesque"
  - id: S1
    severity: 2
    standard: "References keep stripe.com 86%, linear.app 81% of spacing values on a 4px grid"
    gap: "Yours is 40% on-grid; frequent values are 18, 14, 20, 22, 12"
shots:
  mine: ~/.local/share/yardstick/shots/_critique/127.0.0.1-index/hero.png
  stripe.com: ~/.local/share/yardstick/shots/stripe.com/hero.png
  linear.app: ~/.local/share/yardstick/shots/linear.app/hero.png

The contrast finding is real: the italic caption under the dark ledger is 2.72:1, and the region points at it. A deliberately sloppy page (Inter everywhere, purple gradient hero, nine identical rounded cards, 95-character lines) draws 3 severity-3 and 6 severity-2 findings against the same two references; the two hand-made good pages in tests/fixtures draw no severity-3 against each other. That pair is the test suite's positive and negative check.

Every finding is the shape designers validated in the UICrit study: the expected standard, the gap, how to close it, the evidence, and a rectangle on your hero screenshot when it is about an element. The screenshots are on disk so the agent driving the tool can read them and do the subjective half itself.

Related MCP server: mcp-ux-vision

Install

uv tool install git+https://github.com/matthewvilaysack/yardstick   # or: uv build && uv tool install dist/*.whl
yardstick setup                                                       # installs headless Chromium once
yardstick seed                                                        # 1,160 domain pointers for `search`, optional

For hacking on it: git clone, uv sync, uv run pytest -q, and bin/yardstick runs from the checkout.

Claude Code can use the CLI as is, with the skill linked so /yardstick loads the loop, or the MCP shim, which returns the same text plus the hero images as image blocks:

ln -s ~/code/yardstick/skill ~/.claude/skills/yardstick
claude mcp add --transport stdio yardstick --scope user -- ~/.local/bin/yardstick mcp

Three ways to use it

  • CLI, for you and for coding agents that live in a terminal (the skill in skill/SKILL.md teaches Claude Code the loop).

  • MCP server, yardstick mcp, five tools (discover, search, show, measure, critique) for Claude Code, Cursor, Codex, or any MCP client; hero screenshots come back as images.

  • Local app, yardstick app, a one-screen interface on localhost: describe what you are building, pick the references it finds, run, read findings with the region outlined on your own screenshot, run again after fixes. History is kept on disk.

The local app: severity tiles, first change, findings beside the outlined crop

Finding references for an intent

yardstick discover "neighborhood dinner club that seats strangers together weekly" -n 5

Providers run in order and the first one to name a site wins: live web search (DuckDuckGo through ddgs, no key), the seeded catalog of 1,160 design-described domains, then any command you add. A command is how a subagent, an agent CLI, or a paid search API plugs in without touching this code: it gets the intent on stdin and prints a JSON list of {domain, why}.

# ~/.config/yardstick/providers.toml
[providers.claude]
command = "claude -p --output-format text 'Name 5 real websites whose design a $(cat) should learn from. Reply with a JSON list of {domain, why} only.'"
timeout = 120

--providers web,claude picks and orders them per call. --add captures every candidate as a reference in one go, so discover then critique --vs is the whole loop.

Commands

yardstick discover "<intent>" [-n 5] [--providers web,pointers,...] [--add]
yardstick app [--port 7878] [--no-open]
yardstick add <url> [--name n]          capture, measure, store a reference
yardstick list                          stored references
yardstick search "<text>" [-n 5]        references and seeded pointers, with the matched facets as "why"
yardstick show <name> [--full]          measurement and screenshot paths
yardstick measure <url>                 measure without storing (localhost works)
yardstick critique <url> --vs a,b[,c]   table, findings, first change, screenshot paths; --html <path> writes a standalone report
yardstick rm <name>
yardstick seed [--file README.md]       load domain pointers (MIT catalog from scroobius-pip/fudge-design-md)
yardstick setup [--dry-run]             install the headless browser
yardstick mcp                           stdio MCP server

Output is TOON; add --json after the subcommand for JSON. Errors are structured on stdout: exit 1 for usage, exit 2 when a capture fails, with the stage named.

Checks

id

fires when

K1

a text/background pair fails WCAG AA, verified against screenshot pixels; unverifiable pairs are counted, not reported

T4

body paragraphs run outside 40 to 80 characters per line

H2

no button-styled action above the fold while every reference has one

T1

h1/body ratio below 75% of the references' minimum or above 150% of their maximum

T2

more than two families carry text, or headings use Inter, Roboto, Arial, Helvetica, system-ui, Poppins when no reference does

T3

h1 line-height above 1.3, or body line-height outside 1.4 to 1.75

S1

share of spacing values on a 4px grid more than 20 points under the references' minimum

C1

gradient hero background when no reference has one

C2

chromatic hue count above the references' maximum plus two

P1

one radius on eight or more boxed elements while the references vary theirs

H1

h1 pixel size more than 15% outside the references' range

H3

dark versus light differs from every reference (informational)

There is no 0 to 10 score. Counts by severity are honest; a score without calibration data is not.

Layout

src/yardstick/walk.js        one page.evaluate: raw records per visible element (rect, text, computed type, colors, spacing, radius, shadow, parent)
src/yardstick/capture.py     Playwright: load, scroll to settle, remove cookie banners, run the walk, hero + full screenshots, sample pixels behind text
src/yardstick/measure.py     raw records -> measurement (pure Python, unit-tested without a browser)
src/yardstick/critique.py    measurement + references -> table, findings, first change
src/yardstick/store.py       SQLite with FTS5 over facets and pointers
src/yardstick/cli.py         subcommands, TOON output, exit codes
src/yardstick/mcp_server.py  five tools over the CLI, hero images as image blocks
src/yardstick/discover.py    reference discovery: web, catalog, and command providers (providers.toml)
src/yardstick/app.py + app.html   the local one-screen app over a small JSON API
src/yardstick/report.py      standalone HTML report with outlined crops
tests/fixtures/              editorial.html, dark.html (good, different), slop.html (deliberately bad)
docs/design.md               the design; docs/research.md the day of research it rests on

Data lives in ~/.local/share/yardstick/: refs.db and shots/<name>/. Captures are private on disk, nothing is redistributed, and yardstick rm is the takedown.

Tests

uv run pytest -q     # 86 passed; the browser tests take about 30 seconds

For teams and products

Everything runs on the machine that runs it: no accounts, no uploads, no model calls. If you sell websites built with AI, the MCP is the piece your agents call before and after they build, and the app is the piece your clients can look at. The MIT license lets you bundle it; the provider file lets you swap in your own search or your own agent as the discovery step.

Not in this version

Mobile and dark-scheme captures, embeddings, a numeric score, a hosted service, DESIGN.md or Tailwind export, font similarity. Each is a slice once the critique has been used on a few real pages.

Available Tools

5 tools
critiqueA

Critique a URL against comma-separated reference names or domains (domains are captured on the fly). Returns a side-by-side table, findings with standard/gap/fix/evidence/region, the first change to make, and hero images (yours first).

ParametersJSON Schema
NameRequiredDescriptionDefault
vsYes
urlYes

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full transparency burden. It discloses useful behavioral details, including that domains are captured on the fly and that hero images are returned 'yours first,' but it does not mention side effects, whether the URL is modified, or any prerequisites such as authentication.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single dense, well-structured sentence that front-loads the core action and then compacts the return format efficiently. No filler or redundant repetition of the schema is present.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Because there is no output schema, the description compensates by listing the main return components: side-by-side table, findings fields, first change, and hero images. It is complete enough for a simple two-parameter tool, though it omits error behavior and explicit exclusions.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It does: it clarifies that 'url' is the target URL and that 'vs' takes comma-separated reference names or domains. For a two-parameter tool, this is sufficient semantic guidance.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific action ('Critique'), a resource ('a URL'), and a target ('against comma-separated reference names or domains'), making the tool's purpose unambiguous. The detailed return-value summary further differentiates it from sibling tools like search, discover, show, and measure.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies when to use the tool: whenever a URL needs to be compared against reference names or domains. However, it does not explicitly state when not to use it or name alternative sibling tools for related tasks, leaving some routing decisions to the agent.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

discoverA

Find reference sites for what the user is building, e.g. 'neighborhood dinner club landing page' or 'fintech dashboard for accountants'. Providers run in order (local catalog, live web search, then any configured command such as a subagent); one entry per site with a short why. Pass the domains to critique as --vs.

ParametersJSON Schema
NameRequiredDescriptionDefault
nNo
intentYes
providersNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the behavioral burden. It usefully discloses that providers run in order (local catalog, live web search, then configured commands), returns one entry per site with a short rationale, and expects downstream usage with critique. It omits details like side effects or limits, but covers the main execution behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact, front-loaded with the core purpose, and every sentence adds useful information. It avoids filler and fits the tool's complexity well.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description gives a useful orientation and even references downstream critique usage, and an output schema exists. However, it leaves important gaps such as the meaning of 'n' and provider input format, and it does not explain how this tool relates to the sibling 'search' tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate for parameter meaning. It explains the 'intent' parameter well with examples and hints at 'providers' by describing provider ordering, but it says nothing about the 'n' parameter or how to format the providers value.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific verb and resource: 'Find reference sites for what the user is building,' with concrete examples. It conveys the tool's purpose clearly, though it does not explicitly distinguish itself from the sibling 'search' tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear context: use it to discover reference sites based on the user's building intent, and then pass the resulting domains to critique. It does not explicitly state when not to use it or contrast it with alternatives, so it stops short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

measureB

Measure any URL (localhost works) without storing it: fonts, type scale, spacing grid, palette, contrast, hero, layout.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYes

TDQS

B3.4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description must carry the behavioral burden. It does disclose a key trait: 'without storing it', which signals a privacy-conscious read operation, and 'localhost works' indicates local URL support. However, it omits other behaviors such as whether the URL is fetched live, how long measurement takes, or any rate limits. The disclosure is partial but not contradictory.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, front-loaded sentence. It leads with the core action ('Measure any URL'), immediately states the privacy trait, and lists the measured aspects in a compact enumeration. Every word contributes to the tool's utility; there is no redundancy or filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity (one parameter, no output schema, no annotations), the description is largely sufficient. It explains what the tool measures, notes that it does not store the URL, and supports localhost. It could clarify what the agent receives as a result (e.g., a report or scores), but that is not critical for invoking the tool. Overall, it covers the essential context for correct use.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description must explain the parameter. It states the tool measures 'any URL' and explicitly mentions localhost, giving the agent useful context about accepted values. It does not specify protocol requirements (e.g., HTTP/HTTPS) or encoding, but the single parameter is straightforward. The description adds minimal but adequate semantics for the URL parameter.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool measures any URL and enumerates specific design aspects (fonts, type scale, spacing grid, palette, contrast, hero, layout). The verb 'measure' is distinct from siblings like search, discover, show, and critique, though it does not explicitly name an alternative. The purpose is specific and unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. It does not mention any exclusion criteria, prerequisites, or situations where another sibling would be more appropriate. The usage is only implied by the action itself, which is insufficient for an agent to make a confident selection.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

showC

Measurement summary and hero screenshot of a stored reference.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYes

TDQS

C2.6/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden, yet it only lists outputs. It does not state that the operation is read-only, what happens if the name does not match a stored reference, or whether any cached data is regenerated. For a tool named show this is a notable gap.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The single sentence is compact and front-loads the key outputs. It earns its place, though a second sentence explaining the 'name' parameter would improve utility without harming concision.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter tool with no output schema and no annotations, the description should cover what 'name' refers to, whether the call is read-only, and what the response contains. It only partially covers the response (summary and screenshot) and omits the rest, leaving an agent to guess.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% and the description never explains the required 'name' property: what it refers to, how it maps to a stored reference, or expected format. The description therefore adds no semantic value for the only parameter.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description identifies a clear deliverable — a measurement summary and hero screenshot for a stored reference — which distinguishes it from search/discover/measure/critique sibling tools. It lacks an explicit verb like 'retrieve' or 'display' but the noun phrase conveys a display/read action well enough.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no guidance on when to call show versus a sibling, no prerequisites such as having measured the reference first, and no mention of alternatives. The phrase 'stored reference' implies the item must already exist, but that is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 5 tool updatesv0.2.0
    • First observedcritique
    • First observeddiscover
    • First observedmeasure
    • First observedsearch
    • First observedshow

TDQS

B3.4/5.0

Scored across 5 tools

Disambiguation4/5

Search and discover both return reference sites, but search targets stored items via text while discover finds new sites from a project description, so the intent is mostly clear. Show, measure, and critique are each distinct and unambiguous.

Naming Consistency5/5

All five tool names are single lowercase verbs following a consistent action-first pattern (search, discover, show, measure, critique), which is predictable and easy to follow.

Tool Count5/5

Five tools is well-scoped for a design reference, measurement, and critique server. Each tool represents a meaningful, distinct action without excess or redundancy.

Completeness3/5

The server covers searching, measuring, and critiquing reference sites, but there is no tool to store or manage new references, which leaves a notable lifecycle gap. Agents can work around it by using domains on the fly, but the stored-reference workflow feels incomplete.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    A
    maintenance
    Analyzes and extracts design tokens, assets, and layout from live websites to enable AI clients to faithfully replicate them, with tools for screenshotting, component inspection, and pixel-diff verification.
    14
    8 npm
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Audits any site's UX the way a design-literate reviewer would — contrast, tap targets, type scale, colour discipline, scan patterns, copy — and returns the rule, the source line and the exact fix.
    4
    Apache 2.0