feat(fields): adopt <Card> primitive for FieldCard tile [Phase 2a]#627
feat(fields): adopt <Card> primitive for FieldCard tile [Phase 2a]#627barach6662001-bit merged 2 commits intomainfrom
Conversation
Outer container of FieldCard migrated from a hand-rolled
`<div className={s.card}>` to the design-system <Card> primitive.
Card supplies background (subtle/--card-bg), border and radius
(radius="lg" preserves the legacy 12 px radius). Card's auto
padding/gap are disabled (p="0", gap="0") because the tile composes
its own internal layout — a polygon thumb that bleeds to the edges
plus an info block with its own padding.
The local CSS module retains only the tile-specific affordances
that <Card> does not provide: cursor:pointer, position:relative
(for the absolutely-positioned hover overlay), overflow:hidden
(so the bleed thumb clips against the radius), the lift +
shadow hover transition, and the descendant rule that fades the
hover CTA in on hover.
Behavior preserved verbatim from the legacy implementation,
including the existing onClick on a non-button element. The
`border-color: var(--border-hover)` change on hover is dropped
in favor of the dominant transform + shadow lift, matching the
precedent set by Phase 1d–1f migrations (the inline border on
the primitive cannot be overridden by an :hover class rule).
No other consumers of FieldCard.module.css exist; FieldsList is
the only call-site and is unchanged.
Tests: adds frontend/src/pages/Fields/components/__tests__/FieldCard.test.tsx
(4 focused tests covering name/area/cadastral/crop-pill rendering,
the not-seeded fallback, the cadastral-omit case, and click navigation).
No existing Fields tests to update.
Validation: focused vitest suite (62 tests across design-system
+ Fields) passes; tsc --noEmit clean; eslint clean.
Phase 2a — pure-surface migration, no functional changes.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f2c9779791
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| <Card | ||
| as="div" | ||
| radius="lg" | ||
| p="0" | ||
| gap="0" |
There was a problem hiding this comment.
Keep FieldCard hover shadow from being overridden
Switching this container to <Card> causes Surface to inject an inline boxShadow (elevation defaults to 0), and inline styles win over .card:hover { box-shadow: ... } in FieldCard.module.css. In practice, hovering a field tile now only translates it; the shadow lift no longer appears. This regression is introduced by the new Card wrapper in FieldCard and affects every hover interaction in the fields grid.
Useful? React with 👍 / 👎.
Summary
Phase 2a — outer container of
FieldCardmigrated from a hand-rolled<div className={s.card}>to the design-system<Card>primitive.Migration
<div className={s.card} onClick={...}>→<Card as="div" radius="lg" p="0" gap="0" className={s.card} onClick={...}>subtle/--card-bg), 1 px--border, andvar(--radius-lg)(12 px) — matching the legacy values from tokens.p="0"/gap="0"because the tile composes its own internal layout (a polygon thumb that bleeds to the edges + an info block with its own padding).onClickis forwarded by Surface via...rest; behavior unchanged.CSS module
Trimmed
.cardto only the affordances Card does not provide:cursor: pointerposition: relative(anchor for the absolutely-positioned hover overlay)overflow: hidden(so the bleed thumb clips against the rounded corners):hoverlift (translateY(-2px)) andbox-shadow: var(--shadow-md).card:hover .hoverOverlay { opacity: 1 }that fades the CTA in on hoverThe legacy
border-color: var(--border-hover)hover change is dropped — Card's inline border can't be overridden by an:hoverclass rule. The dominant transform + shadow lift is preserved, matching the precedent set by Phase 1d–1f migrations.All inner markup, navigation, badges, crop pill, ownership label, cadastral number and responsive behavior preserved verbatim.
Tests
Adds
frontend/src/pages/Fields/components/__tests__/FieldCard.test.tsx— 4 focused tests:cadastralNumberis undefinedfields.notSeededfallback whencurrentCropis unset/fields/{id}when the card surface is clickedNo existing Fields tests to update.
Scope
Touches only:
frontend/src/pages/Fields/components/FieldCard.tsxfrontend/src/pages/Fields/components/FieldCard.module.cssfrontend/src/pages/Fields/components/__tests__/FieldCard.test.tsx(new)FieldCard.module.csshas only one consumer (FieldsList) — no other call-sites affected. Out of scope per Phase 2a brief:FieldDetail,OperationDetail,AppLayout,Sidebar, noCard highlightprop.Validation
npx vitest run src/design-system src/pages/Fields→ 62/62 passing (incl. 4 new FieldCard tests)npx tsc --noEmitcleannpx eslinton touched files cleanPhase 2 progression
Phase 1a–1f (PRs #616, #620, #621, #622, #624, #625) covered the dashboard row. Phase 2a starts extending Card adoption beyond the dashboard, into list/grid tiles.
FieldCardchosen as the lowest-risk drop-in target (classic tile pattern, no need to reopen the deferredCard highlightprop discussion).