From 2f63cdc6f61fe0c11c90aff0ccde80b57dab9ccf Mon Sep 17 00:00:00 2001 From: "Victor \"David\" Medina" Date: Sun, 28 Jun 2026 10:03:07 -0400 Subject: [PATCH] feat(M1/simple-mode): ship the real calm-skin rules (16px base, caption floor, 48px touch targets) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mobile-refresh lead sprint. SimpleModeLayer documented base-text-16px, 48px-touch-targets, and max-actions as 'PLANNED (not implemented)' — the three rules that most reduce ADHD/mobile friction. This lands the CSS: - .simple-mode 16px reading base + sub-12px caption floor (text-[10px]/[11px] -> 12px) - 48px min touch targets (WCAG 2.5.5) on button / a[role=button] / [role=tab] / select, with [data-simple-compact] as the explicit escape hatch for dense chip/icon rows - body[data-simple-mode=on] marker so the gate is MEASURED (Playwright-assertable), not prose-claimed - comment moved PLANNED -> IMPLEMENTED honestly; max-3-actions noted as a component convention (M3/M4 nav/focus refresh), not falsely claimed as CSS Builds-ahead — deploys with the Deck. tsc 0; brand-scan 0 errors. Co-Authored-By: Claude Opus 4.6 (1M context) --- app/globals.css | 28 +++++++++++++++++++++++++++ components/layout/SimpleModeLayer.tsx | 11 +++++++++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/app/globals.css b/app/globals.css index c7f515e8..a9951e25 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1155,6 +1155,34 @@ html[data-density="compact"] main .py-2\.5 { padding-top: 0.375rem; padding-bott display: none; } +/* ------------------------------------------------------------ + S/M1 (2026-06-28): the calm-skin rules previously documented in + SimpleModeLayer as "PLANNED, not implemented" now land for Simple + Mode — a 16px reading base, a sub-12px caption floor, and 48px + minimum touch targets (WCAG 2.5.5). Opt any dense control out with + [data-simple-compact]. Measured (not prose) via the + body[data-simple-mode="on"] marker SimpleModeLayer sets. + ------------------------------------------------------------ */ +.simple-mode { + font-size: 16px; /* 16px reading base on the calm owner surface */ +} + +/* Caption legibility floor — nothing microscopic on a phone. Raises + only the genuinely-tiny utilities; leaves text-sm/xs layouts intact. */ +.simple-mode .text-\[10px\], +.simple-mode .text-\[11px\] { + font-size: 12px; +} + +/* 48px minimum touch targets on the primary interactive controls. + [data-simple-compact] is the explicit escape for chip/icon rows. */ +.simple-mode :where(button, a[role="button"], [role="tab"], select):not([data-simple-compact]) { + min-height: 48px; +} +.simple-mode :where(button, a[role="button"]):not([data-simple-compact]) { + min-width: 48px; +} + /* ============================================================ DEVELOPER-CHROME GATE (DevChromeLayer + lib/dev-chrome.ts) ------------------------------------------------------------ diff --git a/components/layout/SimpleModeLayer.tsx b/components/layout/SimpleModeLayer.tsx index 59591d95..ed0b5e8d 100644 --- a/components/layout/SimpleModeLayer.tsx +++ b/components/layout/SimpleModeLayer.tsx @@ -17,9 +17,14 @@ import { isSimpleMode, simpleModeBodyClass } from "@/lib/simple-mode"; * shell layout) and the api/admin settings tabs (via filterSettingsTabs) * - `.simple-mode .technical-term` spans are display:none -- inline * jargon gating + * - S/M1 (2026-06-28): a 16px reading base, a sub-12px caption floor, + * and 48px minimum touch targets (WCAG 2.5.5); opt a dense control + * out with [data-simple-compact]. The carries + * data-simple-mode="on" as the measurable (Playwright-assertable) marker. * - * PLANNED (not yet implemented -- do NOT claim these until the CSS lands): - * base text >= 16px, max-3-primary-actions, 48px touch targets. + * COMPONENT CONVENTION (not CSS): cap a screen to <=2 emphasized primary + * actions -- the "one decision per screen" north star, landed structurally + * in the nav/focus refresh (M3/M4), not here. * * Data source: tenants.simple_mode_enabled, the same flag SettingsClient * reads (via lib/simple-mode isSimpleMode) and SimpleModeToggle writes. @@ -90,8 +95,10 @@ export default function SimpleModeLayer({ enabled, children }: SimpleModeLayerPr const cls = simpleModeBodyClass(true); // "simple-mode" if (active) { document.body.classList.add(cls); + document.body.setAttribute("data-simple-mode", "on"); } else { document.body.classList.remove(cls); + document.body.removeAttribute("data-simple-mode"); } return () => { // On unmount, leave the class in place if active so SSR/CSR stay