From c30defc1125df78b683d0b4710f4db803e74f1d4 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 12 Jun 2026 15:08:33 +0000 Subject: [PATCH 1/3] chore(dist): update gzip size badges to reflect unminified bundles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Auto-generated by scripts/bundle.js — falls back to unminified CSS when .min.css is absent, so badge sizes reflect the non-minified gzip footprint. https://claude.ai/code/session_01MAgtQ7JY16X2TqZZyGfkuu --- dist/badge-essential.json | 2 +- dist/badge-optimal.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/badge-essential.json b/dist/badge-essential.json index 4067249e..edabdbe2 100644 --- a/dist/badge-essential.json +++ b/dist/badge-essential.json @@ -1,7 +1,7 @@ { "schemaVersion": 1, "label": "essential", - "message": "15.3 kB gzip", + "message": "52.7 kB gzip", "color": "brightgreen", "namedLogo": "css3" } diff --git a/dist/badge-optimal.json b/dist/badge-optimal.json index 7b84b0db..6a072b38 100644 --- a/dist/badge-optimal.json +++ b/dist/badge-optimal.json @@ -1,7 +1,7 @@ { "schemaVersion": 1, "label": "optimal", - "message": "19.3 kB gzip", + "message": "64.1 kB gzip", "color": "brightgreen", "namedLogo": "css3" } From 8be56ac981fd94d7ea861aeb2efcb3509e6a11c0 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 12 Jun 2026 15:24:36 +0000 Subject: [PATCH 2/3] fix(configurator): font-weight tokens no longer get font-family picker + draggable pane widths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Bug fix: isFontFamilyToken was too broad `--sf-font-weight-heading`, `--sf-font-features`, `--sf-font-numeric` and `--sf-font-variation` all share namespace `font` and the `--sf-font-` prefix. The old check classified them as font-family tokens, so the Typography panel's "Heading weight" row showed the System/Manual stack picker and a preview line ("Ag — The quick brown fox jumps") instead of a plain text input. Fix: narrow `isFontFamilyToken` to exclude `weight-*`, `features`, `numeric` and `variation` via a tighter name-pattern regex. The syntax annotation `` remains the primary signal; the name pattern is the secondary. Updated `tests/fonts.test.js` to reflect the corrected contract (namespace alone is not sufficient — font-weight tokens prove it) and added explicit assertions for every excluded token family. ## Feature: draggable pane widths All three desktop panes (sidebar, main, live preview) now have variable widths driven by pointer-captured drag handles: - Two 6 px `resizer` columns are inserted into the CSS grid between sidebar/main (`rs1`) and main/preview (`rs2`). - Widths are stored in `$state` (sidebarWidth, previewWidth) and persisted in localStorage under `slashed-configurator/pane-widths/v1`. - Sidebar min/max: 160–500 px · Preview min/max: 260–680 px. - At ≤ 1100 px the preview becomes a slide-over overlay and the resizers are hidden; the 1 px left border on `.main` is restored via media query. - `shell--dragging` class applies `cursor: col-resize` globally during a drag so the cursor does not flicker when the pointer outruns the handle. - Double-collapsed state (`--no-sidebar --no-preview`) collapses to the plain 2-column icon-rail layout unchanged. https://claude.ai/code/session_01MAgtQ7JY16X2TqZZyGfkuu --- configurator/src/App.svelte | 201 +++++++++++++++++++++++++++---- configurator/src/lib/fonts.js | 15 ++- configurator/tests/fonts.test.js | 17 ++- 3 files changed, 207 insertions(+), 26 deletions(-) diff --git a/configurator/src/App.svelte b/configurator/src/App.svelte index b30b4ba0..1ddc97d8 100644 --- a/configurator/src/App.svelte +++ b/configurator/src/App.svelte @@ -5,18 +5,20 @@ * Layout (desktop ≥ 1100px): * ┌─────────────────────────────────────────────────────────────────┐ * │ Header (brand · search · basic/advanced · theme · preview) │ - * ├──────────┬───────────────────────────────────────┬──────────────┤ - * │ │ │ │ - * │ Sidebar │ Domain panel / WCAG tool │ Preview │ - * │ │ │ │ - * ├──────────┴───────────────────────────────────────┴──────────────┤ + * ├──────────┬──┬────────────────────────────────────┬──┬───────────┤ + * │ │ │ │ │ │ + * │ Sidebar │↔│ Domain panel / WCAG tool │↔│ Preview │ + * │ │ │ │ │ │ + * ├──────────┴──┴────────────────────────────────────┴──┴───────────┤ * │ Output drawer (override CSS, copy / download / import) │ * └─────────────────────────────────────────────────────────────────┘ * + * The ↔ columns are 6 px drag handles — pointer-captured resize of the + * sidebar and preview pane widths. Widths persist in localStorage. + * * Below 1100px the preview pane becomes a slide-over overlay (closed by * default, opened with the header ◨ toggle, dismissed via the scrim). - * Below 760px the sidebar collapses to a - * compact icon-only rail and the search box widens to fill the header. + * Below 760px the sidebar collapses to a compact icon-only rail. * * State lives in the shared `ui` store; this component just wires the * active domain to its panel. @@ -35,6 +37,64 @@ import Cheatsheet from './components/Cheatsheet.svelte'; import Home from './components/Home.svelte'; + // ── Pane-width resizing ─────────────────────────────────────────────────── + const WIDTHS_KEY = 'slashed-configurator/pane-widths/v1'; + const SIDEBAR_MIN = 160; + const SIDEBAR_MAX = 500; + const PREVIEW_MIN = 260; + const PREVIEW_MAX = 680; + + function clampW(v, lo, hi) { return Math.max(lo, Math.min(hi, v)); } + + function loadWidths() { + if (typeof localStorage === 'undefined') return { sidebar: 240, preview: 380 }; + try { + const raw = localStorage.getItem(WIDTHS_KEY); + if (raw) { + const p = JSON.parse(raw); + return { + sidebar: clampW(Number(p.sidebar) || 240, SIDEBAR_MIN, SIDEBAR_MAX), + preview: clampW(Number(p.preview) || 380, PREVIEW_MIN, PREVIEW_MAX), + }; + } + } catch { /* ignore */ } + return { sidebar: 240, preview: 380 }; + } + + const _w = loadWidths(); + let sidebarWidth = $state(_w.sidebar); + let previewWidth = $state(_w.preview); + + /** Which pane is being dragged ('sidebar' | 'preview' | null). */ + let dragging = $state(/** @type {'sidebar'|'preview'|null} */ (null)); + let _dragStartX = 0; + let _dragStartW = 0; + + function startResize(pane, /** @type {PointerEvent} */ e) { + e.currentTarget.setPointerCapture(e.pointerId); + dragging = pane; + _dragStartX = e.clientX; + _dragStartW = pane === 'sidebar' ? sidebarWidth : previewWidth; + e.preventDefault(); + } + + function onResizeMove(pane, /** @type {PointerEvent} */ e) { + if (dragging !== pane) return; + const dx = e.clientX - _dragStartX; + if (pane === 'sidebar') { + sidebarWidth = clampW(_dragStartW + dx, SIDEBAR_MIN, SIDEBAR_MAX); + } else { + // Preview handle sits LEFT of the preview pane: dragging right shrinks preview. + previewWidth = clampW(_dragStartW - dx, PREVIEW_MIN, PREVIEW_MAX); + } + } + + function endResize() { + if (!dragging) return; + dragging = null; + try { localStorage.setItem(WIDTHS_KEY, JSON.stringify({ sidebar: sidebarWidth, preview: previewWidth })); } catch { /* ignore */ } + } + const home = $derived(ui.mode === 'basic' && ui.domain === 'home'); const domain = $derived(DOMAIN_BY_ID.get(ui.domain) ?? DOMAIN_BY_ID.get('colors')); const tool = $derived(domain?.tool ?? ''); @@ -141,11 +201,32 @@ -
+
+ + {#if ui.sidebarOpen} + + {/if} +
{#if home} @@ -162,6 +243,21 @@ {/if}
+ + {#if ui.previewOpen} + + {/if} + {#if ui.previewOpen} @@ -173,29 +269,60 @@