Commit Graph
87 Commits
Author SHA1 Message Date
pastilhasandClaude Opus 5 5b4571ddd7 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>
2026-09-01 20:09:25 +01:00
pastilhasandClaude Opus 5 c1cf908206 the media preview takes the column
A strip of a photograph tells you it is a photograph and not which one. Full
width, its own aspect, under the button rather than beside it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 20:01:10 +01:00
pastilhasandClaude Opus 5 6ab2fab4ac a media block shows what is in it
"Choose media…" and nothing else asks somebody to open a dialog to find out what
they already chose. The thumbnail and the filename answer it where they stand.

A video shows its poster rather than playing: the question is which file this is,
and a frame answers that.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 19:58:04 +01:00
pastilhasandClaude Opus 5 9c97773cf7 selecting a block shows its settings, not a modal
editComponent means "edit what is IN this block", and for media and galleries
that is a dialog over the whole screen. Firing it on selection threw one at you
before you had asked for it — those kinds get a button in the settings instead.

Free text keeps opening inline, because its editor is in the same column and
opening it IS showing the settings.

The text editor now only renders when it belongs to the selected block. A stale
one showed the previous block's words under the new block's title.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 19:55:05 +01:00
pastilhasandClaude Opus 5 1ec361d006 a block's spacing sits with its title; page settings need the page selected
Spacing had a link in the chrome painted onto the page and lost its door with the
rest of it. It belongs beside the heading: both are properties of the selected
block, and one of the two is not worth a place of its own to go to.

The page's name and address now show only when the page row itself is selected.
With a band or a block open they sat above an editor about something else and
read as belonging to it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 19:50:18 +01:00
pastilhasandClaude Opus 5 361684b8c5 every block is selectable, and has a Title
canEditComponent answers "is there content to type", and a heading is not
content — so the blocks with no editor of their own are now selectable too, and
get the heading alone.

The live draft writes into [data-block] when there is one. A titled component
renders as a wrapper, and writing into the wrapper would replace the heading with
the paragraph as somebody typed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 19:46:59 +01:00
pastilhasandClaude Opus 5 aaf767087e selecting a band opens its settings
Name and Title, in the editor column — the band row in the nav had only scrolled
the preview until now.

Both, not the title alone: a form showing one of two names invites somebody to
change the wrong one, and `name` could only be set when the band was created.

Last in the editor's order, so a block or a spacing editor wins — those were
opened deliberately, and the band is merely still selected underneath them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 19:41:26 +01:00
pastilhasandClaude Opus 5 949d61efc6 a band lists its blocks, and selection is the URL
A fourth level under the open band only — a page of six bands with four blocks
each is thirty rows, and the nav stops being somewhere you can find anything.

Blocks are named by the same list the picker offers, so a thing is called the
same word wherever it appears.

Clicking a block in the PAGE now writes that address too, rather than calling the
handler directly. Both ways of picking a block are the same act, so neither can
disagree with the other about what is selected — and closing it is the band row.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 09:49:44 +01:00
pastilhasandClaude Opus 5 7c4c3f48a8 the open page lists its bands in the nav
A third level, under the selected page only — every page has bands, and listing
all of them would make the nav longer than the site.

Announced by the builder rather than fetched again: it already reads them to know
what it is looking at in the document, and it mounts exactly when a page is open.
Cleared on unmount, or the nav keeps listing the last page's bands.

Clicking one writes a third URL segment and the preview scrolls to it, keyed on
the render count too so the position survives a save replacing the frame.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 09:45:07 +01:00
pastilhasandClaude Opus 5 2eb9142484 the page builder draws nothing on the page
A bar on every band, Edit / Spacing / Delete above every block, dotted outlines
around blocks and columns, an add-zone at the end. Look at that and you are
looking at the editor, not the page — on the screen whose whole purpose is
judging the page.

A click selects a block and its editor opens in the panel beside the preview,
where every other control now lives. data-fofos-component stays: it is how the
panel writes a draft into the page as somebody types.

Adding a section, adding a block, deleting either and opening a spacing editor
have no door any more. The handlers are untouched and nothing calls them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 09:40:34 +01:00
pastilhasandClaude Opus 5 dfc22d2d2c a page's settings sit above the editor
Menu item, address and page title — what the retired /fofos-admin/pages panel
held, moved to the column where everything else about that page is edited.

Above the device toggle, because the toggle scopes what is under it to one width
and none of these three have widths. Read from the URL rather than announced:
they belong to the page whatever is selected inside it, so picking a block must
not replace them.

A changed slug changes the page's own address, so the panel follows it — left
alone the URL would name a page the list no longer holds.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 09:09:35 +01:00
pastilhasandClaude Opus 5 aab536059b the empty size option names the size
"Inherit (default)" answers a question nobody asked. What somebody wants from
that dropdown is what it is now — and a label that will not say makes them change
the setting to find out, which is the one way to lose the answer.

It reads Default (4xl) at the smallest width and Inherit (2xl) above it. The
stylesheet's own sizes come from the API, which reads them out of styles.css.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 08:48:51 +01:00
pastilhasandClaude Opus 5 fc3b9cca4a Sections and Footer get a Gap, three fields across
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) <noreply@anthropic.com>
2026-09-01 08:15:01 +01:00
pastilhasandClaude Opus 5 33f2ea4663 the editor sections fall to one column when it is narrow
A container query on the scroller, once, so a new section is responsive by being
in the column rather than by remembering to say so. Below 320px the two-column
sections stack instead of truncating every select to "Martia…".

Colors stays flex-wrap: three swatches in a two-column grid is 2 and 1.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 08:10:05 +01:00
pastilhasandClaude Opus 5 905e2e3325 one list of roles, named for what they are
Fonts and Font Sizes are the same four things, so they now read in the same order
under the same names — Menu, Titles, Headings, Body — from one list.

font_display is the largest statements and font_title is a section heading, which
is the reverse of what the words suggest. The labels say what the reader sees.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 08:08:05 +01:00
pastilhasandClaude Opus 5 5f7e4d7d61 Font Sizes, per role, per width
A fifth section between Fonts and Spacing. The same four roles the families use,
so a template asks for a role and gets both — and a role used at two sizes on
purpose collapses to one, which was the trade taken over a separate list of text
roles.

The live preview builds the same stylesheet the site renders, because a size is
three values and an inline style cannot carry a media query.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 08:03:21 +01:00
pastilhasandClaude Opus 5 8f26d0d3b6 Spacing says which width it is for
Every field in there writes one breakpoint, chosen by the tabs at the top of the
panel. Without the name the same six selects mean six different things depending
on something above them.

One span, not two: the trigger is justify-between and puts the chevron last.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 07:54:04 +01:00
pastilhasandClaude Opus 5 2a8f3b5814 no hand holding
The line under every nav row, the line under every font select, and the caption
under the preview. Each explained a control to somebody who has already found
it, and the caption was wrong as well — there is no Save to press.

The nav rows are one line now, so they centre.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 07:50:25 +01:00
pastilhasandClaude Opus 5 939f5bf80b a section title outranks its own fields
text-xs was the size of the labels under it, so the three headings read as one
more row of form furniture. Bigger and heavier — weight is the signal that
survives at this density without adding a second thing to look at.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 07:46:45 +01:00
pastilhasandClaude Opus 5 c0c88a434d the toggle drops its labels when the panel is narrow
A container query, not a screen one: this sits in a workspace panel whose width
is a divider somebody drags, so the window says nothing about the room. Below
240px the icons stand alone and the word is the tooltip.

Every editor section starts open. It opened on one, from ?tab=, which is what
tabs want — these are not tabs, and a collapsed column asks somebody to go
looking for the setting they came for. ?tab= is gone with it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 07:44:15 +01:00
pastilhasandClaude Opus 5 d218d3c7a8 the device toggle is three labelled tabs
Icons alone told three things apart by proportion at 12 pixels. Full width, one
of three filled, and the word from DEVICES.

The `size` prop is gone with the bar that needed the small one, and so is the
second copy inside Spacing — the panel header scopes the whole editor now.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 07:41:09 +01:00
pastilhasandClaude Opus 5 b5ca67e68e every control writes itself
The Save button is gone, and so is the SaveTarget context that announced it.
Every screen here is a preview of the thing being edited, so a change is already
visible before it is written — a button then asks somebody to confirm what they
can see.

useAutosave debounces a burst into one request. It takes the unsaved state as a
string rather than a flag, so a failed save is not retried on the next render,
and it flushes on unmount so a section change loses nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 07:34:34 +01:00
pastilhasandClaude Opus 5 a7fc07c52f sections are paths, not a query string
The admin declares its routes in the manifest, so /fofos-admin/layout/header and
/fofos-admin/pages/home are real URLs. One reader takes the two segments after
the mount; links carry no query string at all, so nothing leaks between
sections.

What stays in the query is how you are looking — device, tab, view — rather than
at what.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 04:57:29 +01:00
pastilhasandClaude Opus 5 4d7162363b sections take vertical padding, not a gap
Space between became Vertical padding: space-y hangs its margin below the box,
and the box's bottom border is the separator, so the room never landed above a
rule. The canvas now rewrites both axes on a band.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 04:36:22 +01:00
pastilhasandClaude Opus 5 8307591712 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>
2026-09-01 04:24:05 +01:00
pastilhas 9c904040c4 label the padding as Top Nav's 2026-09-01 04:05:25 +01:00
pastilhas ed900bc2d3 one padding value, for the device on the toggle
The three columns became one select. Which breakpoint it writes comes from the device control above it,
so the value you see is the value for the view you are looking at.
2026-09-01 04:02:37 +01:00
pastilhas 89380a69d6 the device control spans the section
No label, full width, three across. It scopes the fields below it, so it reads as their header.
2026-09-01 03:55:12 +01:00
pastilhasandClaude Opus 5 852c53dd09 one device control, read from the URL
Spacing's settings are per width, so the section has to say which width it is editing — and the answer
must be the one the preview is already showing, not a second selector able to disagree with it.

It reads `?device=` rather than the ViewMode context, and that is not a preference. The editor is a
SIBLING panel: an element built inside the detail panel resolves context where it is RENDERED, so a
toggle handed over would look up a tree with no provider in it and quietly show the default forever.

The URL is the shared state, which means there is nothing to share. Three components read one
parameter, so they cannot disagree, and the preview follows without being subscribed to anything — it
reads the same value. More editor sections can take the control by rendering it.

The bar's own toggle is now the same component. It had its own copy of the options and the handler,
which is exactly how two controls for one thing start to differ.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 03:53:02 +01:00
pastilhas 53d6a0324d Padding is called Spacing
The heading only. What it does is unchanged for now — the word is widening ahead of the behaviour,
which is the right order: the section is about to hold more than one kind of space, and Padding names
only one of them.
2026-09-01 03:49:16 +01:00
pastilhasandClaude Opus 5 5cc4742119 Colors, Fonts, and two by two
The two headings take the site's own spelling and the shorter word. "Typefaces" is the more precise
term and "Fonts" is the one everybody reaches for; a section heading is a signpost, not a definition.

The fonts go two by two. Four across existed to keep this section the same height as Colours, because
as tabs they shared one grid cell and a taller panel moved everything below it. Stacked sections do not
have that constraint — each is as tall as it is and nothing above it moves — so the reason expired with
the tabs.

What four across cost was the labels: in a panel this narrow every select truncated to "Martia…", so
the row said four times that a font had been chosen and never which one. Two columns give each the room
to be read, which is the only thing the control is for.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 03:31:03 +01:00
pastilhasandClaude Opus 5 dd10a4df45 Layout's controls are stacked sections, not tabs
Three tabs became three collapsible sections in a column, because there are going to be more than
three. A tab strip works while the names fit on one line and degrades the moment they do not — it wraps,
or it scrolls, and either way the set stops being visible at a glance.

