Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 67 additions & 2 deletions docs/playground/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,14 @@ button.primary:hover:not(:disabled) { filter: brightness(1.08); }
color: var(--muted); text-decoration: none;
}
.navbar-link:hover { color: var(--ink); background: var(--field); }
/* The foldable group and the control that unfolds it. Wide screens have room for
the whole row, so the group is dissolved into it and the hamburger is absent;
the narrow rule below is the only place either changes. A name spelled out
belongs to the dropdown alone — in the bar the glyph and its tooltip are the
label. */
.navbar-more { display: contents; }
.navbar-burger { display: none; }
.navbar-word { display: none; }
/* At 25px the pack's default 2px stroke reads as heavy beside 14px chrome. */
.navbar-link .icon { stroke-width: 1; }
.navbar-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
Expand Down Expand Up @@ -543,6 +551,19 @@ body.sim-mode .seg button.active { color: var(--brass); }
the model's own data in view first. */
.drop-wrap { order: 99; grid-column: 1; }
.collapse-btn { display: inline-flex; }
/* Show/Hide joins this row only here, and the logo's width was chosen against
a row without it — three rigid items then measure wider than a phone's
column, which widens the single grid track and hands the whole page a
sideways scroll. A smaller mark buys the room at any usual width, and
wrapping is the guarantee: the row can never demand more than its widest
item, however narrow the screen. */
.recipe-head { flex-wrap: wrap; }
.panel-logo { height: 44px; }
/* Same guarantee for the frame scrub: a slider with a floor, a label holding
8em for its value, and the Segment button together measure wider than the
narrowest phones, and the foot is sized by `syncFootHeight` to hold a second
row when one appears. */
.frame-row { flex-wrap: wrap; }
}

/* recipe editor */
Expand Down Expand Up @@ -631,6 +652,50 @@ body.sim-mode .seg button.active { color: var(--brass); }
label's own tooltip since a disabled input does not reliably show one. */
.mode-switch:has(input:disabled) { opacity: .45; cursor: not-allowed; }

