codegouvfr / codegouvfr/react-dsfr

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

Open
#508 0 comments 0 reactions 1 assignee Claimed by @kevbarns View on GitHub
Dominant language
TypeScript
Stars
496
Forks
99
Avg merge
15h 36m
Merged PRs (30d)
4

Description

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-p` → **fall 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.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.