`type="multiple"`, so more than one can be open. Tabs enforced exactly one, which is right when the
panels are alternatives and wrong when they are parts of the same thing: the colours and the padding of
a layout are read together as often as separately.

The grid overlay went with them. Every panel shared one cell so the block stayed as tall as the tallest
and did not jump when the tab changed — a real problem that stacked sections do not have, because a
section that is closed occupies nothing and one that is open is as tall as it is.

`?tab=` still seeds which section starts open, so an old link keeps working and lands on the thing it
named.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 03:28:15 +01:00
pastilhasandClaude Opus 5 a0e05efda9 Layout's controls move to the editor panel too
Colours, Typefaces and Padding sat below the preview and took their height from it — the same trade the
page builder's editors made, and undone for the same reason: this screen exists to be looked at, and
the controls were eating the thing being judged.

Announced through the same slot, so the panel still holds nothing and still knows nothing about what a
layout is.

One thing that does not survive the move: a JSX comment cannot be an argument. The block carried its
own `{/* … */}` above the tabs and it had to become an ordinary comment on the effect, which is where
the reasoning belongs anyway now that the element is announced rather than rendered in place.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 02:55:07 +01:00
pastilhasandClaude Opus 5 9eb17c8d03 an editor opens beside the page, not under it
The spacing and free-text editors rendered below the preview, so they took their height from the thing
being edited: choosing a spacing shrank the page you were choosing it for, and on a laptop two
dropdowns cost a third of the frame.

They are a third panel now, to the right of the preview. The workspace already knows how to size and
move panels; a drawer was a second, worse layout system living inside one of them.

The panel holds nothing. Whichever panel owns a selection BUILDS the editor and announces it through a
`useGlobal` slot — the two are siblings and neither may own the other, and a provider around both would
mean something above them knowing they exist.

The slot carries the ELEMENT, not a description of one. A descriptor plus a switch would move every
editor's loading, saving and cancelling into a panel that would have to learn what a section is — a
second place that understands the page builder, kept in step by hand. SaveTarget makes the same trade
for the same reason.

The hazard is real and worth naming: an element captures the props it was built with, so the announcing
effect must depend on everything the editor DISPLAYS. The save handlers sit in refs so they cannot drag
that list wider — depending on them would re-announce on every keystroke, and omitting them without
refs would announce a stale one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 02:29:08 +01:00
pastilhasandClaude Opus 5 8f673792d1 there is no View, only Edit
The bar offered Edit and View, and View existed to see the page without the builder's outlines and
handles on top of it. It was the DEFAULT, which put a click in front of every arrival at the one panel
whose job is editing — and it put a branch in every painter that did nothing but wait for that click.

So the mode is gone rather than defaulted the other way: no toggle, no `?mode=`, no guard. The clean
page is still one click away and always was — it is the site, on its own URL. What the admin owes is
the editing surface, always.

The left group in the bar stays, empty. The middle one is centred against the BAR rather than against
its neighbours, so removing the left group would let Desktop/Tablet/Mobile drift as the right group
changes width.

The preview's caption stays too. It reads as a duplicate of the device buttons and is not: they say the
width, and it says which SERVER — development, not production — which nothing else on the screen does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 19:23:37 +01:00
pastilhasandClaude Opus 5 10e5a625ff Media replaces Video, and the greyed-out Image
Two rows, one of them marked `soon`, for a distinction that was never the editor's to make. The path
names a real file and what that file IS is a fact about it — the site reads it off the extension the
way a gallery item always has. So the picker asks for a file and stops asking what kind.

`video` is gone from what an editor can create and still legal in the database. Every row was migrated;
one written by something older should still be editable rather than open nothing, which is why the
edit path answers to both.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 15:38:20 +01:00
pastilhasandClaude Opus 5 a4dfb3c81a Contact form and Partners, placed in one click
Both hold nothing, so adding one opens no editor: a dialog with no field on it is a step that exists
only to be got past. The picker itself answers the opposite way for the kinds that DO have a choice
to make, which is the distinction worth keeping.

