Skip to content

Padding shorthands show in the spacing box, and padding edits stay compact - #36

Merged
flowtricks merged 1 commit into
flowtricks:mainfrom
LilianBarbe:spacing-box-padding-shorthands
Oct 13, 2026
Merged

flowtricks merged 1 commit into
flowtricks:mainfrom
LilianBarbe:spacing-box-padding-shorthands

Conversation

@LilianBarbe

Copy link
Copy Markdown
Contributor

The problem

The spacing box has one field per side, so a rule written padding: 1rem 2rem filled none of them: the box read as empty while the padding applied on the page, and the shorthand only showed under Custom properties.

Dragging a side of such a rule then appended a longhand beside the shorthand it overrode. Four drags left five declarations.

The change

Reading. The resolver projects padding, padding-block, padding-inline and margin (one to four values) onto the sides they set. The box shows them, and an edit goes to the rule that holds the shorthand. The shorthand stays a property of its own, still edited and cleared whole under Custom properties. Reading never rewrites the source.

Writing. A committed padding edit rewrites the rule's own padding in its shortest form, where its first padding declaration stood:

  • padding when the four sides agree,
  • else padding-block / padding-inline for each pair that agrees,
  • else the sides.

Only what the rule declares is compacted. Alt-click on a side grouped into a shorthand splits it and keeps the other sides.

The new module is src/features/style/model/spacingShorthand.ts. It is wired into resolved.ts (reading), and into cssRegions.ts and useEditorRuleEdits.ts (writing).

Choices worth a look

  • var() is left as written. A multi-side value holding a var() or env() may substitute several lengths, so it is neither split nor compacted until every side overrides it. A var() side is never grouped either: the shorthand it would make is one the next edit could not split again, so the result would depend on the order of the edits.
  • Axis properties. Two agreeing pairs are written padding-block + padding-inline, not padding: a b. Sides are physical, as the box and the canvas bands already are.
  • !important inside a rule. A later declaration now wins unless an earlier one is !important and it is not. The per-rule fold took the last declaration regardless.
  • Margin is read only. A margin edit still appends a longhand; compacting it is left out of this PR.

Tests

  • test/renderer/style/spacingShorthand.test.js: 16 cases on the model, through the real parser, cascade and resolver. They cover the values the box cannot read side by side, !important, queries, and the assertion on a non-padding property.
  • test/renderer/style/spacingShorthandPanel.test.js: the real EmbedEditor in jsdom, with Shift, Alt, plain and reset drags, checked against the CSS written. It fails without the change.
  • npm run check:changed passes.

🤖 Generated with Claude Code

The spacing box has one field per side, so `padding: 1rem 2rem` reached
none of them while still applying on the page: the box read as empty and
the shorthand sat in Custom properties. Dragging a side of such a rule
appended a longhand beside the shorthand it overrode, and four drags left
five declarations.

Reading: the resolver projects `padding`, `padding-block`,
`padding-inline` and `margin` (one to four values) onto the sides they
set, so the box shows them and an edit goes to the rule that holds the
shorthand. The shorthand stays a property of its own and is still edited
and cleared whole; reading never rewrites the source. Within a rule a
later declaration wins unless an earlier one is `!important` and it is
not, which the per-rule fold did not account for.

Writing: a committed padding edit rewrites the rule's own padding in its
shortest form, where its first padding declaration stood: `padding` when
the four sides agree, else `padding-block` / `padding-inline` for each
pair that agrees, else the sides. Only what the rule declares is
compacted. A reset (Alt-click) of a side grouped into a shorthand splits
it and keeps the other sides.

Two cases are left as written on purpose. A multi-side value holding a
var() or env() may substitute several lengths, so it is neither split nor
compacted until every side overrides it. And a var() side is never
grouped, since the shorthand it would make is one the next edit could
not split again: the result would depend on the order of the edits.

Margin is read only: a margin edit still appends a longhand.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@flowtricks
flowtricks merged commit 44e1249 into flowtricks:main Oct 13, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants