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>
This commit is contained in:
2026-09-01 07:41:09 +01:00
co-authored by Claude Opus 5
parent e45e3d84a8
commit d218d3c7a8
3 changed files with 24 additions and 25 deletions
+22 -18
View File
@@ -2,7 +2,7 @@ import { Monitor, Smartphone, Tablet } from 'lucide-react';
import { useCallback } from 'react';
import { useSearchParams } from 'react-router';
import { Button } from '@/components/ui/button';
import type { DeviceName } from './ViewMode';
import { DEVICES, type DeviceName } from './ViewMode';
/**
* Which width everything is showing, and the control for changing it.
@@ -44,35 +44,39 @@ const OPTIONS: { value: DeviceName; Icon: typeof Monitor }[] = [
];
/**
* The control itself, for an editor section that has per-view settings.
* The control itself: three tabs across the width of whatever holds it.
*
* `size` because the one in the panel's bar is chrome and these are part of a form: the bar's is as
* small as a bar allows, and a section's sits above its own fields and should read as belonging to
* them.
* ── One shape, not two ──
*
* There was a `size` prop. `bar` was a small icon-only cluster for the panel bar across the top of the
* detail panel, and that bar is gone — so what is left is the one shape, and a prop with a single
* value is a decision nobody makes.
*
* ── Labelled, and spread ──
*
* The icons alone were a rebus: a rectangle, a slightly narrower rectangle, and a narrower one still,
* telling three things apart by proportion at 12 pixels. They read as tabs now because they behave
* like tabs — one of three, the whole width, the chosen one filled — and the word is what makes the
* 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.
*/
export const DeviceToggle = ({ size = 'bar' }: { size?: 'bar' | 'section' }) => {
export const DeviceToggle = () => {
const [device, setDevice] = useDevice();
const compact = size === 'bar';
return (
<div
// In a section it spans the width and spreads its three: it scopes everything below it, so it
// reads as a header for the group rather than a control sitting in a corner of one.
className={`flex items-center rounded-md border border-border p-0.5 ${
compact ? 'gap-0.5' : 'w-full justify-around'
}`}
>
<div className="flex w-full items-center rounded-md border border-border p-0.5">
{OPTIONS.map(({ value, Icon }) => (
<Button
key={value}
size="sm"
variant={device === value ? 'secondary' : 'ghost'}
className={compact ? 'h-5 gap-1 px-2 text-xs' : 'h-7 flex-1 text-xs'}
className="h-7 flex-1 gap-1.5 text-xs"
onClick={() => setDevice(value)}
aria-label={value}
title={value}
aria-label={DEVICES[value].label}
>
<Icon className="size-3" />
<Icon className="size-3.5" />
{DEVICES[value].label}
</Button>
))}
</div>
+2 -2
View File
@@ -32,8 +32,8 @@ export const EditorPanel = () => {
return (
<div className="flex size-full flex-col overflow-hidden">
{/* Outside the scroll area, so a long editor cannot carry it off the screen. */}
<div className="flex shrink-0 justify-center border-b border-border px-3 py-2">
<DeviceToggle size="bar" />
<div className="shrink-0 border-b border-border px-3 py-2">
<DeviceToggle />
</div>
{editor ? (
-5
View File
@@ -8,7 +8,6 @@ import { useAutosave } from '../useAutosave';
import { usePreviewable, useViewMode } from '../ViewMode';
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 { SpacingTab } from './SpacingTab';
@@ -348,10 +347,6 @@ export const LayoutPanel = () => {
<AccordionItem value="padding" className="rounded-lg border border-border px-4">
<AccordionTrigger className="text-xs">Spacing</AccordionTrigger>
<AccordionContent>
{/* The width these settings are for. Reads `?device=`, so it moves the preview too. */}
<div className="mb-3">
<DeviceToggle size="section" />
</div>
{/* Three bands. PaddingTab is still the per-SECTION control, for the pages. */}
<SpacingTab
spacing={draft.spacing}