The canvas draws no Edit button for them either — an Edit that opens nothing is worse than no Edit.
It asks the panel rather than holding a list of kinds, because which kinds are editable is the same
knowledge that decides which editor opens, and that already lives there. Absent means editable, so a
caller that has never thought about it gets what it had.

The Spacing dialog's label became a map. It was two kinds and readable as a ternary, then three and
not.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 07:08:30 +01:00
pastilhasandClaude Opus 5 2ab9e78790 Gallery is a component you can add
The picker listed it as `soon` beside Image. It is not soon any more — the set, its ordering and the
carousel that draws it were already built on the site, so the block is a reference to one and the
admin's whole job is choosing which.

`GalleryPicker` chooses a SET and nothing else. Editing what is IN the set stays under Galleries: a
block that could also rearrange its own pictures would be a second place to do one job, and the two
would disagree. No thumbnails — every stored path names an original a browser cannot display, and the
plugin has no way to address a rendition, so the name and the count identify it instead.

Saved on choose, like a video and for the same reason: picking one thing is one act with one outcome,
so there is nothing to draft and nothing to cancel out of.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 06:56:36 +01:00
pastilhasandClaude Opus 5 18c01de6f2 Spacing, on a section and on a component
A Spacing handle on the section bar and in the component tools, opening an editor below the preview —
the same place the text editor goes, and for the same reason: a dialog covers the thing being spaced.

The device toggle picks the breakpoint. Two axes across three widths is six controls, five of them for a
width you are not looking at; this way you switch to Mobile, see the phone, and change the padding you
can watch changing. The site's GUTTER is the exception and shows all three at once, because it is one
axis and one value and it is a progression judged as a whole.

Inherit and 0 stay separate options, and the select names the value it is inheriting rather than leaving
it to be worked out.

Written IMMEDIATELY rather than drafted. A step is one choice from a short list and the effect is on
screen before the menu closes; a Save for it would be a second thing to remember on a panel whose other
verbs already act.

Spacing sits before Delete in the section bar, ordered by how often each is wanted rather than how they
read — the destructive one furthest from where the hand arrives.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 06:29:38 +01:00
pastilhasandClaude Opus 5 45104c1f98 the text editor renders below the page, and types into it
A modal covered the thing being edited, which for a page builder is the whole subject: you are choosing
how words sit in a column, and a dialog in front of the column answers a different question. It sits
under the preview now, the way the Layout section's controls already do.

AND THE TEXT LANDS IN THE PAGE AS IT IS TYPED. The panel writes each change straight into the
component's node in the frame, so the loop stops being "type, save, look" and becomes looking. That is
the same door everything else here goes through — the frame is same origin — and it is what the modal
made impossible rather than merely awkward.

The node's children are replaced and the injected tools put back. Those live inside the component and
belong to the admin, so an innerHTML that forgot them would take the Edit button away mid-edit.

Not sanitised while typing. The server cleans on write, so what is on screen can be marginally more
permissive than what is stored — and the difference shows the moment it is saved, which is the right
place to find out.

Cancel reloads the frame rather than restoring the markup by hand. The node has been written into on
every keystroke, and putting it back correctly means remembering what was there before the first one; a
reload is the same answer with nothing to get wrong.

The editor is capped and scrolls, so a long piece does not push the page off the top of the panel.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 06:06:45 +01:00
pastilhasandClaude Opus 5 466ad26f11 Video, chosen from the media store
The picker offers it now, and choosing it opens the media picker rather than a dialog. A video is a
PATH, saved as soon as it is chosen: free text needs a draft because writing is something people do
halfway, and picking a file is one act with one outcome.

Edit means whatever editing that KIND means. The canvas draws one button; which door it opens is decided
in the panel, where the kinds are known — so Edit on a video reopens the picker and Edit on text opens
the editor.

