@fasl-work/caos-app-shell 0.6.2 → 0.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +15 -0
- package/dist/index.js +4 -2
- package/package.json +1 -1
- package/styles.css +17 -6
package/dist/index.d.ts
CHANGED
|
@@ -68,6 +68,21 @@ interface ShellConfig {
|
|
|
68
68
|
es: string;
|
|
69
69
|
};
|
|
70
70
|
};
|
|
71
|
+
/**
|
|
72
|
+
* Routes whose surface IS the viewport (ADR-0071 rule 1): on these the shell adds
|
|
73
|
+
* `.app-shell.fixed`, which sizes the page to 100dvh and lets flex distribute the
|
|
74
|
+
* height, so the ONE container that owns long content scrolls and the document
|
|
75
|
+
* never does. A doc route is not listed and keeps the document scroll. Matching is
|
|
76
|
+
* exact, or by prefix for a non-root path (`/lab` matches `/lab/case-1`).
|
|
77
|
+
*
|
|
78
|
+
* The stylesheet has carried `.app-shell.fixed` with the note "apps that fill the
|
|
79
|
+
* viewport add it" since the containment fix, and no app could: this component
|
|
80
|
+
* rendered a fixed class string. Measured on Porvenir before this field existed:
|
|
81
|
+
* every tab of the App route scrolled the document by 200 to 770px.
|
|
82
|
+
*/
|
|
83
|
+
fixedRoutes?: string[];
|
|
84
|
+
/** Every route fixed: a single-surface app (a hub, a one-page tool). */
|
|
85
|
+
fixed?: boolean;
|
|
71
86
|
}
|
|
72
87
|
/** The shared CAOS/Faena app shell: sticky header (brand + nav + icon-links + lang/theme) + footer.
|
|
73
88
|
* Wrap your <Routes> (or single landing) in it, inside a Router. */
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createContext, useState, useRef, useEffect, useCallback, useId, useContext } from 'react';
|
|
2
|
-
import { NavLink } from 'react-router';
|
|
2
|
+
import { useLocation, NavLink } from 'react-router';
|
|
3
3
|
import { Sun, Moon, Languages, Boxes, Github, Globe, Briefcase, Info } from 'lucide-react';
|
|
4
4
|
import { create } from 'zustand';
|
|
5
5
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
@@ -254,7 +254,9 @@ function AppShell({ config, children }) {
|
|
|
254
254
|
const portfolio = config.links.portfolio ?? PORTFOLIO;
|
|
255
255
|
const [archOpen, setArchOpen] = useState(false);
|
|
256
256
|
const archLabel = lang === "es" ? "Arquitectura / C\xF3mo funciona" : "Architecture / How it works";
|
|
257
|
-
|
|
257
|
+
const { pathname } = useLocation();
|
|
258
|
+
const fixed = config.fixed === true || (config.fixedRoutes ?? []).some((p) => p === pathname || p !== "/" && pathname.startsWith(p.replace(/\/$/, "") + "/"));
|
|
259
|
+
return /* @__PURE__ */ jsxs("div", { className: fixed ? "app-shell fixed" : "app-shell", children: [
|
|
258
260
|
/* @__PURE__ */ jsx("header", { className: "site-header", children: /* @__PURE__ */ jsxs("div", { className: "header-inner", children: [
|
|
259
261
|
/* @__PURE__ */ jsxs(NavLink, { to: "/", className: "brand", "aria-label": config.product.name, children: [
|
|
260
262
|
/* @__PURE__ */ jsx("span", { className: "brand-mark", children: config.product.mark ?? /* @__PURE__ */ jsx(Boxes, { size: 18, "aria-hidden": "true" }) }),
|
package/package.json
CHANGED
package/styles.css
CHANGED
|
@@ -181,7 +181,12 @@ code, kbd { font-family: var(--font-mono); }
|
|
|
181
181
|
.subtab { padding: 0.32rem 0.8rem; border: 1px solid var(--color-border); border-radius: 999px; background: var(--color-surface); color: var(--color-fg-subtle); font-size: 0.84rem; font-weight: 500; transition: color 0.15s, background-color 0.15s, border-color 0.15s; }
|
|
182
182
|
.subtab:hover { color: var(--color-fg); border-color: var(--color-fg-faint); }
|
|
183
183
|
.subtab.active { color: var(--color-accent); background: var(--color-accent-soft); border-color: color-mix(in oklab, var(--color-accent) 40%, transparent); font-weight: 600; }
|
|
184
|
-
|
|
184
|
+
/* `minmax(0, 1fr)`, NOT `1fr`. A bare `1fr` track is `minmax(auto, 1fr)`, and `auto` is the
|
|
185
|
+
min-content width of the panel, so a wide table in a sub-tab panel sized the whole grid to
|
|
186
|
+
the table and pushed the page past the viewport instead of scrolling inside its wrapper
|
|
187
|
+
(ADR-0071 rule 3). Measured on Porvenir's Datasets tab at 1280x800: the panel column
|
|
188
|
+
rendered 1380px wide on a 1280px viewport. */
|
|
189
|
+
.subtabs-vertical { display: grid; grid-template-columns: minmax(180px, 240px) minmax(0, 1fr); gap: 1.5rem; align-items: start; }
|
|
185
190
|
.subtabs-vertical .subtablist { flex-direction: column; margin-bottom: 0; }
|
|
186
191
|
.subtabs-vertical .subtab { text-align: left; border-radius: 8px; }
|
|
187
192
|
@media (max-width: 760px) { .subtabs-vertical { grid-template-columns: 1fr; } .subtabs-vertical .subtablist { flex-direction: row; } }
|
|
@@ -293,7 +298,7 @@ html, body { overflow-x: hidden; }
|
|
|
293
298
|
width of the display, which is the opposite of what ADR-0017 s1.1 specifies. The wide workbench
|
|
294
299
|
opt-in is `.page-body.wide`; a prose page must keep the measure. */
|
|
295
300
|
.page-body { min-width: 0; }
|
|
296
|
-
.tabs, .tabpanel { min-width: 0; max-width: 100%; }
|
|
301
|
+
.tabs, .tabpanel, .subtabs, .subtabpanels, .subtabpanel { min-width: 0; max-width: 100%; }
|
|
297
302
|
.tablist { min-width: 0; max-width: 100%; width: 100%; }
|
|
298
303
|
|
|
299
304
|
/* NO MAGIC CHROME NUMBER. An app surface must not compute its height from a hardcoded guess at the
|
|
@@ -308,6 +313,12 @@ html, body { overflow-x: hidden; }
|
|
|
308
313
|
/* A viewport-filling app must not carry the prose footer margin: it becomes dead space between the
|
|
309
314
|
content and the chrome. Measured on ChargeCascade as exactly 48px (3rem). */
|
|
310
315
|
.app-shell.fixed .site-footer { margin-top: 0; }
|
|
316
|
+
/* On a contained surface the footer competes with the instrument for a fixed height. With a
|
|
317
|
+
product's provenance and disclaimer it wrapped into 178px at 1600x900 (five lines), and the
|
|
318
|
+
instrument fell from 56 to 42 percent of the viewport, under the ADR-0071 rule 8 floor. The
|
|
319
|
+
text stays (ADR-0016 s2: provenance and the honest disclaimer are the footer's job); it is set
|
|
320
|
+
compact, so it takes two to three lines, not five. */
|
|
321
|
+
.app-shell.fixed .site-footer .footer-inner { padding: 0.45rem 1.5rem; gap: 0.4rem; font-size: 0.74rem; line-height: 1.3; }
|
|
311
322
|
|
|
312
323
|
/* ADR-0058 diagram i18n. A hand-authored architecture SVG carries BOTH languages inside ONE file,
|
|
313
324
|
and the modal wrapper picks one. Two files would be two things to keep in step, and the one not
|
|
@@ -316,7 +327,7 @@ html, body { overflow-x: hidden; }
|
|
|
316
327
|
[data-arch-lang] .l-es { display: none; }
|
|
317
328
|
[data-arch-lang="es"] .l-en { display: none; }
|
|
318
329
|
[data-arch-lang="es"] .l-es { display: inline; }
|
|
319
|
-
|
|
320
|
-
/* The compact one-of-N case picker (ADR-0071 rule 7). Ten cases in four categories
|
|
321
|
-
cost a 308px chip block, 21% of a 2560x1440 viewport, and one row as a dropdown. */
|
|
322
|
-
.cs-select { width: 100%; max-width: 460px; }
|
|
330
|
+
|
|
331
|
+
/* The compact one-of-N case picker (ADR-0071 rule 7). Ten cases in four categories
|
|
332
|
+
cost a 308px chip block, 21% of a 2560x1440 viewport, and one row as a dropdown. */
|
|
333
|
+
.cs-select { width: 100%; max-width: 460px; }
|