Skip to content

optimize-css: opt-in trimming of the spacing utility grid in core (~78 kB raw / ~11.5 kB gzip) #508

Description

@kevbarns

Follow-up to #304 and #505. With component trimming in place (#505, now optimize-css), the consumer-side floor of the DSFR stylesheet is core — shipped as a single block regardless of usage.

Measured breakdown of core.main.min.css (dsfr 1.14.x, 172 kB raw / 25 kB gzip)

Family Raw weight Notes
Base / reset / HTML elements ~62 kB base typography, forms, lists — hard to split safely
Spacing utilities fr-m*-* / fr-p*-* ~39 kB top-level (927 classes) + a large share of the 29 kB min-width:48em block (1,053 responsive selectors) exhaustive grid of values × sides × breakpoints
All @media blocks 45.3 kB 29.2 kB for 48em, 5.1 kB forced-colors
fr-grid / fr-col 6.9 kB + 196 responsive selectors
Typo utilities (fr-text-*, fr-h*, fr-display) ~1.6 kB
@font-face 1.9 kB negligible

Most apps use a small fraction of the spacing grid, so ~50-60 kB raw (~6-8 kB gzip estimated) is the one core family that is both large and trimmable per class with reasonable guarantees: unlike component CSS, these classes are never toggled by the DSFR runtime (data-fr-js-*), so the failure mode of rule-level purging that #505 deliberately avoided does not apply in the same way. The remaining risk is dynamically built class names (`fr-mt-${x}`).

Proposal: opt-in --trim-spacing-utilities in optimize-css

Same fail-safe philosophy as #505 — when in doubt, include:

  1. Scan sources for literal fr-(m|p)[trblxvy]?-* classes → allowlist of used utilities.
  2. Detect dynamic forms — any template literal or concatenation whose static prefix matches fr-m/fr-pfall back to including the whole family, with an explicit log line (and a non-silent mode where the fallback is observable in CI).
  3. Separate opt-in from component trimming, so enabling one never silently enables the other.

Tests would need the false-positive cases first (a URL or comment mentioning fr-mt-, an over-broad file that contains everything, the dynamic near-miss), not only the nominal ones.

If the approach sounds right I can put together a PR along the lines of #505/#506.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions