Skip to content

Give the components the knobs they were pretending to have - #34

Open
pablostanley wants to merge 2 commits into
mainfrom
worktree-inspector-variants
Open

Give the components the knobs they were pretending to have#34
pablostanley wants to merge 2 commits into
mainfrom
worktree-inspector-variants

Conversation

@pablostanley

@pablostanley pablostanley commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Started from one bug: pick Trend "down" on a stat card and you get a down arrow beside +12.5% with a sparkline still climbing. Two audit rounds later, that turned out to be a pattern rather than an incident.

Two commits. 119 controls added, 4 removed. 150 components audited twice.


Round one — controls that lied

234 findings across two audits, each put through an adversarial verify pass before it was allowed to matter.

Controls that did nothing. profile-screen offered 2–9 cards and drew 3 — its own default of 6 was inert. gallery went to 9 tiles and silently dropped any that overflowed. file-upload pinned Files to 0 unless a second control was set just so. Four more ranges whose top values were identical to a lower one.

Components arguing with themselves. cart's header read "3 things" beside an Items control set to anything else. kanban-board badges fixed at 6/3/9/1, wrong even at the default. order-summary showed one $24.00 line above a $48.00 total. usage-meter said "14 of 20 used" whatever you set Value to.

Copy with no way in. Every navbar CTA said "Sign up", every footer said "squig", both hero buttons and the chart title were literals.

Options too thin. contact-form-block's "Split layout" was a yes/no select whose default was the boolean true, so it rendered blank. feature-grid and pricing-block each marked four controls quick when the context row hard-slices at three.

Trend, specifically

Trend Arrow Delta Sparkline
up +12.5% rises
down −12.5% falls
flat 12.5% flat wobble

An unsigned delta picks up the trend's sign — the default is 12.5% now — but a sign you typed yourself is left alone. Arguing with someone's own input is worse than the contradiction.


Round two — what the first pass missed, and what it broke

The big screens couldn't say anything. landing-page is 1000×830 and its 34px headline was a literal, so every landing page said the same sentence. app-shell had three chrome toggles and no way to name the page you were on. pricing shipped two count knobs against frozen arrays of plan names and prices. chat-screen was permanently a conversation with Maya.

The first sweep created its own inconsistencies. Seven marketing blocks got a quick heading toggle and no text control, so you could switch the heading on and off and were stuck with the joke it shipped with. profile-header gained a name control, but profile-screen composed it and dropped the prop, so the person the screen is about was uneditable one level up. "Heading" was a toggle in ten defs and a text box in four. A button's text field went by three different names. spinner labelled its toggle "Label" and its text field "Text" — exactly backwards from the six defs it should match.

Blocks still contradicting themselves: onboarding-checklist's progress readout was off by one against its own Current step; product-grid drew stars unconditionally while its sibling gated them; command-palette pinned the highlight to row 0.

Number controls accepted fractions. The field parsed 2.5 and only clamped to min/max, and defs index arrays and divide grids by that raw value — so a typed fraction wasn't a smaller count, it was a broken layout. All 70 number controls are counts, indices or percents, so this rounds once at commit in variant-controls.tsx, covering both the panel and the quick row while geometry fields keep their decimals.


Saved documents keep rendering the same

Nodes freeze {...def.defaults} into props at drop time, so an old node never gains a newly-added key. Every added prop falls back to the literal it replaced.

Verified mechanically rather than by eye: both versions of every def rendered across the old prop set at several scales and the prims diffed. No prop key was renamed — relabelling is safe because labels aren't persisted, which is why most of the consistency fixes cost nothing. Four controls were removed (divider.showLabel, file-upload.variant, accordion.count, button-group.count); each stays readable in render(), so existing nodes are unaffected.

New: scripts/test-defs.ts

Every def at 60/100/150%, asserting it doesn't throw, draws something, has no NaN, stays inside its own box, keeps every control key in defaults, keeps select defaults among their options, and respects the three-quick-control cap. 1200 checks over 150 components, wired into pnpm test.

It immediately caught five defs that had been spilling content out of the box when scaled down — login by 130px, signup by 84px — all fixed here. frame is exempted: its name hangs above the artboard on purpose, the way Figma does.

Verification

  • pnpm lint clean, pnpm test 1305 checks pass, pnpm build succeeds
  • Trend verified interactively across all three states, including that a pre-existing canvas node kept its explicit +12.5% while correctly flipping its sparkline
  • Editable plan names and the fraction fix verified live: typing 2.4 into Plans commits as 2
  • Kitchen sink eyeballed across components, blocks and screens
  • Zero file overlap with the three commits that landed on main meanwhile

One thing I left alone

logo-cloud renders "Teams that draw badly on purpose", which sits awkwardly against framing squig as low-fidelity rather than bad. It predates this branch, so it wasn't mine to change — say the word.

🤖 Generated with Claude Code

The stat card's Trend was the tell: pick "down" and you got a down arrow
next to "+12.5%" and a sparkline still climbing. The control wasn't dead
exactly — it moved a 12px glyph — it just didn't touch either of the two
things that carry the meaning. An audit of all 150 defs found the same
shape of problem everywhere.

Four kinds of broken, now fixed:

Controls that did nothing. Ranges whose top half was inert — profile-screen
offered 2-9 cards and drew 3, gallery dropped any tile that overflowed,
file-upload pinned Files to 0 unless a second control was set just so. Each
one either reflows to honour the range or admits the honest maximum.

Components arguing with themselves. A cart header reading "3 things" beside
an Items control set to 5, kanban badges fixed at 6/3/9/1, an order summary
whose total ignored its line items, a usage meter forever "14 of 20 used".
These now derive from the prop they used to contradict.

Copy with no way in. Every navbar CTA said "Sign up", every footer said
"squig", both hero buttons and the chart title were literals. 71 new
controls, mostly text fields over strings that were previously unreachable.

Options too thin to matter. contact-form-block's "Split layout" was a
yes/no select defaulting to boolean true, so it rendered blank.
feature-grid and pricing-block each marked four controls quick when the
context row hard-slices at three, so the fourth silently vanished.

On Trend specifically: it now drives the arrow, the sparkline's slope, and
the delta's sign. An unsigned delta picks up the trend's sign — the default
is "12.5%" now — but a sign you typed yourself is left alone, because
arguing with someone's own input is worse than the contradiction.

Saved documents keep rendering the same. Nodes freeze {...def.defaults}
into props at drop time, so an old node never gains a new key; every added
prop falls back to the literal it replaced. Verified by rendering both
versions of each def across the old prop set and diffing prims.

Adds scripts/test-defs.ts: every def at 60/100/150%, asserting it doesn't
throw, draws something, has no NaN, stays inside its own box, keeps every
control key in defaults, keeps select defaults among their options, and
respects the three-quick-control cap. 1200 checks over 150 components. It
caught five defs that had been spilling content out of the box when scaled
down — login by 130px — which are also fixed here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
squig Ready Ready Preview Jul 30, 2026 10:28pm

Request Review

Second pass over the library, aimed at what the first one missed.

The screens had nothing to say. landing-page is the biggest thing you can
drop — 1000x830 — and its 34px headline was a string literal, so every
landing page anyone wireframed said the same sentence. app-shell had three
chrome toggles and no way to name the page you were on. pricing shipped two
count knobs against frozen arrays of plan names and prices, which is
everything a pricing table exists to say. chat-screen was permanently a
conversation with Maya. inbox-screen's 20px subject was the largest type on
the screen and hardcoded.

The first sweep made its own inconsistencies. Seven marketing blocks got a
quick heading toggle and no text control, so you could switch the heading
on and off and were stuck with the joke it shipped with. profile-header
gained a name control, but profile-screen composed it and dropped the prop,
so the person the screen is about was uneditable one level up. "Heading"
was a toggle in ten defs and a text box in four. A button's text field went
by three different names. spinner labelled its toggle "Label" and its text
field "Text", exactly backwards from the six defs it should match.

Blocks that still argued with themselves: onboarding-checklist's progress
readout was off by one against its own Current step, product-grid drew
stars unconditionally while its sibling gated them, command-palette pinned
the highlight to row 0, payment-methods to card 0.

Number controls accepted fractions. The field parsed "2.5" and only clamped
to min/max, and defs index arrays and divide grids by that raw value, so a
typed fraction wasn't a smaller count, it was a broken layout. All 70
number controls are counts, indices or percents, so this rounds once at
commit in variant-controls.tsx, covering the panel and the quick row while
geometry fields keep their decimals.

48 controls added, one removed: button-group's Segments, which fought its
own comma-separated label list — accordion lost the same fight last commit,
so it now follows the same rule. Saved documents still honour a frozen
count.

Saved documents keep rendering the same, verified by diffing every def's
prims against the previous commit across the old prop set rather than by
eye. No prop key was renamed; relabelling is safe because labels aren't
persisted, which is why most of the consistency fixes cost nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@pablostanley
pablostanley marked this pull request as ready for review July 30, 2026 22:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant