Skip to content

chore(CardHorizontal): migrate to CSS Modules with visual regression baseline#1039

Merged
DreaminDani merged 4 commits into
mainfrom
chore/migrate-cardhorizontal-css-modules
May 26, 2026
Merged

chore(CardHorizontal): migrate to CSS Modules with visual regression baseline#1039
DreaminDani merged 4 commits into
mainfrom
chore/migrate-cardhorizontal-css-modules

Conversation

@DreaminDani
Copy link
Copy Markdown
Contributor

@DreaminDani DreaminDani commented May 20, 2026

Summary

Migrates CardHorizontal from styled-components to CSS Modules (cva + cn), following the procedure established by ButtonGroup (PR #1034), IconButton (PR #1036), and CardPrimary (PR #1038) and codified in the `component-css-modules-migration` skill.

Two commits, each green on its own:

  • `test(CardHorizontal): add visual regression baseline before CSS Modules migration` — extends the stories with one named story per visual variant (color, size, alignment, selectable, selected, disabled, disabled+selected, with-badge, with-button) and adds `tests/cards/cardhorizontal.spec.ts`. 28 snapshots captured against the existing styled-components rendering, covering light + dark themes plus hover/focus interactive states on both selectable and non-selectable cards. The stories-only `GridCenter` decorator (a Storybook display helper) is converted from `styled.div` to inline style in the same commit to keep the directory free of `styled-components` after the migration.
  • `chore(CardHorizontal): migrate styling from styled-components to CSS Modules` — adds `CardHorizontal.module.css` consolidating six styled-components (Wrapper, Header, Description, CardIcon, ContentWrapper, IconTextContentWrapper) into one stylesheet. Re-asserts the baseline snapshots byte-for-byte with zero regenerations.

The migration is a pure styling refactor — no a11y refinements, no type changes, no consumer updates.

Judgment calls to flag

  1. Local CSS variables for color variants. `CardHorizontal` has two color variants (`default`, `muted`) and each appears in four states (default, hover, active/focus, disabled) across four token families (background, title, stroke, description). Instead of emitting a full state matrix per color (≈80 rules), I exposed the per-color tokens as locally-scoped CSS custom properties (`--card-bg-default`, `--card-stroke-active`, etc.) on `.wrapper_color_default` and `.wrapper_color_muted`, then write the state rules once against `var(--card--)`. This kept the stylesheet compact without changing any rendered behavior — confirmed by zero snapshot regeneration.

  2. Scoped `stylelint-disable no-descending-specificity` around the disabled-state block. The disabled rules intentionally come after hover/active rules to mirror the source cascade order. `pointer-events: none` plus `tabIndex=-1` on disabled cards prevent the hover/active rules from ever firing, so the lint complaint is structural rather than behavioral. Scoped narrowly to that block.

  3. Scoped `stylelint-disable-next-line media-feature-range-notation` on the responsive `@media`. The repo's stylelint-config-standard expects modern range notation (`(width <= 768px)`), but `plugin/no-unsupported-browser-features` flags that syntax as unsupported in Edge 102–103, Chrome 102–103, Safari 15.6, etc. The two rules conflict; the prefix notation (`(max-width: 768px)`) is required for browser compatibility per `.browserslistrc`. Single-line disable.

Test plan

  • `yarn test:visual tests/cards/cardhorizontal.spec.ts` — all 28 snapshots pass against the baseline with zero regenerations after the migration commit.
  • `yarn test CardHorizontal` — unit tests (18) pass unchanged.
  • `yarn lint:code src/components/CardHorizontal/` — no new errors.
  • `yarn lint:css` — clean (with the scoped disables noted above).
  • `yarn build` — succeeds.
  • `grep -r 'styled-components' src/components/CardHorizontal/` — empty.

🤖 Generated with Claude Code


Note

Low Risk
Styling-only refactor in a single component, guarded by extensive visual snapshots and unchanged public props/types.

Overview
CardHorizontal moves from styled-components to CSS Modules with cva/cn for variant classes, a new CardHorizontal.module.css (including scoped CSS variables per color and preserved hover/selectable/disabled cascade), and optional className on the root. Storybook gains dedicated variant stories and drops styled-components from the decorator; a changeset records a patch with no intended behavior change.

Tests: Playwright visual regression (tests/cards/cardhorizontal.spec.ts) covers light/dark variants, hover/focus, and basic a11y (focus + aria-disabled / tabIndex).

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

DreaminDani and others added 2 commits May 19, 2026 18:45
…es migration

Captures the current styled-components rendering for color variants
(default, muted), size (md, sm), alignment (top), selectable/selected,
disabled (with and without isSelected), badge, button, and the hover
and focus interactive states (on both selectable and non-selectable
cards) under both light and dark themes. These snapshots will be
re-asserted byte-for-byte after the CSS Modules migration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…Modules

Replaces six styled.div templates (Header, Description, Wrapper, CardIcon,
ContentWrapper, IconTextContentWrapper) with a single CardHorizontal.module.css
+ cva/cn. Per-color tokens are exposed as local CSS variables so the state
rules (default, hover, active/focus/focus-within, disabled) are written once
and consumed by both the default and muted color variants. The DOM tree and
every visual state are preserved byte-for-byte against the snapshots
captured in the baseline commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 20, 2026

🦋 Changeset detected

Latest commit: a81f62c

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

@DreaminDani
Copy link
Copy Markdown
Contributor Author

Scoped stylelint-disable-next-line media-feature-range-notation on the responsive @media. The repo's stylelint-config-standard expects modern range notation ((width <= 768px)), but plugin/no-unsupported-browser-features flags that syntax as unsupported in Edge 102–103, Chrome 102–103, Safari 15.6, etc. The two rules conflict; the prefix notation ((max-width: 768px)) is required for browser compatibility per .browserslistrc. Single-line disable.

These rules feel like they conflict with one another. I don't think we should use the width <= 768px if it's going to cause all this extra rule disabling inline.

Comment thread src/components/CardHorizontal/CardHorizontal.module.css
Comment thread .changeset/migrate-cardhorizontal-to-css-modules.md Outdated
Comment thread src/components/CardHorizontal/CardHorizontal.module.css
$color={color}
$size={size}
$alignment={alignment}
<div
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same question about replacing Wrapper with a div

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Same answer: #1038 (comment)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread src/components/CardHorizontal/CardHorizontal.tsx
…x and aria-disabled

Move {...props} after the hardcoded tabIndex, aria-disabled, and onClick
so consumer-provided values can override them, matching the pre-migration
behavior.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@workflow-authentication-public
Copy link
Copy Markdown
Contributor

Storybook Preview Deployed

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

Built from commit: 4de5337289a7701f6659d4bd29e810ce8da90c19

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a81f62c. Configure here.

Comment thread src/components/CardHorizontal/CardHorizontal.module.css
Comment thread tests/cards/cardhorizontal.spec.ts
@DreaminDani DreaminDani merged commit 24c8be3 into main May 26, 2026
10 checks passed
@DreaminDani DreaminDani deleted the chore/migrate-cardhorizontal-css-modules branch May 26, 2026 16:52
DreaminDani added a commit that referenced this pull request May 26, 2026
* docs(skill): codify prop spread order rule for CSS Modules migrations

Spreading {...props} before hardcoded attributes (aria-disabled,
tabIndex, onClick) silently overrides consumer-passed values. The
styled-components version of these components put {...props} last,
so the migration must preserve that order. TypeScript doesn't catch
the regression because HTMLAttributes<...> admits those props.

Caught post-merge on CardPrimary (#1038) and CardHorizontal (#1039).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(skill): codify local CSS variable aliasing for multi-state variants

When a component has color/theme variants that cross many states
(default, hover, active, disabled) and many properties (background,
title, stroke, description), writing one state rule per variant blows
up the file. The pattern adopted in CardHorizontal (#1039) aliases the
long global tokens to short local custom properties inside each color
modifier block, then writes the state rules once on the base class
referencing the short names — the cascade resolves the right value.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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