From dd10a4df453920d8fa6f08df4708e5e5a37e762a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Tue, 1 Sep 2026 03:28:15 +0100 Subject: [PATCH] Layout's controls are stacked sections, not tabs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- web/sections/LayoutPanel.tsx | 65 ++++++++++-------------------------- 1 file changed, 18 insertions(+), 47 deletions(-) diff --git a/web/sections/LayoutPanel.tsx b/web/sections/LayoutPanel.tsx index 819d2a3..ae57f03 100644 --- a/web/sections/LayoutPanel.tsx +++ b/web/sections/LayoutPanel.tsx @@ -1,5 +1,5 @@ import { useCallback, useEffect, useRef, useState } from 'react'; -import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion'; import { useSearchParams } from 'react-router'; import { useClient } from 'hooks/useClient'; import { readableError } from '../errors'; @@ -275,37 +275,10 @@ export const LayoutPanel = () => { } setEditorRef.current( - - - - Colours - - - Typefaces - - - Padding - - - - {/* - EVERY panel in the same grid cell, so the block is as tall as the TALLEST of them and stops - moving when the tab changes. - - Measuring the panels and setting a height would need a ResizeObserver, a re-measure on every - edit, and a wrong answer for one frame after each. `grid-area: 1/1` gets it from the layout - engine for nothing, and it stays right when a tab is added or one of them grows a row — - which is the case that made the first fix insufficient. - - `invisible` and not `hidden`: an invisible element still takes its space, which IS the - mechanism. It is also removed from the tab order, so the controls behind the front panel - cannot be reached by keyboard. - */} -
-
+ + + Colours + <>
{COLOURS.map(({ key, label, fallback }) => ( @@ -336,12 +309,11 @@ export const LayoutPanel = () => { ))}
-
- -
+ + + + Typefaces + <> {/* Four across, one row. @@ -382,12 +354,11 @@ export const LayoutPanel = () => { ))}
-
- -
+ + + + Padding + {/* The site's gutter, not a per-band padding. @@ -404,9 +375,9 @@ export const LayoutPanel = () => { steps={draft.padding_steps} onChange={(padding_x) => patch({ padding_x })} /> -
- -
+ + + ); }, [draft, tab, device]);