From fc3b9cca4a8118c75edbf2fbb2b2d3bc8243f167 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Tue, 1 Sep 2026 08:15:01 +0100 Subject: [PATCH] Sections and Footer get a Gap, three fields across MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The room between a band's columns: gap-8 in a two-column section, gap-x-24 gap-y-10 in the footer. Both were written into the template that drew them. The canvas now SKIPS a utility nothing has chosen instead of stripping it. The old rule lied twice — a section's own padding vanished while the panel was open, and a band with no chosen gap lost the site's default. Co-Authored-By: Claude Opus 5 (1M context) --- web/canvas.ts | 21 ++++++++++++++++----- web/sections/SpacingTab.tsx | 6 +++++- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/web/canvas.ts b/web/canvas.ts index 909c8e6..def133a 100644 --- a/web/canvas.ts +++ b/web/canvas.ts @@ -599,14 +599,13 @@ export function paintMeasure(document: Document): () => void { * header, `py-*` from a band. Everything else on the element survives, which is what makes it safe to * do this to a `
` carrying a border and a background as well. * - * The one exception is a SECTION, whose own padding is also a `px-*` on the marked element — so while - * the panel is open its padding reads as the site gutter. Harmless on the layout page, whose bands have - * none, and that page is the only one this runs against. + * A target with nothing chosen is skipped entirely — see the note in the loop. That is what keeps a + * section's own padding, and the site's default gap, from disappearing while the panel is open. */ export type SpacingDraft = { header?: { paddingX?: Steps; marginBottom?: Steps }; - sections?: { paddingX?: Steps; paddingY?: Steps }; - footer?: { paddingX?: Steps; marginTop?: Steps }; + sections?: { paddingX?: Steps; paddingY?: Steps; gap?: Steps }; + footer?: { paddingX?: Steps; marginTop?: Steps; gap?: Steps }; }; export type Steps = Partial>; @@ -630,6 +629,8 @@ const TARGETS = [ { selector: '[data-gutter="footer"]', utility: 'px', read: (s: SpacingDraft) => s.footer?.paddingX }, { selector: '[data-space="header"]', utility: 'mb', read: (s: SpacingDraft) => s.header?.marginBottom }, { selector: '[data-space="footer"]', utility: 'mt', read: (s: SpacingDraft) => s.footer?.marginTop }, + { selector: '[data-gap="sections"]', utility: 'gap', read: (s: SpacingDraft) => s.sections?.gap }, + { selector: '[data-gap="footer"]', utility: 'gap', read: (s: SpacingDraft) => s.footer?.gap }, ] as const; export function applySpacing(document: Document, spacing: SpacingDraft): () => void { @@ -639,6 +640,16 @@ export function applySpacing(document: Document, spacing: SpacingDraft): () => v const pattern = new RegExp(`^(?:md:|lg:)?${target.utility}-\\d+$`); const classes = scaleClasses(target.utility, target.read(spacing)).split(/\s+/).filter(Boolean); + /* + Nothing chosen means DON'T TOUCH IT, rather than strip it. + + Stripping was the old rule and it lied twice: a section's own `px-*` vanished while the panel was + open, and a band with no chosen gap lost the site's default `gap-8` — so the preview showed a + layout the page would never render. What the panel is drafting is an override, and an override + that has not been set is not an instruction to remove anything. + */ + if (!classes.length) continue; + for (const element of document.querySelectorAll(target.selector)) { touched.push([element, element.className]); const kept = element.className.split(/\s+/).filter((name) => name && !pattern.test(name)); diff --git a/web/sections/SpacingTab.tsx b/web/sections/SpacingTab.tsx index d10f12d..2ceb017 100644 --- a/web/sections/SpacingTab.tsx +++ b/web/sections/SpacingTab.tsx @@ -46,6 +46,7 @@ const BANDS = [ fields: [ { key: 'paddingX' as const, label: 'Horizontal padding' }, { key: 'paddingY' as const, label: 'Vertical padding' }, + { key: 'gap' as const, label: 'Gap' }, ], }, { @@ -54,6 +55,7 @@ const BANDS = [ fields: [ { key: 'paddingX' as const, label: 'Horizontal padding' }, { key: 'marginTop' as const, label: 'Margin top' }, + { key: 'gap' as const, label: 'Gap' }, ], }, ]; @@ -94,7 +96,9 @@ export const SpacingTab = ({ {BANDS.map((band) => (

{band.label}

-
+ {/* Three across, and the same grid for every band — Top Nav has two fields and leaves the + third empty, so the columns line up down the section rather than staggering. */} +
{band.fields.map((field) => { const current = (spacing as Record | undefined>)[band.key]?.[field.key]; const own = current?.[key];