feat(design): design lab + DESIGN.md skeleton (E28 S2a)
- docs/DESIGN.md: the written source of truth for the UI. Decided chapters (principles, device adaptation, action placement, text stays selectable, interaction rules, copy, anti-patterns, decision log); controls and surfaces are marked Pending until the owner picks from the lab. - /design-lab (dev only): renders each open decision's variants with the app's real CSS, themes and font scale. Batch A = six control decisions. - devOnlyGuard: a production build fails if anything under src/design-lab reaches the bundle (seen red with an unguarded import). - Size contract: tablet portrait + landscape and up, replacing the 1024x768 floor with its never-built "too small" message.
This commit is contained in:
+12
-2
@@ -38,6 +38,10 @@ const App = lazyWithPreload(() => import("./App"));
|
||||
const PrivacyPolicy = lazy(() => import("./components/legal/PrivacyPolicy"));
|
||||
const Terms = lazy(() => import("./components/legal/Terms"));
|
||||
const WatchPage = lazy(() => import("./components/WatchPage"));
|
||||
// The design lab (E28): the living style guide, rendered with the app's real CSS so a design decision
|
||||
// is taken on what will ship. DEV only — Vite folds the condition to `false` in a production build,
|
||||
// the import becomes dead code and the chunk is never emitted (devOnlyGuard fails the build if it is).
|
||||
const DesignLab = import.meta.env.DEV ? lazy(() => import("./design-lab/DesignLab")) : null;
|
||||
|
||||
const queryClient = createQueryClient();
|
||||
|
||||
@@ -49,13 +53,19 @@ de.theme ||= "dark";
|
||||
de.scheme ||= "midnight";
|
||||
|
||||
const path = window.location.pathname;
|
||||
const isPublicPage = path === "/privacy" || path === "/terms" || path.startsWith("/watch/");
|
||||
const isLab = DesignLab !== null && path === "/design-lab";
|
||||
const isPublicPage =
|
||||
path === "/privacy" || path === "/terms" || path.startsWith("/watch/") || isLab;
|
||||
// Fetch the app chunk NOW, alongside BootGate's identity probe, instead of only once the gate opens:
|
||||
// the two waits then overlap rather than queue, and the gate usually opens straight onto the app. A
|
||||
// failed fetch is left to the render's own load, which retries it and reports to the ErrorBoundary.
|
||||
if (!isPublicPage) App.preload().catch(() => {});
|
||||
const root =
|
||||
path === "/privacy" ? (
|
||||
isLab && DesignLab ? (
|
||||
<FloatingTree>
|
||||
<DesignLab />
|
||||
</FloatingTree>
|
||||
) : path === "/privacy" ? (
|
||||
<PrivacyPolicy />
|
||||
) : path === "/terms" ? (
|
||||
<Terms />
|
||||
|
||||
Reference in New Issue
Block a user