Image and Gallery stay named and disabled. Both are things the site already has.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 06:03:07 +01:00
pastilhasandClaude Opus 5 8ab0a4a516 Add component asks what kind, even with one to choose
It opened the text editor straight away, and I had argued for that: Free Text is the only kind that
works, so a picker offering it is a click asking to be got past.

That was wrong in the way a shortcut usually is. It optimised the state of the app today and hid its
shape — somebody adding a component could not see what a component IS, or that there will be others,
and the day the second kind lands the interaction changes underneath them.

It is also the pattern this admin already uses. The nav lists unbuilt sections as `soon` rather than
hiding them, because a list of only what is finished gives no sense of what the thing is for. The same
argument applied here and I had not noticed.

Image, Video and Gallery are named and disabled. Those three are things the site already HAS — media,
video and galleries all exist as concepts here — which is the difference between saying what is coming
and inventing a roadmap.

The list lives in the plugin rather than the API. The API decides what may be STORED; this decides what
may be OFFERED, and it has to be here because every kind needs a label, an icon and an editor that only
exists in this plugin. A kind the server would accept and this has never heard of is not creatable
anyway.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 05:45:21 +01:00
pastilhasandClaude Opus 5 79b879f3f4 the gutter shows as you choose it, and the tab is in the URL
CHANGES APPEAR IMMEDIATELY. Colours and typefaces go into the frame as CSS custom properties because
that is what the page reads them as. A gutter cannot: only a class can carry a breakpoint, and there is
no variable to set instead. So the classes are swapped on the three elements marked `data-gutter`.

That works for one reason worth stating — the classes are in the SITE's stylesheet, which the frame
already has, and the site safelists every step. A class invented in the admin would silently do
nothing, which is the failure this whole arrangement keeps circling.

Only gutter classes are touched. Those three elements carry the gutter and nothing that looks like it —
a section's own padding lands on the <section> and the band padding on <header> — so stripping `px-N`
from them is safe where doing it to an arbitrary element would not be.

Driven by the dependency list rather than a second code path: `gutter` is a dependency of onDocument, so
changing it hands SitePreview a new function, whose effect runs the previous application's cleanup —
putting the original classes back — and then applies the new ones.

THE TAB IS IN THE URL. `?tab=padding`, same rule as the mode and the device: it is addressable. "Look at
the typefaces" is a link somebody sends, and a reload that drops you back on Colours loses the place you
were working in. Absent means Colours.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 05:36:19 +01:00
pastilhasandClaude Opus 5 2b2d60f45c the gutter shows all three widths at once
One control, three values, horizontal only — Mobile, Tablet, Desktop on one row, smallest first because
that is the direction they cascade.

Not tied to the device toggle, and the difference from the per-band control is the reason. That one has
two axes and three bands, where showing every width would be eighteen inputs, so it writes to whichever
width the preview is showing. Here there is one axis and one value, so all three fit — and seeing them
together is the point. A gutter is a progression, 4 then 8 then 12, judged as a whole; reading it off
three separate visits to the same control is how a site ends up tighter on a tablet than on a phone.

Inherit still says what it is inheriting, and Mobile still says "None", because there is nothing below
it to inherit from.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 05:32:01 +01:00
pastilhasandClaude Opus 5 08d1a3089f Layout's Padding tab sets the site's gutter
Not a per-band control. The header, the content column and the footer share one measure and should
share one number at its edges — three sections' worth of controls would be three ways to disagree about
the same decision.

PaddingTab is untouched and stays. It is the per-band control and it is right for the pages, where a
band genuinely does want its own spacing. This panel just is not where that decision belongs.

Same interaction as everything else here: the device toggle picks the breakpoint, Inherit and 0 are
separate options because they are separate instructions, and the select says which value it is
inheriting rather than leaving it to be worked out.

Saved with the colours and the typefaces, because it is a field of the same row — so the per-band write
path this panel had briefly is gone with it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 05:29:01 +01:00
pastilhasandClaude Opus 5 2e8eabbc6b a Padding tab, where the device toggle picks the breakpoint
Choose a band — Header, Page, Footer — and set its horizontal and vertical padding. Which WIDTH you are
setting is decided by the Desktop / Tablet / Mobile buttons already in the bar:

  Mobile   390   base   every width
  Tablet   834   md     768 and up
  Desktop  1440  lg     1024 and up

