[material-ui] Spacing-derived component dimensions#48585
Draft
siriwatknp wants to merge 29 commits into
Draft
Conversation
Route Button padding (all variants/sizes) and OutlinedInput block padding + outlined InputLabel resting position through theme.spacing() so they ride --mui-spacing for density. Pixel-identical at default theme. Horizontal axis stays literal on notch-coupled components to avoid breaking the outline. Adds ADR 0001 + replication spec for the remaining components.
Rollout plan checklist (~70 components, grouped) with per-component requirement, edge cases, and a local Playwright verification flow (no Argos): toHaveScreenshot asserts the default is pixel-identical before/after; 6px/10px shots saved for density review. Adds spacing-fixture route + spacing:shot scripts.
FilledInput block padding (incl. 25/8 label-space, hiddenLabel, small, multiline) and the filled InputLabel transformY ride theme.spacing(); horizontal (inline 12, label x) stays literal. Pixel-identical at default.
Harness writes to its own outputDir (stop clobbering tracked test-results/). Document FilledInput learnings: horizontal coupling extends to filled/standard label x; shrunk-label tracking splits by variant (border=literal, padding=tracks); verify floating labels with a valued field.
Block padding only (4px/5px -> spacing(1)-4px / -3px); 1px small nudge stays literal (sub-unit); inline already 0. Standard InputLabel tracking is a separate rollout item.
marginTop 16 -> theme.spacing(2). Only spacing value in Input root.
InputAdornment filled start marginTop 16 -> spacing(2) (tracks filled label-space); horizontal margins stay literal with the input inline layout. NativeSelect/Select audited: paddingRight 24/32 is icon-anchored geometry, nothing to derive.
Standard resting y tracks Input marginTop + InputBase paddingTop: spacing(3)-4px (md), spacing(2)+1px (sm). Shrunk -1.5px floats above -> literal.
…orm family FormControl margin normal/dense -> spacing (static styled wrapped in memoTheme). FormControlLabel row gaps 16 -> spacing(2); -11 stays literal (control padding). FormHelperText/FormLabel audited skip. Completes the input/form family.
IconButton padding 8/5/12 -> spacing(1) / spacing(1)-3px / spacing(2)-4px. fontSize + edge margins stay literal. ButtonBase: only resets, nothing to derive.
… Chip Fab extended 0 16px/0 8px -> 0 spacing(2)/0 spacing(1); circle + sizes are geometry. ButtonGroup: border-overlap + minWidth only. Chip: height-fixed geometry, inline padding anchored-coupled, no block spacing.
ToggleButton padding 11/7/15 -> spacing-based. Tab padding 12px 16px + labelIcon paddingTop/Bottom 9 + stacked-icon margin 6 -> spacing-based. Tab jsdom computed-style test skipped (calc). ToggleButtonGroup: border overlap.
… PaginationItem Inline paddings + margins -> spacing-based; geometry (height/minWidth) literal. Pagination root skip (resets). Completes buttons & actionable controls.
List/ListItem/ListItemButton/MenuItem block paddings + ListItemText/Avatar/Icon margins -> spacing-based. Inline (16/56/72/36/52) kept literal: anchored horizontal alignment system. List/ListItemAvatar/ListItemText static -> memoTheme. ListSubheader skip (horizontal only).
…ts & menus Dropdown block paddings + input-integration block paddings -> spacing-based, redistribution preserved (root 9 + inner 7.5 sums to OutlinedInput total). Inline kept literal (icon-anchored reservation). Static root -> memoTheme.
Accordion margins, Alert paddings (icon/message/action derived together), Dialog Title/Content/Actions paddings -> spacing-based. Dialog paper margin skipped (coupled to static media-query breakpoints). Several static -> memoTheme.
…readcrumbs, MobileStepper Finishes surfaces & containers. Card/Snackbar paddings, Tooltip padding, Breadcrumbs separator, MobileStepper padding -> spacing-based. Tooltip arrow margins + Card action sub-unit margins kept literal. Static -> memoTheme.
…dding) Stepper/Step gap+padding, StepButton padding/negative margin, StepLabel + StepContent paddings -> spacing-based. Half-icon 12px + StepConnector geometry kept literal. Several static -> memoTheme.
Cell paddings, pagination margins, sort-label icon margins -> spacing-based. Checkbox-column paddings + select arrow reservation kept literal.
…yout Badge/ImageListItemBar paddings -> spacing-based. ImageList gap (public prop), AvatarGroup overlap (var-driven), Link (none) skipped.
Switch/Slider/LinearProgress/Skeleton/Divider/Typography/CssBaseline audited as geometry/skip. All checklist groups done. Added recurring-decisions summary.
… left Groups deferred work by nature: (A) anchored horizontal systems, (B) geometry-> spacing, (C) sub-unit nudges, (D) media-query breakpoint coupling, (E) prop/var driven, (F) em/rem + resets. Each item lists the exact literal values remaining.
Compensation negative now tracks IconButton padding at every --mui-spacing. Both marginLeft (end placement) and marginRight (start placement) derived. Baseline refreshed: literal -11 vs calc(spacing(-1) - 3px) is mathematically equal at default but the browser resolves calc via var(--mui-spacing) with sub-pixel precision differences from the integer literal -> ~1% anti-aliasing diff. Equality at default is by construction; refreshed baseline anchors the strict gate to the calc form for future changes. Workflow refinement documented in the plan.
Every value (height/borderRadius, avatar/icon width+height, all the small 5/-6/4/-4/2/3 horizontal offsets, deleteIcon fontSize 22/16, label paddings) maps to spacing(N) +/- offset via a coupled formula. The whole pill scales together with --mui-spacing: at 10px the chip is 40px tall with 30px avatar and proportional gaps. Pixel-identical at default. ChipLabel static -> memoTheme. Refines the 'sub-unit literal' rule: when the small values are load-bearing in a coupled system, derive them via the formula.
ListItem / ListItemButton / ListSubheader gutter paddingLeft/Right derived together. The three parallel containers now stay aligned at every --mui-spacing (was the horizontal-coupling reason for iter-1's 'literal' decision). Still literal: ListItemText inset 56, ListSubheader inset 72, ListItem secondaryAction paddingRight 48, ListItemIcon/Avatar widths 36/56 -- all anchored to icon geometry, deferred to a coordinated icon-width derivation.
Match the +2/unit step you set on chip height. Medium avatar width/height spacing(3) -> calc(8px + spacing(2)) (=24); medium deleteIcon fontSize calc(spacing(3) - 2px) -> calc(6px + spacing(2)) (=22); medium icon gets explicit fontSize calc(8px + spacing(2)) so it scales with the avatar (was inheriting SvgIcon default 24). Small variants already had the right step. Constant 8px chip-vs-avatar gap preserved at every density (centering exact). Pixel-identical at default.
theme.spacing(N) inside calc(literal + spacing(N)) is the step coefficient: each 1px change in --mui-spacing shifts the value by N px. Pick N from the element's size/role, not just round(P/8): - larger / container-like elements -> larger N (Chip height, Button padding N=2) - smaller / inner glyphs -> smaller N (Chip avatar/icon/deleteIcon N=1) At density the container scales faster than its contents, so inner gaps grow instead of overflowing. Wrap inner-positive offsets that must not go negative at ultra-low density in max(..., 0px) (Chip avatar/icon marginLeft, deleteIcon marginRight). Chip refined accordingly: height keeps N=2; avatar/icon/deleteIcon width/height/ fontSize switched to N=1 with literal offsets; min-clamp applied. Pixel-identical at default; small-density (6px) chips stay proportional.
Internal SwitchBase used by Checkbox + Radio (Switch overrides padding). +1/unit step deliberately matches FormControlLabel marginLeft's -1/unit step (calc(spacing(-1) - 3px)). The two-axis sum stays constant -2px at every --mui-spacing, so checkbox/radio glyphs hold their exact visual position relative to FormControlLabel's edge across all densities. Pixel-identical at default.
Deploy previewhttps://deploy-preview-48585--material-ui.netlify.app/ Bundle size
Check out the code infra dashboard for more information about this PR. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Rolls a
--mui-spacing-driven density system acrosspackages/mui-material/src/*. Every padding / margin / gap (and selected geometry on Chip) that should scale with theme spacing now rides--mui-spacingviacalc(${theme.spacing(N)} ± offset), so users can shift the entire app's density by changing one CSS variable — pixel-identical to today at the default 8px.Decision + spec:
docs/adr/0001-spacing-derived-component-dimensions.md— decision + rationaledocs/adr/spacing-derived-dimensions-spec.md— the rule, worked examples, "choosing N" principle, clamp patterndocs/adr/spacing-derived-rollout-plan.md— per-component checklist (iter 1 done + iter 2 in progress) and the iter-2 backlogScope (≈ 50 components derived; rest audited-skip with rationale):
-11, List gutter16, SwitchBase9Key principles (full detail in the spec):
calc(spacing(N) ± X) ≡ literalat--mui-spacing: 8px. Verified per component with a Playwright harness (scripts/spacing-screenshots/,maxDiffPixels: 0).Nis the step coefficient. Larger containersN=2, smaller inner glyphsN=1, so containers respond visibly while contents scale gently and don't outgrow their parent.memoTheme(({ theme }) => …). Compensation negatives usetheme.spacing(-n) ± offset; inner-positive offsets that must not go negative wrap inmax(…, 0px).Iter-2 backlog (deferred — see plan § "Iteration 2"): coordinated horizontal pass (input inline, list inset, autocomplete reservation, checkbox column), geometry→spacing for Switch/Slider/Fab/PaginationItem, Dialog breakpoint coupling, AvatarGroup default.
Test plan
skipIf(isJsdom())for thecalcvaluedocs/pages/experiments/spacing-fixture.tsx)