Skip to content

feat(design-system): add Surface and Card primitives + first dashboard adoption [Phase 1d]#622

Merged
barach6662001-bit merged 1 commit intomainfrom
replit/phase-1d-surface-card
Apr 24, 2026
Merged

feat(design-system): add Surface and Card primitives + first dashboard adoption [Phase 1d]#622
barach6662001-bit merged 1 commit intomainfrom
replit/phase-1d-surface-card

Conversation

@barach6662001-bit
Copy link
Copy Markdown
Owner

Phase 1d — Поверхнева система: Surface + Card

Це другий шар design-system над примітивами Phase 1b. Зараз кожен «панельний» вигляд (фон + бордер + радіус + падінг + тінь) — це власний CSS-модуль. Тепер це один токенізований компонент.

Що додано

Surface — базовий контейнер

  • variant: 'subtle' (--card-bg) | 'raised' (--bg-elevated) | 'flat'
  • bordered: boolean — 1 px --border
  • radius: 'sm' | 'md' | 'lg' | 'xl' | '2xl' (мапиться на --radius-*)
  • elevation: 0..4--shadow-0..4
  • interactive: hover/focus стани через :hover / :focus-visible (єдине місце, де знадобився CSS-модуль; решта стилізації — інлайн через токени)
  • Поліморфний as, forwardRef, повний padding pass-through (p / px / py / pt / pr / pb / pl) — той самий ідіом, що в Box

Card — пресет Surface

  • Дефолти: subtle, xl, p=5, без тіні, з бордером, внутрішній вертикальний flex з токен-gap
  • Sub-компоненти композуються природно всередині

CardHeader / CardTitle / CardDescription / CardContent / CardFooter

Тонкі обгортки над уже існуючими примітивами (Stack, Cluster, Heading, Text). Жодного дублювання, жодних паралельних CSS, жодних нових токенів.

Міграція (proof-of-adoption)

Один сайт у DashboardV2.tsx — обгортка тайлу «Залишки на складах»: <div className={s.card}><Card>.

  • Візуальна ієрархія збережена: section-label все ще над карткою; падінг 20 px; той самий радіус (16 / xl); той самий hairline border; той самий subtle bg.
  • Дрифт токенів — навмисний: легасі-клас використовував off-token значення (.02 bg, .06 border, 22 px горизонтального падінгу); новий Card використовує канонічні --card-bg / --border / --space-5. Візуально нерозрізнимо, але керується системою.
  • Дві сусідні картки в тому ж рядку (OperationsTimeline, Upcoming) навмисно залишаються на старому s.card — мінімальний радіус впливу. Майбутні PR мігрують решту.

Поза скоупом (явні non-goals)

  • AppLayout / Sidebar — НЕ мігруємо
  • Жодних нових токенів
  • Жодних нових залежностей
  • Жодних змін публічних роутів, API-викликів чи бізнес-логіки

Перевірки

Перевірка Результат
vitest 162 / 162 passed (25 нових: 14 на Surface, 11 на Card / Card.*)
tsc -b clean
eslint 0 errors (тільки успадковані warnings)
vite build OK; PWA bundle майже не змінився (+0.07 KB)

Що це розблоковує

  • AlertsPanel та решта дашборд-тайлів зможуть викинути власні card-CSS і прийняти <Card> тривіальними PR-ами
  • Сайдбарні панелі і form-контейнери можна прийняти на <Surface> напряму, параметризовано через variant / elevation
  • Storybook-сторі (Phase 2) — одна Card story замість N майже-однакових panel-сторі

