docs(fuselage): propose enforcing the spacing scale

Draft
GG
1d ago
+452-0

Proposed changes (including videos or screenshots)

The spacing scale is x4, x8, x12, x16, x20, x24, x32 and x40. Fuselage doesn't enforce it: there are no spacing tokens, spacing() accepts any x<n> or number, and the margin, padding and gap props take any value.

This draft adds:

  • docs/proposals/fuselage-spacing-scale.md:
    • what develop uses today;
    • where each 2px comes from and why x2 stays off the scale;
    • how space, radius and border width split into token families;
    • a plan of one pull request per step;
    • the list of every off-scale site as file:line.
  • packages/fuselage/scripts/scan-spacing.mjs: an AST scan that reproduces the numbers. Run node packages/fuselage/scripts/scan-spacing.mjs [out.json] from the repository root.

Key numbers on develop:

  • 1,383 spacing literals, 115 of them off the scale. Most are 2px (40) and 10px (36).
  • About 22 of the 2px values are gap emulation, which becomes gap='x4' with the same pixels.
  • 1,043 on-scale values are written as numbers rather than tokens.
  • Sizes use x<n> 827 times, with 68 distinct values, but they shouldn't be tokens.

Handoff checklist

This PR only carries the proposal. Each step below is its own PR, stacked as docs/change-organization.md describes.

  • Scale: add spacing.json to fuselage-tokens, export SpacingToken, and have spacing() read it. No runtime change.
  • 2px:
    • gap emulation becomes gap='x4' on the parent, with the same pixels;
    • focus-ring padding/margin moves into Scrollable and ModalContent;
    • optical nudges get a screenshot review.
  • 10, 14, 28 and 36px, grouped as the proposal suggests, with screenshots before and after for each view:
    • Marketplace logs filters;
    • Outbound skeleton and forms;
    • modals;
    • Account and E2E;
    • the scattered ones.
  • Values above the scale (56, 60, 64, 96, 144) move out of the spacing props into explicit CSS.
  • Codemod on-scale numbers to tokens (8 → 'x8'): same pixels, one commit per package.
  • Codemod size x<n> values to numbers (width='x120' → width={120}): same pixels.
  • Narrow the spacing prop types to the scale, its negatives, 0, 'none' and 'auto'. Add a development-only warning in spacing(), plus a major changeset for @rocket.chat/fuselage.
  • Radius cleanup:
    • borderRadius={4} becomes 'medium';
    • the about 30 raw CSS radii move to the radius tokens;
    • 5px and 3px go to design.
  • Stroke compensations in Button, Banner and Sidebar read borderWidth('medium').
  • Re-run the scan; the off-scale count must reach 0.

Still to decide with design:

  • Rounding of 10, 14, 28 and 36px: one rule, or site by site?
  • Whether numbers equal to a step (8) stay accepted as an alias of the token.

Issue(s)

https://rocketchat.atlassian.net/browse/ARCH-2568

Steps to test or reproduce

node packages/fuselage/scripts/scan-spacing.mjs from the repository root, with dependencies installed. It prints the totals and the off-scale values by area. The 4 ResponsiveBar hits are nivo chart props, a known false positive.

Further comments

The Tamagui pilot (#42942) builds on this. Its space tokens become the eight steps plus their negatives, instead of about 400 x<n> tokens, each injected as a :root custom property.