Skip to content

feat(mantle): unified size prop for the button family + size/intent decision record - #1315

Merged
cody-dot-js merged 4 commits into
mainfrom
button-size-decision-record
Jul 14, 2026
Merged

feat(mantle): unified size prop for the button family + size/intent decision record#1315
cody-dot-js merged 4 commits into
mainfrom
button-size-decision-record

Conversation

@cody-dot-js

@cody-dot-js cody-dot-js commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

One PR for the first two stages of the button refresh (prior art: #1105 — design direction kept, diff not salvaged; it was 151 commits stale and predates the docs IA reorg, optional-type, data-slot, and the button test suites).

1. Decision record (decisions/2026-07-13-button-size-and-intent-api.md)

  • Phase 1 (this PR): unified size scale. One ButtonSize scale shared by Button and IconButton; SplitButton.Root owns size for its halves; typography and icon glyphs deliberately do not scale for now (explicit follow-ups).
  • Phase 2 (accepted, ships separately): priorityintent. Button values become accent | danger | neutral; appearance and intent become required (no defaults) — replacing the POC's silent accent→neutral default flip with deterministic compile errors; subtle must meet ≥ 4.5:1 contrast in both themes before shipping (POC's light theme fails: accent ≈ 3.6–4.1:1, danger ≈ 3.7–3.9:1 from the actual oklch tokens); Badge keeps color; no deprecation aliases — hard cutover via a codemod-style migration prompt driven by the record's explicit priority → (intent, appearance) mapping table; toast PriorityToastIntent; data-prioritydata-intent.
  • Informed by measured blast radius in ngrok's frontend: ~1,035 priority= occurrences across 336 files; ~176 un-annotated <Button>s that would have silently restyled under a default flip.

2. size prop implementation (patch, purely additive)

size Button IconButton
xs h-6 px-2 size-6
sm h-7 px-2.5 size-7
md (default) h-9 px-3 size-9
lg h-10 px-3.5 size-10
xl h-12 px-4 size-12
  • Button: size applies via compound variants gated to filled | ghost | outlinedappearance="link" is untouched (no box classes, no typography change, no data-size), structurally avoiding the POC's text-sm font-medium leak onto link buttons. Icon-side padding generalizes the existing −0.125rem rule per size. New ButtonSize type exported from @ngrok/mantle/button.
  • IconButton: gains lg/xl on the shared scale; now always emits data-size (previously absent when the prop was omitted).
  • SplitButton: size on Root, driven through context to both halves; the parts Omit it (pinned by a @ts-expect-error test), so mixed-height composites aren't expressible in the type system.

Verification

  • lint, fmt:check, typecheck, and the full test suite pass (including the agent-surface drift test — no snapshot regeneration needed).

  • Adversarially reviewed: a 480-case old-vs-new render matrix (appearance × priority × isLoading × icon placement × className overrides) confirmed rendering is identical at the default md — no visual change for any existing consumer.

  • New tests pin the size contract: per-size height + padding, link exclusion (box, typography, data-size), icon padding (start/end/default), consumer className overrides still winning, asChild forwarding, and SplitButton plumbing across all five sizes.

  • Docs: new "Common mistakes" section on the Button page — icon-only buttons must be IconButton (accessible label, square box), not Button with an icon and no children.

Copilot AI review requested due to automatic review settings July 14, 2026 01:19
@changeset-bot

changeset-bot Bot commented Jul 14, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: df57b89

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@ngrok/mantle Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@ngrok-ship

ngrok-ship Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

🚀 Deploy Previews

Updated 2026-07-14 01:53 UTC

App URL Commit Status
ngrok-mantle https://mantle-1315.ngrok-previews.ngrok.app df57b89 🟢 Running

@cody-dot-js cody-dot-js changed the title docs: decision record — unified button size scale and the priority→intent redesign feat(mantle): unified size prop for the button family + size/intent decision record Jul 14, 2026
@cody-dot-js cody-dot-js self-assigned this Jul 14, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a unified, shared size scale across the button family and documents the associated design decisions (including the later priority → intent redesign plan), updating both implementation and docs in Mantle.

Changes:

  • Introduces ButtonSize (xs | sm | md | lg | xl) and applies it to Button, IconButton, and SplitButton.Root (with SplitButton enforcing a single size across both halves via context).
  • Updates styling/DOM attributes so size affects box geometry (not typography), and is intentionally ignored for Button with appearance="link" (no data-size emitted).
  • Adds/updates unit tests and documentation pages to cover the new size behavior and API surface.

Conventions pass: Confirmed class composition uses cx, size is modeled as a shared exported type, and SplitButton prevents mixed-size composites by omitting size from part props.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/mantle/src/components/split-button/split-button.tsx Adds size on SplitButton.Root and shares it via context to both halves.
packages/mantle/src/components/split-button/split-button.test.tsx Adds coverage ensuring Root size drives both halves and parts reject size at the type level.
packages/mantle/src/components/button/sizes.ts Introduces shared ButtonSize type and documents the unified scale.
packages/mantle/src/components/button/index.ts Re-exports ButtonSize from the @ngrok/mantle/button entrypoint.
packages/mantle/src/components/button/icon-button.tsx Extends IconButton size variants to lg/xl and defaults size to md.
packages/mantle/src/components/button/icon-button.test.tsx Adds IconButton size coverage, including asChild forwarding.
packages/mantle/src/components/button/button.tsx Adds size variant, applies size-driven height/padding (excluding link), and generalizes icon-side padding per size.
packages/mantle/src/components/button/button.test.tsx Adds Button size coverage, including link non-effect and override precedence.
decisions/2026-07-13-button-size-and-intent-api.md Records the size and intent decisions, constraints, and migration plan.
apps/www/app/docs/components/actions/split-button.mdx Documents SplitButton sizing and updates prop tables accordingly.
apps/www/app/docs/components/actions/icon-button.mdx Documents IconButton’s expanded size scale and examples.
apps/www/app/docs/components/actions/button.mdx Documents Button sizing behavior, including appearance="link" exclusion.
.changeset/button-family-size-prop.md Publishes the API addition via changeset.

Comment on lines +7 to +10
- **Phase 1 (`size`)**: implemented alongside this record; ships in the next `@ngrok/mantle` minor
(decoupled from the intent redesign so consumers can adopt `size` and `CenteredLayout` first).
- **Phase 2 (`intent` redesign)**: accepted, not yet implemented; ships in a later minor with its
own changeset and migration prompt.
@cody-dot-js
cody-dot-js merged commit f9445f5 into main Jul 14, 2026
12 checks passed
@cody-dot-js
cody-dot-js deleted the button-size-decision-record branch July 14, 2026 02:32
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.

2 participants