You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Worth doing, at a deliberately trimmed scope that now includes spacing. Revised 2026-07-23 after measuring against the real dist/ bundle.
Original decision (2026-07-17) was display + text-align only, with spacing explicitly out of scope. That has been reversed: without responsive spacing the feature has little practical value, and the measured cost of a trimmed spacing set is acceptable. Naming stays plain suffix style (.display-none-md, .mt-md2-lg), matching the framework's existing kebab/property-prefixed helper names.
Effort: M.
What changed since the original plan
Spacing helpers are now generated by src/tools/generate.help.spacing.cjs: 16 properties x 28 sizes = 448 rules, ~24 kB raw. That is the base any responsive variant multiplies.
There is now a real distributable (npm run build:css -> dist/mcss.css, dist/mcss.min.css), so bloat can be measured rather than estimated.
Spacing tokens are fixed px, not fluid clamp() values. Responsive spacing therefore adds capability the framework genuinely lacks. The layout scaffolds (global.layout.css) and --section-spacing cover page-level and section-level cases, but nothing covers per-element spacing changes across breakpoints.
Still true: the framework has no display helpers at all (help.layout.css covers overflow/position/visibility/z-index only), so those get added as part of this.
Each scope below was generated, appended inside @layer helpers, and re-minified through the same esbuild pipeline build-css.mjs uses. Breakpoints are sm/md/lg, mobile-first, in every row.
Scope
Rules added
Minified
Gzip
Brotli
Display + text-align only (original scope)
42
+2.1 kB (+1.4%)
+0.3 kB
+0.1 kB
+ spacing, full cross-product (16 props x 28 sizes)
1386
+62.8 kB (+43%)
+6.5 kB (+30%)
+1.9 kB (+12%)
+ spacing, minus w/h (14 x 28)
1218
+56.5 kB
+5.9 kB
+1.8 kB
+ spacing, logical props only (10 x 28)
882
+41.3 kB
+4.5 kB
+1.3 kB
+ spacing, logical props, sizes 0-xl3 (10 x 16)
522
+23.7 kB (+16%)
+2.7 kB (+12%)
+0.9 kB (+5.5%)
Two costs point in opposite directions. Over the wire it is nearly free: the CSS is repetitive enough that brotli eats it, and even the full 1386-rule version costs 1.9 kB. Raw size is the real bloat: full responsive spacing adds 63 kB minified (+43%) and would grow the spacing helpers alone to ~87 kB, roughly 40% of the framework. For a copy-it-you-own-it framework whose files are meant to be read, that is disproportionate, and not every consumer serves brotli.
Decision: the trimmed set (522 rules, +16% minified)
Ship display helpers, text-align variants, and spacing variants restricted to:
Breakpoints (3):sm (>=480px), md (>=768px), lg (>=1024px)
The trims are principled, not just thrifty:
No responsive ml/mr/pl/pr (base classes stay): responsive layout is exactly where direction-aware logical properties belong. Removes 4 of 16 properties.
No responsive w/h: width-at-breakpoint is a layout concern the grid system and layout scaffolds already own. .w-md1-lg is a smell.
No mega/giga/tera: section-scale spacing is already tokenized (--section-spacing) and themeable at whatever breakpoints a consumer wants.
Escape hatch: consumers who want pl-* variants or tera sizes edit the arrays in generate.help.spacing.cjs and regenerate in their own copy. That is the most mCSS-shaped answer available, and it must be documented as part of this issue.
Implementation plan
1. Base display helpers (new)
In src/styles/framework/help.layout.css (already imported layer(helpers) in mcss.css, no import changes needed), add a DISPLAY section following the existing long-form naming:
.display-none, .display-block, .display-inline, .display-inline-block, .display-flex, .display-grid, plus short aliases .d-none etc., mirroring the .of-*/.overflow-* alias pattern. Property-prefixed names dodge the .grid class collision with the grid system.
Naming wrinkle to document:.display-sm, .display-md, .display-lg etc. already exist in help.typography.css as font-size helpers, so the .display-* prefix does double duty once .display-none-md lands. No actual collisions (display-property values never look like size tokens), but it is an argument for documenting the short .d-* aliases prominently.
2. Responsive variants
Semantics: mobile-first. A -{bp} suffix applies at that breakpoint and above, using the existing @custom-media tokens from settings.media-queries.css (--sm >=480px, --md >=768px, --lg >=1024px).
text-align helpers in help.typography.css -> .text-center-md, .text-start-lg, ... (18 rules)
spacing helpers -> .mt-md2-lg, .p-sm1-md, ... (480 rules: 10 props x 16 sizes x 3 bp)
Down-direction needs no extra classes: class="display-none display-block-md" hides below md. Document this idiom.
Order matters within each file: emit @media blocks in ascending breakpoint order after the base classes so larger breakpoints win at equal specificity.
Names stay unambiguous: size tokens always carry a digit (md2), breakpoint suffixes never do, so .mt-md2-lg parses cleanly.
3. Generator
Extend src/tools/generate.help.spacing.cjs with a responsive pass driven by explicit responsiveProperties / responsiveSizes / breakpoints arrays, kept separate from the existing properties/sizes arrays so the base set and the responsive subset can diverge. Emit the @media blocks after all base rules in the same file. Keep the generated-file header comment pointing at the script.
4. Explicitly still out of scope
Responsive color, opacity, typography-size, and ratio helpers. Responsive w/h and physical-direction margin/padding. Sizes above xl3. The grid system has its own responsive story (col/span attributes, plus #10).
5. Docs & verification
src/content/docs/helpers.mdx: new "Responsive helpers" section covering the naming rule, mobile-first semantics, the hide/show idiom, the covered-property/size table, and how to regenerate a wider set via the generator script.
agents/css.md: record the -{bp} suffix convention and the deliberate property/size restriction.
Future-proofing: suffix names are plain strings (no escaping), and a single safelist regex /-(sm|md|lg)$/ covers them when PurgeCSS returns (companion issue Re-evaluate and re-enable PurgeCSS #13, currently a "don't adopt").
Verify: dev server, resize across 480/768/1024, confirm variants flip at the right widths and helpers still beat component styles (helpers is the last layer). Re-run npm run build:css and confirm the minified delta lands near the projected +23.7 kB.
Assessment
Worth doing, at a deliberately trimmed scope that now includes spacing. Revised 2026-07-23 after measuring against the real
dist/bundle.Original decision (2026-07-17) was display + text-align only, with spacing explicitly out of scope. That has been reversed: without responsive spacing the feature has little practical value, and the measured cost of a trimmed spacing set is acceptable. Naming stays plain suffix style (
.display-none-md,.mt-md2-lg), matching the framework's existing kebab/property-prefixed helper names.Effort: M.
What changed since the original plan
src/tools/generate.help.spacing.cjs: 16 properties x 28 sizes = 448 rules, ~24 kB raw. That is the base any responsive variant multiplies.npm run build:css->dist/mcss.css,dist/mcss.min.css), so bloat can be measured rather than estimated.clamp()values. Responsive spacing therefore adds capability the framework genuinely lacks. The layout scaffolds (global.layout.css) and--section-spacingcover page-level and section-level cases, but nothing covers per-element spacing changes across breakpoints.help.layout.csscovers overflow/position/visibility/z-index only), so those get added as part of this.Measured cost
Baseline
dist/mcss.min.css: 145.2 kB minified, 22.0 kB gzip, 16.2 kB brotli.Each scope below was generated, appended inside
@layer helpers, and re-minified through the same esbuild pipelinebuild-css.mjsuses. Breakpoints aresm/md/lg, mobile-first, in every row.Two costs point in opposite directions. Over the wire it is nearly free: the CSS is repetitive enough that brotli eats it, and even the full 1386-rule version costs 1.9 kB. Raw size is the real bloat: full responsive spacing adds 63 kB minified (+43%) and would grow the spacing helpers alone to ~87 kB, roughly 40% of the framework. For a copy-it-you-own-it framework whose files are meant to be read, that is disproportionate, and not every consumer serves brotli.
Decision: the trimmed set (522 rules, +16% minified)
Ship display helpers, text-align variants, and spacing variants restricted to:
m,mt,mb,mis,mie,p,pt,pb,pis,pie0,xs1-3,sm1-3,md1-3,lg1-3,xl1-3sm(>=480px),md(>=768px),lg(>=1024px)The trims are principled, not just thrifty:
ml/mr/pl/pr(base classes stay): responsive layout is exactly where direction-aware logical properties belong. Removes 4 of 16 properties.w/h: width-at-breakpoint is a layout concern the grid system and layout scaffolds already own..w-md1-lgis a smell.--section-spacing) and themeable at whatever breakpoints a consumer wants.Escape hatch: consumers who want
pl-*variants or tera sizes edit the arrays ingenerate.help.spacing.cjsand regenerate in their own copy. That is the most mCSS-shaped answer available, and it must be documented as part of this issue.Implementation plan
1. Base display helpers (new)
In
src/styles/framework/help.layout.css(already importedlayer(helpers)inmcss.css, no import changes needed), add a DISPLAY section following the existing long-form naming:.display-none,.display-block,.display-inline,.display-inline-block,.display-flex,.display-grid, plus short aliases.d-noneetc., mirroring the.of-*/.overflow-*alias pattern. Property-prefixed names dodge the.gridclass collision with the grid system.Naming wrinkle to document:
.display-sm,.display-md,.display-lgetc. already exist inhelp.typography.cssas font-size helpers, so the.display-*prefix does double duty once.display-none-mdlands. No actual collisions (display-property values never look like size tokens), but it is an argument for documenting the short.d-*aliases prominently.2. Responsive variants
-{bp}suffix applies at that breakpoint and above, using the existing@custom-mediatokens fromsettings.media-queries.css(--sm>=480px,--md>=768px,--lg>=1024px)..display-none-md,.d-flex-lg, ... (18 rules, aliased so 18 selectorsx2 names)help.typography.css->.text-center-md,.text-start-lg, ... (18 rules).mt-md2-lg,.p-sm1-md, ... (480 rules: 10 props x 16 sizes x 3 bp)class="display-none display-block-md"hides below md. Document this idiom.@mediablocks in ascending breakpoint order after the base classes so larger breakpoints win at equal specificity.md2), breakpoint suffixes never do, so.mt-md2-lgparses cleanly.3. Generator
Extend
src/tools/generate.help.spacing.cjswith a responsive pass driven by explicitresponsiveProperties/responsiveSizes/breakpointsarrays, kept separate from the existingproperties/sizesarrays so the base set and the responsive subset can diverge. Emit the@mediablocks after all base rules in the same file. Keep the generated-file header comment pointing at the script.4. Explicitly still out of scope
Responsive color, opacity, typography-size, and ratio helpers. Responsive
w/hand physical-direction margin/padding. Sizes abovexl3. The grid system has its own responsive story (col/spanattributes, plus #10).5. Docs & verification
src/content/docs/helpers.mdx: new "Responsive helpers" section covering the naming rule, mobile-first semantics, the hide/show idiom, the covered-property/size table, and how to regenerate a wider set via the generator script.agents/css.md: record the-{bp}suffix convention and the deliberate property/size restriction./-(sm|md|lg)$/covers them when PurgeCSS returns (companion issue Re-evaluate and re-enable PurgeCSS #13, currently a "don't adopt").npm run build:cssand confirm the minified delta lands near the projected +23.7 kB.