spacing is three bands, not one gutter
Top Nav, Sections and Footer each get their own horizontal padding, plus the one space that band owns: the room under the rule, the gap between bands, the room above the footer. The preview marker now names its band, so a change moves one band and not all three. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,12 +6,12 @@ import { readableError } from '../errors';
|
||||
import { useFlash } from '../useFlash';
|
||||
import { useSaveTarget } from '../SaveTarget';
|
||||
import { usePreviewable, useViewMode } from '../ViewMode';
|
||||
import { applyGutter, paintMeasure } from '../canvas';
|
||||
import { applySpacing, paintMeasure, type SpacingDraft } from '../canvas';
|
||||
import { SitePreview, useDevPort } from '../SitePreview';
|
||||
import { DeviceToggle } from '../DeviceToggle';
|
||||
import { useEditorSlot } from '../useEditorSlot';
|
||||
import { type LayoutBand } from './PaddingTab';
|
||||
import { GutterTab, type Gutter } from './GutterTab';
|
||||
import { SpacingTab } from './SpacingTab';
|
||||
|
||||
/**
|
||||
* The shell every page renders inside: the menu, the footer, the colours and the typefaces.
|
||||
@@ -59,8 +59,8 @@ type Layout = {
|
||||
sections: LayoutBand[];
|
||||
/** The steps a padding may take, sent by the API so this file holds no copy of the scale. */
|
||||
padding_steps: number[];
|
||||
/** The site's horizontal gutter, per breakpoint. */
|
||||
padding_x: Gutter;
|
||||
/** Where the space goes, band by band. See SpacingTab, and the site's own spacingClasses. */
|
||||
spacing: SpacingDraft;
|
||||
/**
|
||||
* Which face plays each role when nothing has been chosen, read from the site's own stylesheet.
|
||||
*
|
||||
@@ -111,21 +111,6 @@ const themeProperties = (draft: Layout): Record<string, string | null> => ({
|
||||
'--font-display': draft.font_display && `"${draft.font_display}", sans-serif`,
|
||||
});
|
||||
|
||||
/**
|
||||
* The gutter as the classes the site would emit.
|
||||
*
|
||||
* Mirrors `gutterClasses` in the site's layout-settings.ts, and is a copy for the same reason
|
||||
* `themeProperties` is: showing a change before it is saved means producing it here. The check is the
|
||||
* same — save, and if the live preview and the saved page disagree, this copy is the wrong one.
|
||||
*/
|
||||
const gutterClasses = (gutter: Gutter): string => {
|
||||
const prefix = { base: '', md: 'md:', lg: 'lg:' } as const;
|
||||
return (['base', 'md', 'lg'] as const)
|
||||
.filter((breakpoint) => typeof gutter[breakpoint] === 'number')
|
||||
.map((breakpoint) => `${prefix[breakpoint]}px-${gutter[breakpoint]}`)
|
||||
.join(' ');
|
||||
};
|
||||
|
||||
const COLOURS = [
|
||||
{ key: 'background' as const, label: 'Background', fallback: '#ffffff' },
|
||||
{ key: 'foreground' as const, label: 'Text', fallback: '#000000' },
|
||||
@@ -187,7 +172,7 @@ export const LayoutPanel = () => {
|
||||
made in a build script or a stylesheet.
|
||||
*/
|
||||
const WRITABLE = [
|
||||
'padding_x',
|
||||
'spacing',
|
||||
'background',
|
||||
'foreground',
|
||||
'accent',
|
||||
@@ -234,24 +219,26 @@ export const LayoutPanel = () => {
|
||||
at the page.
|
||||
*/
|
||||
/*
|
||||
The measure, and the gutter being drafted.
|
||||
The measure, and the spacing being drafted.
|
||||
|
||||
`gutter` is in the dependencies, so changing it hands SitePreview a new function: its effect runs
|
||||
the previous application's cleanup — which puts the original classes back — and then applies the new
|
||||
ones. A padding shows in the page as it is chosen, like a colour does, with no save and no reload.
|
||||
`spacingKey` is in the dependencies, so changing a number hands SitePreview a new function: its
|
||||
effect runs the previous application's cleanup — which puts the original classes back — and then
|
||||
applies the new ones. A gap shows in the page as it is chosen, like a colour does, with no save.
|
||||
*/
|
||||
const gutter = draft ? gutterClasses(draft.padding_x) : '';
|
||||
// Serialised, so the callback is rebuilt when a NUMBER changes rather than when the object identity
|
||||
// does — a fresh `{}` every render would re-apply the whole preview on every keystroke elsewhere.
|
||||
const spacingKey = JSON.stringify(draft?.spacing ?? {});
|
||||
|
||||
const onDocument = useCallback(
|
||||
(frameDocument: Document) => {
|
||||
const measure = paintMeasure(frameDocument);
|
||||
const spacing = applyGutter(frameDocument, gutter);
|
||||
const space = applySpacing(frameDocument, JSON.parse(spacingKey) as SpacingDraft);
|
||||
return () => {
|
||||
spacing();
|
||||
space();
|
||||
measure();
|
||||
};
|
||||
},
|
||||
[gutter],
|
||||
[spacingKey],
|
||||
);
|
||||
|
||||
const [, setEditor] = useEditorSlot();
|
||||
@@ -364,21 +351,11 @@ export const LayoutPanel = () => {
|
||||
<div className="mb-3">
|
||||
<DeviceToggle size="section" />
|
||||
</div>
|
||||
{/*
|
||||
The site's gutter, not a per-band padding.
|
||||
|
||||
PaddingTab is the per-band control and it stays — for the pages, where a band really
|
||||
does want its own spacing. Here the three wrappers share one measure and should share
|
||||
one number.
|
||||
|
||||
All three widths at once, and deliberately NOT tied to the device toggle: a gutter is a
|
||||
progression, 4 then 8 then 12, and it is judged as one. Reading it off three separate
|
||||
visits to the same control is how a site ends up tighter on a tablet than on a phone.
|
||||
*/}
|
||||
<GutterTab
|
||||
gutter={draft.padding_x}
|
||||
{/* Three bands. PaddingTab is still the per-SECTION control, for the pages. */}
|
||||
<SpacingTab
|
||||
spacing={draft.spacing}
|
||||
steps={draft.padding_steps}
|
||||
onChange={(padding_x) => patch({ padding_x })}
|
||||
onChange={(spacing) => patch({ spacing })}
|
||||
/>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
Reference in New Issue
Block a user