/* The navbar on a phone, where the full row runs off the side of the page.
Only the two controls that change what the page shows and how it looks keep
their place in the bar, each reduced to the single button its two halves were
already agreeing on; the links and the citation fold into the hamburger's
panel. Declared after the switch and the toggle because it overrides them. */
@media (max-width: 700px) {
.navbar { gap: 8px; padding: 8px 12px; }
/* Transient text beside permanent controls: it is the one item that gives up
width, so the switch and the right-hand cluster keep theirs whole instead of
every item shrinking in proportion and each clipping a little. */
.navbar-status {
min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mode-switch, .navbar-right { flex: none; }
.navbar-burger { display: inline-flex; width: 28px; height: 26px; }
.navbar-burger[aria-expanded="true"] { color: var(--ink); background: var(--field); }
/* The same wrapper, now a panel under the bar's right edge. Glass recipe: see
the shared rule with `.menu`. */
.navbar-more {
display: none; position: absolute; z-index: 60;
top: calc(100% + 6px); right: 10px;
flex-direction: column; gap: 2px; padding: 4px; border-radius: 10px;
}
.navbar-more.open { display: flex; }
/* Named rows with a tap-sized footprint, rather than the bar's bare glyphs. */
.navbar-more .navbar-link {
width: auto; height: auto; justify-content: flex-start; gap: 8px;
padding: 8px 10px; color: var(--ink-2);
font: 500 12px var(--font-ui); white-space: nowrap;
}
.navbar-more .icon { width: 18px; height: 18px; color: var(--accent); }
.navbar-word { display: inline; }
/* A rule divided a row; a column is divided already. */
.navbar-more .navbar-rule { display: none; }
.mode-switch { grid-template-columns: 1fr; }
.mode-switch input:not(:checked) ~ span:last-of-type,
.mode-switch input:checked ~ span:first-of-type { display: none; }
.mode-switch::before { width: calc(100% - 6px); }
.mode-switch:has(input:checked)::before,
.mode-switch:hover:not(:has(input:checked))::before,
.mode-switch:hover:has(input:checked)::before { transform: none; }
.seg.seg-icon button:not(.active) { display: none; }
}

.model-line { margin: 12px 0; color: var(--ink-2); }
.model-line select { margin-left: 4px; }

Expand Down Expand Up @@ -1292,15 +1357,15 @@ body.sim-mode .seg button.active { color: var(--brass); }
.modal-card.compact .modal-body { flex: 0 1 auto; }
.modal-card.notice-card { max-width: 46ch; }
/* Every floating surface (this dialog card, the ROI toolbox, the
segmentation menu) wears the tooltip's glass: the panel surface under
segmentation menu, the navbar's narrow-screen panel) wears the tooltip's glass: the panel surface under
half opacity over a heavy blur, an accent-tinted edge, and a shadow that
combines outer depth, an accent halo and an inset top highlight — see
`.tip` for why a tinted fill cannot do this instead. A dialog carries far
more body text than a tooltip, so the fill runs richer than `.tip`'s 62%;
72% is still comfortably inside the contrast `--ink` keeps over
`--panel-2` at any usable alpha, and is what keeps a paragraph legible
over a moving backdrop where 62% was only ever measured for a few words. */
.modal-card, .draw-palette, .menu {
.modal-card, .draw-palette, .menu, .navbar-more {
background: color-mix(in srgb, var(--panel-2) 72%, transparent);
-webkit-backdrop-filter: blur(16px) saturate(1.8);
backdrop-filter: blur(16px) saturate(1.8);
Expand Down
25 changes: 25 additions & 0 deletions docs/playground/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,30 @@ function wireRecipeCollapse() {
narrow?.addEventListener?.("change", (e) => setRecipeCollapsed(e.matches));
}

// The hamburger panel. Only reachable on a narrow screen — where the CSS gives
// the group a box of its own — so nothing here checks the width: on a wide one
// the button is not rendered and the class it toggles has no effect.
function wireNavbarMenu() {
const panel = $("navbar-more");
const btn = $("navbar-burger");
const setOpen = (open) => {
panel.classList.toggle("open", open);
btn.setAttribute("aria-expanded", String(open));
};
btn.onclick = () => setOpen(!panel.classList.contains("open"));
// Picking an item is the end of the errand, and a menu that cannot be
// dismissed is a trap.
panel.addEventListener("click", (e) => {
if (e.target.closest(".navbar-link")) setOpen(false);
});
document.addEventListener("click", (e) => {
if (!e.target.closest("#navbar-more, #navbar-burger")) setOpen(false);
});
document.addEventListener("keydown", (e) => {
if (e.key === "Escape") setOpen(false);
});
}

// The two always-present viewers, and the state they share.
function setUpViewers() {
// Otherwise the canvas backing store renders at 1x and is upscaled by CSS to
Expand Down Expand Up @@ -440,6 +464,7 @@ async function main() {
wireDataDrop();
wireMeasurements();
wireModals();
wireNavbarMenu();

app.wasm = await loadWasm();
if (await populateModels()) await loadModel($("model").value);
Expand Down
36 changes: 26 additions & 10 deletions docs/playground/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,27 @@
</span>
<span id="status" class="navbar-status busy">Starting…</span>
<div class="navbar-right">
<a class="navbar-link" href="https://qmrlab.org/qmrust" target="_blank" rel="noopener"
title="Documentation"><span data-icon="book-text" data-icon-size="25"></span></a>
<a class="navbar-link" href="https://github.com/qmrlab/qmrust" target="_blank" rel="noopener"
title="Source on GitHub"><span data-icon="git-compare-arrows" data-icon-size="25"></span></a>
<a class="navbar-link" href="https://qmrlab.org/mooc" target="_blank" rel="noopener"
title="qMRI course (MOOC)"><span data-icon="graduation-cap" data-icon-size="25"></span></a>
<span class="navbar-rule" aria-hidden="true"></span>
<button class="navbar-link" id="cite-open" type="button"
title="Cite this work"><span data-icon="quote" data-icon-size="25"></span></button>
<span class="navbar-rule" aria-hidden="true"></span>
<!-- Where to go, and how to credit it: the items a phone folds behind the
hamburger. One home each — `display: contents` leaves them inline navbar
items on a wide screen, and the narrow rule turns this same wrapper into
the dropdown, so nothing is restated for mobile. Each carries its name
as text for that panel, hidden while it is a glyph in the bar. -->
<div class="navbar-more" id="navbar-more">
<a class="navbar-link" href="https://qmrlab.org/qmrust" target="_blank" rel="noopener"
title="Documentation"><span data-icon="book-text" data-icon-size="25"></span><span
class="navbar-word">Documentation</span></a>
<a class="navbar-link" href="https://github.com/qmrlab/qmrust" target="_blank" rel="noopener"
title="Source on GitHub"><span data-icon="git-compare-arrows" data-icon-size="25"></span><span
class="navbar-word">Source on GitHub</span></a>
<a class="navbar-link" href="https://qmrlab.org/mooc" target="_blank" rel="noopener"
title="qMRI course (MOOC)"><span data-icon="graduation-cap" data-icon-size="25"></span><span
class="navbar-word">qMRI course (MOOC)</span></a>
<span class="navbar-rule" aria-hidden="true"></span>
<button class="navbar-link" id="cite-open" type="button"
title="Cite this work"><span data-icon="quote" data-icon-size="25"></span><span
class="navbar-word">Cite this work</span></button>
<span class="navbar-rule" aria-hidden="true"></span>
</div>
<!-- The select stays a real select — native keyboard, native mobile picker —
and only its appearance is replaced: it sits transparent over the glyph,
which is the only way an icon-sized control can also be a <select>,
Expand All @@ -53,6 +64,11 @@
<button id="mode-light" type="button" data-icon="sun" data-icon-size="14"></button>
<button id="mode-dark" type="button" data-icon="moon" data-icon-size="14"></button>
</div>
<!-- Opens the panel above. Present only on a narrow screen, where the row
would otherwise run off the side of the page. -->
<button class="navbar-link navbar-burger" id="navbar-burger" type="button"
aria-expanded="false" aria-controls="navbar-more" aria-label="More"
title="More"><span data-icon="menu" data-icon-size="22"></span></button>
</div>
</div>
<div id="fallback" hidden>
Expand Down
2 changes: 1 addition & 1 deletion docs/playground/vendor/MANIFEST.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
"version": "1.27.0",
"source": "https://lucide.dev",
"license": "ISC",
"sha256": "9cb79d1dbb9253436bb54ad29dc26dad7c5564b3fdb6c5dbad3558d4283564b3"
"sha256": "72e5c4d37cbabc089fe905b2cd6ee3498ac9b84f6bcd95766e33858ef85a6042"
},
{
"file": "js-yaml.js",
Expand Down
2 changes: 2 additions & 0 deletions docs/playground/vendor/icons.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ const SHAPES = {
'<circle cx="12" cy="12" r="10" /><path d="M12 16v-4" /><path d="M12 8h.01" />',
"list-collapse":
'<path d="M10 5h11" /><path d="M10 12h11" /><path d="M10 19h11" /><path d="m3 10 3-3-3-3" /><path d="m3 20 3-3-3-3" />',
"menu":
'<path d="M4 5h16" /><path d="M4 12h16" /><path d="M4 19h16" />',
"moon":
'<path d="M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401" />',
"palette":
Expand Down
Loading