A bordered surface with a radius, a background and sometimes a shadow appears all over the app, and no two implementations agree.
| Where |
radius |
padding |
shadow |
experiments/MetricsTrendChart |
xl |
20px |
sm |
experiments/MetricsComparisonTable |
xl |
12px |
sm |
experiments/VariationTable |
xl |
12px |
sm |
experiments/MetricsTable |
lg |
10px |
none |
experiments/CreateMetricForm |
md |
14px |
none |
pages/usage sections |
lg |
24px |
none |
Fourteen files hand-roll it in SCSS with border: 1px solid var(--color-border-default), including base/Chip, base/SelectableCard, base/grid/ContentCard, eight under experiments/, and the onboarding connect panel.
There is already a component for this
base/grid/Panel.tsx renders a bordered surface with an optional heading row carrying a title and an action, which is the shape most of the hand-rolled ones are reaching for.
It gets avoided because it is pre-token: a class component rendering .panel, themed through a .dark ancestor selector and $panel-* SCSS variables rather than custom properties. components/Card.tsx is a thinner version of the same thing and renders panel panel-default, a class no stylesheet defines.
The drift above is the symptom. Everyone routed around the legacy component and picked their own values.
Blast radius: Panel has 16 consumers, .panel appears directly in 15 more files, Card has 3 consumers across 6 usages.
What needs deciding first
One radius, one padding, one shadow-or-not for a panel surface. Nothing else can start until that is settled, and it is a design decision rather than an engineering task.
Then
- express it once, as a token group or a single utility, so a component and a hand-rolled stylesheet can both reach it
- point
Panel at it and fold Card in, since Card is a Panel without a heading
- let the 14 hand-rolled surfaces adopt incrementally, not in one sweep: the dark-mode mechanism changes underneath them
Note on naming
"Card" is the wrong word for most of these. The usage meter and chart are full-width page sections with a heading and content: not one of a set, not clickable, not a preview of something else. Panel already names it correctly.
A bordered surface with a radius, a background and sometimes a shadow appears all over the app, and no two implementations agree.
experiments/MetricsTrendChartexperiments/MetricsComparisonTableexperiments/VariationTableexperiments/MetricsTableexperiments/CreateMetricFormpages/usagesectionsFourteen files hand-roll it in SCSS with
border: 1px solid var(--color-border-default), includingbase/Chip,base/SelectableCard,base/grid/ContentCard, eight underexperiments/, and the onboarding connect panel.There is already a component for this
base/grid/Panel.tsxrenders a bordered surface with an optional heading row carrying a title and an action, which is the shape most of the hand-rolled ones are reaching for.It gets avoided because it is pre-token: a class component rendering
.panel, themed through a.darkancestor selector and$panel-*SCSS variables rather than custom properties.components/Card.tsxis a thinner version of the same thing and renderspanel panel-default, a class no stylesheet defines.The drift above is the symptom. Everyone routed around the legacy component and picked their own values.
Blast radius:
Panelhas 16 consumers,.panelappears directly in 15 more files,Cardhas 3 consumers across 6 usages.What needs deciding first
One radius, one padding, one shadow-or-not for a panel surface. Nothing else can start until that is settled, and it is a design decision rather than an engineering task.
Then
Panelat it and foldCardin, sinceCardis aPanelwithout a headingNote on naming
"Card" is the wrong word for most of these. The usage meter and chart are full-width page sections with a heading and content: not one of a set, not clickable, not a preview of something else.
Panelalready names it correctly.