That mapping is Tailwind's own and it lines up with the three preview widths exactly. Editing all three
at once would be six controls per section, five of them for a width you are not looking at; this way
you switch to Mobile, see the phone, and change the padding you can see changing.

INHERIT AND ZERO ARE DIFFERENT OPTIONS, because they are different instructions. Unset takes the
smaller breakpoint's value — which is what makes "one padding everywhere" a single choice rather than
three — and zero means no padding HERE even though the smaller width has some. The select says which
value is being inherited rather than leaving it to be worked out, and at the smallest width it says
"None", since there is nothing below to inherit from.

An emptied breakpoint is REMOVED rather than stored as `{}`, so "inherit everything" stays an absence
instead of becoming a thing that has to be told apart from one later.

One Save for both endpoints. The alternative was a second Save for spacing, which means somebody
changing a colour and a padding presses two buttons and is told about unsaved work by one of them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 05:24:30 +01:00
pastilhasandClaude Opus 5 495320f555 the tab panels share one grid cell, so nothing jumps
Making Typefaces one row equalised the two tabs by luck, not by construction — the labels and the hints
under the selects still differed, and any tab that grows a row would put the jump straight back.

So every panel now sits in the SAME grid cell. The block is as tall as the tallest of them and stops
moving entirely, and it stays right when a tab is added or one of them grows.

Measuring instead would have meant a ResizeObserver, a re-measure after every edit, and a wrong answer
for one frame after each. `col-start-1 row-start-1` gets the same number out of the layout engine for
nothing.

`invisible`, not `hidden`. An invisible element still takes its space, which IS the mechanism — and it
is out of the tab order, so the controls behind the front panel cannot be reached by keyboard. The Tabs
root is controlled now, because visibility is decided here rather than by Radix unmounting.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 05:20:53 +01:00
pastilhasandClaude Opus 5 7e29953c07 four typefaces across from sm, not lg
The one-row change was committed and never deployed, so it had not been seen at all. And the breakpoint
was lg — 1024 — which four short selects had no reason to wait for. The breakpoint here is the browser
window's rather than the panel's, and the panel is wide in every window the admin runs in.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 05:17:57 +01:00
pastilhasandClaude Opus 5 5c3bb83bab the typefaces fit on one row, so the tabs stop jumping
Two columns made Typefaces twice the height of Colours, so switching tabs moved everything below it and
resized the preview above. A tab strip whose contents jump is one that has to be re-read every time it
is used — which is the opposite of what tabs were for here.

Four across on a wide panel, two on a narrow one, and the hints truncate rather than wrapping into the
second row of height they were really costing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 05:12:51 +01:00
pastilhasandClaude Opus 5 8d5c4797ad the mode is in the URL, and the controls are tabs
?mode=edit

docs/navigation-audit.md draws the line: anything ADDRESSABLE goes in the URL, and whether you are
editing a page or looking at it is exactly that. It survives a reload, it can be sent to somebody, and
a link to a page being edited is a different thing from a link to the page.

`mode`, not `view`. The parameter names the AXIS and the value names the state, so the pair reads
`mode=edit` and `mode=view` rather than the tautological `view=view`. Absent means View — the default
and the common case — so an ordinary link carries nothing and the parameter appears once somebody has
chosen.

The DEVICE deliberately stays in React state. It is a way of looking rather than a thing being looked
at, and nobody has wanted to send one. Worth keeping that distinction deliberate rather than letting
both drift into whichever place was convenient.

COLOURS AND TYPEFACES ARE TABS. They were two stacked cards taking as much height as both together, and
every pixel of it came off the preview — which is the thing being judged. One at a time costs a click
and gives the page back most of the panel.

The 55% cap on the controls is gone with them: one tab is short enough not to need capping, which is
the other thing this bought.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 05:00:21 +01:00