Skip to content

chore(Icon): migrate to CSS Modules with visual regression baseline#1050

Open
DreaminDani wants to merge 5 commits into
mainfrom
chore/migrate-icon-css-modules
Open

chore(Icon): migrate to CSS Modules with visual regression baseline#1050
DreaminDani wants to merge 5 commits into
mainfrom
chore/migrate-icon-css-modules

Conversation

@DreaminDani
Copy link
Copy Markdown
Contributor

@DreaminDani DreaminDani commented May 27, 2026

Commits

  • test(Icon): add visual regression baseline before CSS Modules migration — adds stories, Playwright spec, and fresh PNG snapshots that capture the current rendering across size, state, custom-color/width-height, and asset (flag/logo/payment) variants in light and dark themes.
  • chore(Icon): migrate styling from styled-components to CSS Modules — replaces all three styled-components (the SvgWrapper in Icon.tsx, the polymorphic SvgImageElement.svg, and the gallery ResponsiveGridContainer in stories) with .module.css + cva/cn. DOM-identical, byte-for-byte visual parity verified.

Notes

  • Removing styled-components from SvgImageElement forces a tiny consumer prop rename: $size -> size in Flag.tsx, Logo.tsx, Payment.tsx, and Assets/Icons/system/Icon.tsx. These four call-site updates are an unavoidable consequence of dropping the transient styled-components prop and are bundled into the migration commit so main stays green.
  • The original width: ${$width} interpolation emitted invalid CSS (e.g. width: 40 without unit), causing the SVG to fall back to its intrinsic width/height attributes. That quirk is preserved by stringifying the value into a CSS custom property used inside var(), which keeps the unit-less value invalid and triggers the same fallback path.
  • Stories use a small co-located Icon.stories.module.css for the responsive grid that previously lived in styled(GridContainer).

Verification

  • yarn test:visual tests/utils/icon.spec.ts passes with zero snapshot regenerations (37 tests, 36 PNGs)
  • yarn lint:css, yarn lint:code, yarn build all green
  • grep -r 'styled-components' src/components/Icon/ empty

Closes CUI-36


Note

Medium Risk
Icon is a core design-system surface; the migration is broad but guarded by extensive visual regression tests and a minimal public API tweak ($size→size on internal SvgImageElement consumers).

Overview
Icon styling moves off styled-components onto CSS Modules plus cva/cn, with a patch changeset and the stated goal of unchanged visuals.

The glyph wrapper (SvgWrapper in Icon.tsx) and shared SvgImageElement are reimplemented with Icon.module.css (sizes, semantic states, stroke/fill, custom color via --svg-icon-color, width/height via CSS variables). SvgImageElement is no longer a styled svg; it is a small polymorphic component with a public size prop, so Flag, Logo, Payment, and Assets/Icons call sites switch from transient $size to size.

Storybook adds an IconHarness (data-testid="icon-harness") and many focused stories (sizes, states, custom color/dimensions, flag/logo/payment assets); the icon gallery’s responsive grid moves from styled(GridContainer) to Icon.stories.module.css. A large tests/display/icon.spec.ts Playwright suite snapshots those stories in light and dark (plus one a11y check on aria-label).

Reviewed by Cursor Bugbot for commit 714f8dd. Bugbot is set up for automated code reviews on this repo. Configure here.

@DreaminDani DreaminDani self-assigned this May 27, 2026
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 27, 2026

🦋 Changeset detected

Latest commit: 714f8dd

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

This PR includes changesets to release 1 package
Name Type
@clickhouse/click-ui 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

Address Copilot's standing review feedback on the migration PRs:
tests/utils/ is reserved for shared helpers like getStoryUrl;
component specs belong in component-family folders. Migration skill
updated in PR #1049 to codify this for future migrations.

Visual regression remains byte-for-byte: 37/37 snapshots match without
regeneration.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Migrates the shared Icon component styling from styled-components to CSS Modules while adding Playwright visual regression coverage to lock in rendering parity across themes, sizes, states, overrides, and asset-backed variants (flags/logos/payments).

Changes:

  • Added Storybook harness stories + Playwright screenshot spec and baselines for icon variants in light/dark.
  • Replaced styled-components wrappers with CSS Modules + cva/cn for Icon and SvgImageElement.
  • Updated four asset call sites to use size instead of the transient styled-components prop $size.

Reviewed changes

Copilot reviewed 11 out of 47 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/utils/icon.spec.ts Adds Playwright visual regression coverage for Icon variants (light/dark).
src/components/Icon/SvgImageElement.tsx Converts SvgImageElement from styled svg to a normal component using CSS Modules + cva.
src/components/Icon/Icon.tsx Replaces SvgWrapper styled-component with CSS Module classes and CSS custom props for overrides.
src/components/Icon/Icon.stories.tsx Adds harness stories used by visual tests; replaces responsive grid styled-component with CSS module class.
src/components/Icon/Icon.stories.module.css Implements responsive grid styling previously in styled-components.
src/components/Icon/Icon.module.css New CSS Module for wrapper sizing/state styling and asset svg sizing.
src/components/Assets/Payments/system/Payment.tsx Renames $size prop usage to size for SvgImageElement.
src/components/Assets/Logos/system/Logo.tsx Renames $size prop usage to size for SvgImageElement.
src/components/Assets/Icons/system/Icon.tsx Renames $size prop usage to size for SvgImageElement.
src/components/Assets/Flags/system/Flag.tsx Renames $size prop usage to size for SvgImageElement.
.changeset/migrate-icon-to-css-modules.md Publishes the migration as a patch changeset.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/components/Icon/Icon.module.css
Comment thread src/components/Icon/Icon.stories.tsx Outdated
… prettier

Address Copilot review feedback on PR #1050: type ResponsiveGridContainer
as `ComponentProps<typeof GridContainer>` and merge className via `cn`
so callers can extend the wrapper without losing the responsive-grid
class.

Also runs prettier on the file to clear pre-existing code-quality CI
failures from the migration commit (one-line JSX expressions that
prettier wants on multiple lines). Auto-format, no behavior change.

The unaddressed Copilot point — `circle[fill]` appearing in both the
stroke and fill rules of Icon.module.css — is a pre-existing bug that
the migration deliberately preserved per the byte-for-byte parity rule
(`circle[stroke]` was missing from the original styled-components
source too). Fix in a separate PR after this lands.
DreaminDani added a commit that referenced this pull request May 27, 2026
Match the convention established in PRs #1045, #1046, #1048, and
#1050 and codified in the migration skill: tests/utils/ is reserved
for shared helpers like getStoryUrl, primitives go in tests/display/.

Visual regression remains byte-for-byte: 10/10 snapshots match without
regeneration.
The IconHarness wrapper applied a hardcoded grey #888 background that
made sense for Spacer/Separator (where you need contrast against an
otherwise invisible primitive) but hurt Icon's stories — it muddied
the state pills (success/warning/danger/info badges) and washed out
glyphs whose visibility doesn't need any artificial contrast against
Storybook's page background.

Drop the backdrop and the 24px padding; keep the wrapper as a
testid-only inline-flex container so the Playwright locator still
attaches. All 36 Icon snapshots regenerated.

No component code changes; stories/spec layout only.
@workflow-authentication-public
Copy link
Copy Markdown
Contributor

Storybook Preview Deployed

✅ Preview URL: https://click-g4twkisn6-clickhouse.vercel.app

Built from commit: 6c0612aebf9a638402293eb1d0c8e491864e3624

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