guides on the page builder, and negative horizontal on a block

The measure was drawn on Layout only, and this is the screen where things are
placed against it. It also answers the question a negative margin asks: a block
escaping the gutter has to be seen escaping it, against the line it crossed.

Horizontal padding now offers negatives, built from the same scale so there is
still one list.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-01 20:09:25 +01:00
co-authored by Claude Opus 5
parent c1cf908206
commit 5b4571ddd7
2 changed files with 30 additions and 3 deletions
+14 -1
View File
@@ -134,7 +134,20 @@ export const BlockSettings = ({
<option value="">
{breakpoint === 'base' ? 'None' : from === null ? 'Inherit (none)' : `Inherit (${from})`}
</option>
{steps.map((step) => (
{/*
Horizontal offers NEGATIVES, vertical does not.
A negative horizontal is how a block escapes the gutter its band applied — a video
that runs to the edge of the window inside an inset band. It becomes `-mx-*`, which
cancels the band's `px-*` exactly, so the value to pick is the band's own number.
Vertically there is nothing to cancel: a band's vertical padding is the room around
the whole band, and a block pulling itself into the one above is a bug.
*/}
{(axis === 'x'
? [...steps.filter((step) => step > 0).map((step) => -step).reverse(), ...steps]
: steps
).map((step) => (
<option key={step} value={String(step)}>
{step}
</option>
+16 -2
View File
@@ -7,7 +7,7 @@ import { useClient } from 'hooks/useClient';
import { readableError } from '../errors';
import { SitePreview, useDevPort } from '../SitePreview';
import { usePreviewable, useViewMode } from '../ViewMode';
import { paintSelection, type CanvasComponent, type CanvasSection } from '../canvas';
import { paintMeasure, paintSelection, type CanvasComponent, type CanvasSection } from '../canvas';
/*
What the Spacing dialog calls a block, by kind.
@@ -226,7 +226,16 @@ export const PageBuilderPanel = () => {
const onDocument = useCallback(
(frameDocument: Document) => {
frameDoc.current = frameDocument;
return paintSelection(frameDocument, {
/*
The measure, here as well as on Layout.
Where the content column ends is invisible until something is placed against it, and this is
the screen where things are placed. It also answers the question a negative margin asks: a
block escaping the gutter has to be seen escaping it, against the line it crossed.
*/
const measure = paintMeasure(frameDocument);
const selection = paintSelection(frameDocument, {
sections,
onAddSection: () => handlersRef.current.add(),
onDeleteSection: (section) => handlersRef.current.remove(section.id),
@@ -245,6 +254,11 @@ export const PageBuilderPanel = () => {
onSpaceSection: (section) => handlersRef.current.spaceSection(section),
onSpaceComponent: (component) => handlersRef.current.spaceComponent(component),
});
return () => {
selection();
measure();
};
},
// `mode` is in here so switching to View gives SitePreview a NEW function, which runs the previous
// paint's cleanup and then paints nothing. That is what removes the chrome without a reload.