From c0c88a434d8b6c647eb6511ae0391fb1c612282a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Tue, 1 Sep 2026 07:44:15 +0100 Subject: [PATCH] the toggle drops its labels when the panel is narrow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- web/DeviceToggle.tsx | 15 +++++++++++++-- web/sections/LayoutPanel.tsx | 31 ++++++++++++------------------- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/web/DeviceToggle.tsx b/web/DeviceToggle.tsx index 24bc0e7..2c45c35 100644 --- a/web/DeviceToggle.tsx +++ b/web/DeviceToggle.tsx @@ -60,12 +60,20 @@ const OPTIONS: { value: DeviceName; Icon: typeof Monitor }[] = [ * choice legible without hovering for a tooltip. * * The label comes from DEVICES, which is also what the preview names in its own caption. One list. + * + * ── Narrow, it goes back to icons ── + * + * A CONTAINER query, not a screen one: this sits in a workspace panel whose width is a divider the + * user drags, so the window's width says nothing about the room there is. `@container` on the row + * makes the row itself the thing measured. + * + * The word is then the `title`, so the answer is still one hover away rather than gone. */ export const DeviceToggle = () => { const [device, setDevice] = useDevice(); return ( -
+
{OPTIONS.map(({ value, Icon }) => ( ))}
diff --git a/web/sections/LayoutPanel.tsx b/web/sections/LayoutPanel.tsx index 3d5ce12..c01f003 100644 --- a/web/sections/LayoutPanel.tsx +++ b/web/sections/LayoutPanel.tsx @@ -1,6 +1,5 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion'; -import { useSearchParams } from 'react-router'; import { useClient } from 'hooks/useClient'; import { readableError } from '../errors'; import { useFlash } from '../useFlash'; @@ -110,6 +109,9 @@ const themeProperties = (draft: Layout): Record => ({ '--font-display': draft.font_display && `"${draft.font_display}", sans-serif`, }); +/** Every section, open from the start. The values below must match the AccordionItems. */ +const SECTIONS_OPEN = ['colours', 'typefaces', 'padding']; + const COLOURS = [ { key: 'background' as const, label: 'Background', fallback: '#ffffff' }, { key: 'foreground' as const, label: 'Text', fallback: '#000000' }, @@ -124,23 +126,7 @@ export const LayoutPanel = () => { const [stored, setStored] = useState(null); const [draft, setDraft] = useState(null); const [busy, setBusy] = useState(false); - const [params, setParams] = useSearchParams(); const devPort = useDevPort(); - /** Controlled, because the grid stack below decides visibility rather than Radix unmounting. */ - /* - The open tab, 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, the first one, so an ordinary link carries nothing. - */ - const tab = params.get('tab') ?? 'colours'; - const setTab = (next: string) => { - const search = new URLSearchParams(params); - search.set('tab', next); - setParams(search, { replace: true }); - }; const [error, setError] = useState(null); const [saved, flashSaved] = useFlash(); @@ -263,7 +249,14 @@ export const LayoutPanel = () => { } setEditorRef.current( - + /* + Every section open to begin with. + + It opened on one, from `?tab=`, which is what a set of TABS wants — one at a time, and which + one is addressable. These are not tabs: they are the whole of what this screen decides, and a + column that starts collapsed asks somebody to go looking for the setting they came for. + */ + Colors @@ -357,7 +350,7 @@ export const LayoutPanel = () => { ); - }, [draft, tab, device]); + }, [draft, device]); // Cleared on unmount, or the panel goes on offering Layout's controls over somebody else's page. useEffect(() => () => setEditorRef.current(null), []);