…d adoption [Phase 1d]

  Establishes the dashboard "surface system" — the second layer of the
  design system, sitting on top of the Phase 1b layout primitives. With
  this PR, the typical "panel" / "card" pattern (background, border,
  radius, padding, elevation) becomes a single, token-driven component
  instead of a bespoke CSS module per consumer.

  What ships
  - `Surface`: the primitive container.
    - `variant`:    'subtle' (--card-bg) | 'raised' (--bg-elevated) | 'flat'
    - `bordered`:   boolean → 1 px --border outline
    - `radius`:     'sm' | 'md' | 'lg' | 'xl' | '2xl' (token-mapped)
    - `elevation`:  0..4 → --shadow-0..4
    - `interactive`: hover/focus states via :hover / :focus-visible (the
      only place CSS modules are needed; rest of the styling is inline-
      token).
    - Polymorphic `as`, forwardRef, full padding pass-through (p / px /
      py / pt / pr / pb / pl) — same idiom as Box.
  - `Card`: pre-configured Surface (subtle, xl, p=5, no shadow, bordered)
    with internal vertical flex + token gap. Sub-components compose
    naturally inside.
  - `CardHeader` / `CardTitle` / `CardDescription` / `CardContent` /
    `CardFooter`: thin compositions over the existing primitives —
    Stack, Cluster, Heading, Text. No duplication, no parallel CSS,
    no new tokens.

  Migration (proof-of-adoption)
  - `DashboardV2.tsx` "Warehouse snapshot" tile: replaces the local
    `<div className={s.card}>` wrapper with `<Card>`.
    - Visual hierarchy preserved: section label still renders above the
      card; padding stays at 20 px; same radius (16 / xl); same hairline
      border; same subtle bg.
    - Token drift is intentional: the legacy class used off-token values
      (.02 bg, .06 border, 22 px horizontal padding); the new Card uses
      the canonical `--card-bg` / `--border` / `--space-5`. Visually
      indistinguishable, but now governed by the design system.
  - The other two cards in the same row (OperationsTimeline, Upcoming)
    intentionally stay on `s.card` — single-site adoption keeps blast
    radius minimal. Future PRs migrate the rest.

  Out of scope (explicit non-goals for this PR)
  - AppLayout / Sidebar are NOT migrated.
  - No new tokens added.
  - No new dependencies.
  - No public route or API call changed; no business logic touched.

  Verification
  - 162 / 162 tests green (25 new: 14 on Surface, 11 on Card / Card.*).
  - `tsc -b` clean.
  - `eslint` 0 errors (only inherited warnings).
  - `vite build` OK; PWA bundle size unchanged (+0.07 KB).

  What this unlocks
  - `AlertsPanel` and the remaining dashboard tiles can now drop their
    per-component card CSS modules and adopt `<Card>` in trivial PRs.
  - Sidebar panels and form containers can adopt `<Surface>` directly,
    parameterised by variant/elevation.
  - Storybook stories (Phase 2) get a single `Card` story instead of
    N near-duplicate panel stories.
@barach6662001-bit barach6662001-bit merged commit 8ece50b into main Apr 24, 2026
3 checks passed
@barach6662001-bit barach6662001-bit deleted the replit/phase-1d-surface-card branch April 24, 2026 10:43
barach6662001-bit added a commit that referenced this pull request Apr 24, 2026
…[Phase 1f] (#625)

Finishes the DashboardV2 row-card migration. Both remaining sibling
  panels (OperationsTimeline in row 4, UpcomingPanel in row 5) now use
  the design-system <Card> primitive instead of the local `s.card`
  wrapper. Combined with Phase 1d (WarehouseSnapshot) and Phase 1e
  (AlertsPanel), every dashboard surface in this row is now driven by
  the same primitive.

  Wrapper migration (DashboardV2.tsx)
  -----------------------------------
  - <div className={s.card}><OperationsTimeline …/></div> → <Card>…</Card>
  - <div className={s.card}><UpcomingPanel …/></div>     → <Card>…</Card>

  Both sites are pure wrapper swaps. <Card> defaults
  (variant=subtle, radius=xl=16px, padding=5=20px, bordered=true) are
  an exact drop-in for the legacy `.card` rule, so the visual contract
  (background, border, radius, padding) is preserved. The internal
  list/item content of each panel is untouched.

  CSS dead-code removal (DashboardV2.module.css)
  ----------------------------------------------
  With both consumers gone, these rules become unreferenced and are
  deleted (≈39 lines):
  - .card                       (background, border, radius, padding, transition)
  - .card:hover                 (subtle background lift)
  - .card::before               (decorative top hairline)
  - .cardHeader                 (flex justify-between row, never used in DashboardV2.tsx)
  - .cardLink, .cardLink:hover  (eyebrow link button, never used in DashboardV2.tsx)
  A short tombstone comment is left in place so future readers see why
  the section is empty and where to look (Card primitive path).

  Out of scope
  ------------
  - No route changes
  - No API changes
  - No business-logic changes
  - No refactor of inner panel markup
  - No new dependencies
  - No Card highlight prop introduction
  - No new tests (spec: only update if coverage exists; none did)

  Verification (local)
  --------------------
  - frontend tsc -b: clean
  - frontend lint: 0 errors (warning count unchanged)
  - vitest design-system + dashboard subset: 67/67 passing
  - Full test + build run delegated to CI.

  Phase 1 status after this PR
  ----------------------------
  - ✅ 1a — TS tokens (#616)
  - ✅ 1b — Layout + Typography primitives (#620)
  - ✅ 1c — PageHeader migration (#621)
  - ✅ 1d — Surface + Card primitives + first dashboard adoption (#622)
  - ✅ 1e — AlertsPanel adoption (#624)
  - ✅ 1f — OperationsTimeline + UpcomingPanel adoption (this PR)

Co-authored-by: replit-agent <agent@replit.local>
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