chore(Checkbox): migrate to CSS Modules with visual regression baseline#1052
chore(Checkbox): migrate to CSS Modules with visual regression baseline#1052DreaminDani wants to merge 4 commits into
Conversation
🦋 Changeset detectedLatest commit: 3a9fd2d The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
`window.getComputedStyle(checkbox).cursor === 'not-allowed'` relied on the styled-components runtime injecting a <style> tag that jsdom could read. CSS Modules import the stylesheet via Vite at build time and jsdom does not compute its rules, so the assertion now reads 'default'. Replace the brittle computed-style probe with the semantic intent of the test: the rendered button is `disabled` and Radix's `data-disabled` attribute is present. The visual disabled cursor is still covered byte-for-byte by the Playwright snapshots in `tests/forms/checkbox.spec.ts`.
There was a problem hiding this comment.
Pull request overview
This PR migrates the Checkbox component styling from styled-components to CSS Modules while adding Storybook stories and Playwright visual regression coverage to lock in byte-for-byte rendering parity across themes, variants, and states.
Changes:
- Added a Playwright visual regression suite for Checkbox (light/dark themes, variants, states, interaction snapshots) plus basic accessibility checks.
- Migrated
Checkboxstyles fromstyled-componentstoCheckbox.module.css, wiring classes viacva/cn. - Expanded Storybook stories to cover Checkbox variants, states, and layout permutations, and added a patch changeset entry.
Reviewed changes
Copilot reviewed 6 out of 35 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
tests/forms/checkbox.spec.ts |
Adds Checkbox visual regression snapshots + basic a11y behavior checks. |
src/components/Checkbox/Checkbox.tsx |
Switches Checkbox styling from styled-components to CSS Modules (cva/cn) and updates DOM styling hooks. |
src/components/Checkbox/Checkbox.stories.tsx |
Adds dedicated stories for Checkbox states/variants/layout used by visual tests. |
src/components/Checkbox/Checkbox.module.css |
New CSS Module implementing former styled-components rules + specificity override for FormRoot. |
.changeset/migrate-checkbox-to-css-modules.md |
Records the migration as a patch (no intended behavior change). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| data-testid="checkbox" | ||
| variant={variant} | ||
| disabled={disabled} | ||
| aria-label={`${label}`} |
There was a problem hiding this comment.
Preserving this exactly as-is in the migration: the original styled-components Checkbox had the same aria-label={\${label}`}` template-string. Per the migration project's scope rule, ARIA refinements are kept out of the CSS Modules PR so the visual-regression byte-for-byte guarantee holds. Filed as a follow-up
| export const NoLabel: Story = { | ||
| args: { | ||
| label: 'Accept terms and conditions', | ||
| label: undefined, | ||
| }, | ||
| }; |
There was a problem hiding this comment.
The NoLabel story is intentionally exercising the current (broken) rendering so the visual-regression snapshot captures it. The underlying aria-label="undefined" bug is a pre-existing styled-components behavior the migration preserves; fixing the story without first fixing the component would change what the snapshot tests. The fix will land separately and the story can be updated alongside it.
| it('disabled checkbox is not focusable via Tab', async ({ page }) => { | ||
| await page.goto(getStoryUrl('forms-checkbox--disabled', 'light'), { | ||
| waitUntil: 'networkidle', | ||
| }); | ||
| const checkbox = page.getByRole('checkbox'); | ||
| await expect(checkbox).toBeDisabled(); | ||
| }); |
There was a problem hiding this comment.
Good catch — fixed in the follow-up commit by adding the actual Tab navigation assertion (page.keyboard.press('Tab') → expect(checkbox).not.toBeFocused()).
The "disabled checkbox is not focusable via Tab" test had a misleading name — it only checked `toBeDisabled()` without exercising Tab. Add the Tab keypress and assert the checkbox does not receive focus so the test matches its stated behavior.
Storybook Preview Deployed✅ Preview URL: https://click-2z3d9a7ps-clickhouse.vercel.app Built from commit: |
Commits
test(Checkbox): add visual regression baseline before CSS Modules migration— adds stories, Playwright spec, and fresh PNG snapshots that capture the current rendering.chore(Checkbox): migrate styling from styled-components to CSS Modules— replaces styled-components with.module.css+cva/cn. DOM-identical, byte-for-byte visual parity verified.Verification
yarn test:visual tests/forms/checkbox.spec.tspasses with zero snapshot regenerations (33 tests across light/dark themes, 7 color variants, indeterminate/checked/disabled states, hover/focus, keyboard activation, and label-click toggling)yarn lint:css,yarn lint:code,yarn buildall greengrep -r "from 'styled-components'" src/components/Checkbox/emptyNotes
tests/forms/directory: Checkbox is the first form-input migration; the directory mirrors the StorybookForms/*category and gives Switch / RadioGroup / other form inputs a home as they migrate..wrapper.wrapper) so it reliably overrides the underlyingFormRootstyled-components rules (align-items: flex-start) regardless of stylesheet insertion order between static CSS Modules and runtime-injected styled-components. Without this boost, the screenshots drift by 1px because the wrapper falls back to flex-start.Closes CUI-17
Note
Low Risk
Presentation-only refactor on a single form control, guarded by broad visual and a11y tests; wrapper specificity vs FormRoot is the main subtle regression surface.
Overview
Checkbox styling moves from styled-components to
Checkbox.module.css, with variant classes driven bycva/cnon the Radix root and aclassNamepassthrough. Layout still usesFormRoot(still styled-components); a.wrapper.wrapperspecificity boost preservesalign-items: centerandmax-width: fit-contentagainstFormRoot’s defaultflex-start.Storybook gains dedicated stories (states, var1–var6, orientation, no label) to feed a new
tests/forms/checkbox.spec.tsPlaywright suite (light/dark snapshots, hover/focus, a11y). The unit test dropsgetComputedStylefor disabled cursor in jsdom and assertsdisabled/data-disabledinstead. A patch changeset records no intended behavior change.Reviewed by Cursor Bugbot for commit 3a9fd2d. Bugbot is set up for automated code reviews on this repo. Configure here.