Skip to content

Fix missing form-control color tokens in the ESM build - #1417

Merged
stefankp merged 2 commits into
mainfrom
stefankp/brand-esm-control-tokens
Jul 23, 2026
Merged

Fix missing form-control color tokens in the ESM build#1417
stefankp merged 2 commits into
mainfrom
stefankp/brand-esm-control-tokens

Conversation

@stefankp

@stefankp stefankp commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Summary

The @primer/react-brand/esm build never shipped the --brand-control-* color token family, so pure-ESM consumers that drop the global lib/css/main.css lose every form-control border and box: TextInput / Select / Textarea borders disappear and Checkbox / Radio render invisible (this is what broke the ContactSales form on github.com/enterprise/contact).

The control color tokens reached CSS only through a single side-effect import in the forms/index.ts re-export barrel. The ESM build (Vite + rolldown, preserveModules) rewrites the package entry to import the leaf component modules directly and never emits esm/forms/index.js, so that barrel's CSS side-effect — and the tokens with it — silently vanished from the ESM output. Every other component (Button, Card, Label, …) already co-locates its own colors-with-modes.css import inside the component, so its tokens ride along with the component module; the forms family was the lone exception relying on the barrel.

This moves the control-token import into each of the six form components to match that established per-component pattern, and drops the now-redundant barrel import. lib/css/main.css (the monolithic CSS-class build) is unaffected.

Both released 0.70.0 and 0.71.0 ship the ESM build without these tokens — no released version has ever shipped working ESM control tokens — so consumers on the ESM entry will need to upgrade to the release containing this fix.

List of notable changes:

  • added the functional/components/control/colors-with-modes.css import to TextInput, Textarea, Select, Checkbox, Radio and FormControl, so the --brand-control-* color tokens ship with the components in the ESM build (mirrors the existing Button pattern)
  • removed the shared control-token import from forms/index.ts, since the ESM build drops that re-export barrel and it therefore never delivered the tokens to ESM consumers

What should reviewers focus on?

  • That the co-located import matches the existing per-component convention (e.g. Button.tsx), rather than the barrel-level import it replaces.
  • That lib/css/main.css is unchanged in content — the fix only changes what the ESM build emits (the CSS-class build was already self-contained).
  • That the control sizing/animation tokens (--brand-control-large-*, --brand-control-medium-*, --brand-control-animation-*), which already shipped in ESM, are not regressed.

Steps to test:

  1. Build the library: npm run build:lib.
  2. Inspect packages/react/esm/: each of the six form component .js files now side-effect-imports a functional/components/control/colors-with-modes-*.css, and that token CSS is emitted into esm/ (previously it was absent entirely).
  3. Run the reference-vs-definition check over the built esm/**/*.css: 0 --brand-control-* tokens are now used without a definition (was 32).
  4. Or reproduce as a consumer: import FormControl / TextInput / Select / Checkbox / Radio from @primer/react-brand/esm in a bundler without importing main.css. Controls now render with borders/boxes; before this fix the borders collapsed and checkboxes/radios were invisible.
Reference-vs-definition check (run from packages/react, prints 0 after the fix)
node -e "const fs=require('fs'),p=require('path'),f=[];(function w(d){for(const e of fs.readdirSync(d,{withFileTypes:1})){const q=p.join(d,e.name);e.isDirectory()?w(q):e.name.endsWith('.css')&&f.push(q)}})('esm');const D=/--(?:brand|base)-[\w-]+\s*:/g,R=/var\(\s*(--(?:brand|base)-[\w-]+)\s*([,)])/g,defs=new Set(),refs=new Map();for(const x of f){const s=fs.readFileSync(x,'utf8');let m;while(m=D.exec(s))defs.add(m[0].replace(/\s*:$/,''));while(m=R.exec(s)){const t=m[1];if(!refs.has(t))refs.set(t,0);if(m[2]===')')refs.set(t,1)}}console.log([...refs].filter(([t,n])=>n&&!defs.has(t)&&t.includes('control')).length)"

Supporting resources (related issues, external links, etc):

  • Affects released @primer/react-brand@0.70.0 and @0.71.0 — both ship the ESM build without the --brand-control-* color tokens. Consumers using the ESM entry should upgrade once this ships.

Contributor checklist:

  • All new and existing CI checks pass (tsc, ESLint with 0 warnings, Prettier, jest src/forms — 82 tests)
  • Tests prove that the feature works and covers both happy and unhappy paths — N/A: this is a build/packaging fix with no unit-test surface; validated against the built esm/ output (reference check + an ESM-only consumer harness)
  • Any drop in coverage, breaking changes or regressions have been documented above
  • UI Changes contain new visual snapshots — N/A: no component markup/style changes; main.css is unchanged
  • All developer debugging and non-functional logging has been removed
  • Related issues have been referenced in the PR description — N/A

Reviewer checklist:

  • Check that pull request and proposed changes adhere to our contribution guidelines and code of conduct
  • Check that tests prove the feature works and covers both happy and unhappy paths
  • Check that there aren't other open Pull Requests for the same update/change

Screenshots:

Same ContactSales-style form rendered from @primer/react-brand/esm with no main.css, production-bundled.

Before After
Before After

The `--brand-control-*` color tokens only reached CSS through a shared
side-effect import in the forms barrel (`src/forms/index.ts`), which the
ESM build's tree-shaking dropped. As a result, consumers importing from
`@primer/react-brand/esm` without the global `lib/css/main.css` lost all
form-control borders and boxes (checkboxes and radios rendered invisible).

Co-locate the control `colors-with-modes.css` import in each form
component (TextInput, Textarea, Select, Checkbox, Radio, FormControl),
matching the established Button pattern, so the tokens ride along in the
component modules that are unavoidably included when the components are
used. Remove the now-redundant barrel import.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: afd2ea59-8442-4214-bd4b-ecc92d8987a5
@changeset-bot

changeset-bot Bot commented Jul 22, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5ebb629

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

This PR includes changesets to release 9 packages
Name Type
@primer/react-brand Patch
@primer/brand-docs Patch
@primer/brand-css Patch
@primer/brand-primitives Patch
@primer/brand-e2e Patch
@primer/brand-fonts Patch
@primer/brand-mcp Patch
@primer/brand-config Patch
@primer/brand-storybook 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

@github-actions

Copy link
Copy Markdown
Contributor

🟢 No design token changes found

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

🟢 Unit test coverage changes found

Unit test coverage has been updated through this PR.

Changes: 0 new tests, 0 removed tests, 2 improved, 0 decreased

Component/Hook Statements Functions Branches Change
Radio 84.6% 85.7% 50.0% 50.0% +1.1%
FormControl 93.2% 93.3% 100.0% 84.9% +0.1%

@github-actions

Copy link
Copy Markdown
Contributor

🟢 Bundle size report

CheckMainBranchChange
UMD — full bundle (JS)101.39 kB101.39 kB🟢 No change
UMD — full bundle (CSS)66.97 kB66.98 kB⬆️ +8 B (+0.0%)
ESM — full bundle (JS + CSS)1.51 MB1.52 MB⬆️ +4.48 kB (+0.3%)
ESM — tree-shaken simple (Button)69.94 kB70.28 kB⬆️ +354 B (+0.5%)
ESM — tree-shaken complex (ActionMenu)78.83 kB79.17 kB⬆️ +354 B (+0.4%)

@stefankp
stefankp marked this pull request as ready for review July 22, 2026 10:41
@stefankp
stefankp requested a review from a team as a code owner July 22, 2026 10:41
Copilot AI review requested due to automatic review settings July 22, 2026 10:41
@github-actions

Copy link
Copy Markdown
Contributor

🟢 No visual differences found

Our visual comparison tests did not find any differences in the UI.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Fixes a packaging gap in @primer/react-brand/esm where the shared --brand-control-* color token CSS was not being emitted for ESM consumers (causing form controls to render without borders/boxes when lib/css/main.css is not loaded). The change aligns forms with the existing per-component “co-located token import” pattern used elsewhere in the library (e.g. Button).

Changes:

  • Co-locate the functional/components/control/colors-with-modes.css side-effect import into each of: TextInput, Textarea, Select, Checkbox, Radio, and FormControl.
  • Remove the now-ineffective shared side-effect import from packages/react/src/forms/index.ts (which the ESM build does not reliably emit as a preserved module).
  • Add a changeset documenting the ESM-only fix and impacted released versions.
Show a summary per file
File Description
packages/react/src/forms/TextInput/TextInput.tsx Adds co-located control color token CSS import so ESM TextInput carries required tokens.
packages/react/src/forms/Textarea/Textarea.tsx Adds co-located control color token CSS import for ESM Textarea token availability.
packages/react/src/forms/Select/Select.tsx Adds co-located control color token CSS import for ESM Select token availability.
packages/react/src/forms/Checkbox/Checkbox.tsx Adds co-located control color token CSS import for ESM Checkbox token availability.
packages/react/src/forms/Radio/Radio.tsx Adds co-located control color token CSS import for ESM Radio token availability.
packages/react/src/forms/FormControl/FormControl.tsx Adds co-located control color token CSS import for ESM FormControl token availability.
packages/react/src/forms/index.ts Removes barrel-level side-effect import that didn’t survive ESM output.
.changeset/fix-esm-control-color-tokens.md Documents the patch release and the ESM-only consumer impact/upgrade note.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 0
  • Review effort level: Low

Comment thread .changeset/fix-esm-control-color-tokens.md Outdated

@rezrah rezrah left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM. One change request for the changelog. Thanks for fixing this 🙇.

Co-authored-by: Rez <13340707+rezrah@users.noreply.github.com>
@stefankp
stefankp merged commit 1d59236 into main Jul 23, 2026
17 checks passed
@stefankp
stefankp deleted the stefankp/brand-esm-control-tokens branch July 23, 2026 10:03
@primer primer Bot mentioned this pull request Jul 22, 2026
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.

3 participants