From 079fd296f0a2978612c66e80f134c56665cdd6a7 Mon Sep 17 00:00:00 2001 From: fOuttaMyPaint Date: Thu, 23 Jul 2026 20:55:59 -0400 Subject: [PATCH 1/2] feat: add search, density toggle, and sticky controls to gallery index At 40 examples the index was a 13003px two-column scroll with only a tag row to narrow it. The generator now emits a sticky controls bar (search, tag chips, compact/detailed density toggle, match count) plus hash-based shareable state, back-to-top, and '/'/Escape keyboard affordances. Compact density (the default with JS) collapses each card to hero + name + one-line teaser for a 67% page-height reduction; the description and WITNESSES text stay in the DOM via visual clipping, so search, screen readers, and no-JS rendering are unaffected. Everything is gated on JS-added classes: with JavaScript disabled all 40 cards render expanded and the controls are simply inert. No external dependencies; vanilla CSS/JS emitted by scripts/build_gallery.py. Signed-off-by: fOuttaMyPaint --- scripts/build_gallery.py | 309 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 287 insertions(+), 22 deletions(-) diff --git a/scripts/build_gallery.py b/scripts/build_gallery.py index fd9c4bc..4d413a3 100644 --- a/scripts/build_gallery.py +++ b/scripts/build_gallery.py @@ -6,6 +6,12 @@ zoom), a run-it-yourself command, the example's README rendered inline, and the full Python source syntax-highlighted at build time. +The index carries a sticky controls bar (search, tag chips, compact/detailed +density toggle, back-to-top) driven by inline vanilla JS — no external +dependencies. Everything is no-JS safe: compact density and chip collapsing +are applied only via JS-added classes, so with JavaScript disabled every +card renders expanded and readable. + This is a LOCAL, this-repo gallery that rides alongside the generated docs/index.html (which scripts/site/build_site.py owns and overwrites). It writes ONLY under docs/gallery/ so it never collides with the landing build's @@ -164,14 +170,82 @@ def assert_alts_survive_dotted_paths(examples: list) -> None: font-size: clamp(2.2rem, 5vw, 3.4rem); letter-spacing: 0.005em; line-height: 0.98; } header.hero p { color: var(--text-dim); max-width: 62ch; margin-top: 0.7rem; font-size: 1rem; } - /* ---- index: filter chips ---- */ - .chips { max-width: var(--maxw); margin: 0 auto; padding: 0 1.25rem 0.25rem; - display: flex; flex-wrap: wrap; gap: 0.5rem; } + /* ---- index: sticky controls (search, density toggle, tag chips) ---- */ + .controls { position: sticky; top: 46px; z-index: 4; + background: color-mix(in srgb, var(--bg) 94%, transparent); + backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); + border-bottom: 1px solid var(--border); } + .controls-inner { max-width: var(--maxw); margin: 0 auto; padding: 0.55rem 1.25rem 0.6rem; + display: flex; flex-direction: column; gap: 0.5rem; } + .controls-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; } + .searchwrap { position: relative; flex: 1 1 240px; min-width: 150px; } + .searchwrap input { width: 100%; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text); border-radius: var(--radius); padding: 0.34rem 1.9rem 0.34rem 0.7rem; + font-family: var(--font-sans); font-size: 0.85rem; line-height: 1.4; } + .searchwrap input:focus { border-color: var(--select); outline: none; } + .searchwrap input::placeholder { color: var(--text-dim); } + .searchwrap input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; } + .q-clear { position: absolute; right: 0.2rem; top: 50%; transform: translateY(-50%); + background: none; border: none; color: var(--text-dim); font-size: 1.05rem; line-height: 1; + cursor: pointer; padding: 0.25rem 0.45rem; border-radius: 3px; } + .q-clear:hover { color: var(--select); } + .count { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; color: var(--text-dim); white-space: nowrap; } + .density { display: flex; border: 1px solid var(--border); border-radius: var(--radius); + overflow: hidden; } + .density-btn { background: var(--surface-2); border: none; color: var(--text-dim); cursor: pointer; + font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; padding: 0.36rem 0.7rem; transition: color 0.15s, background 0.15s; } + .density-btn + .density-btn { border-left: 1px solid var(--border); } + .density-btn:hover { color: var(--select); } + .density-btn.active { background: var(--select); color: #1a1b1e; } + .tags-toggle { display: none; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text-dim); border-radius: 3px; padding: 0.3rem 0.7rem; cursor: pointer; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; text-transform: uppercase; } + .tags-toggle:hover, .tags-toggle.has-active { color: var(--select); border-color: var(--select); } + + /* Tag chips wrap by default (no-JS safe). With JS the row becomes a scroll + strip on wide viewports and collapses behind the Tags toggle on narrow + ones, so the sticky bar never eats the mobile viewport. */ + .chips { display: flex; flex-wrap: wrap; gap: 0.4rem; } + html.js .chips { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 0.25rem; + scrollbar-width: thin; scrollbar-color: var(--border) transparent; } + html.js .chips::-webkit-scrollbar { height: 5px; } + html.js .chips::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } .chip { background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); border-radius: 3px; padding: 0.22rem 0.7rem; font-size: 0.72rem; font-weight: 400; - font-family: var(--font-mono); cursor: pointer; transition: color 0.15s, border-color 0.15s; } + font-family: var(--font-mono); cursor: pointer; white-space: nowrap; flex: 0 0 auto; + transition: color 0.15s, border-color 0.15s; } .chip:hover { color: var(--select); border-color: var(--select); } .chip.active { color: #1a1b1e; background: var(--select); border-color: var(--select); } + @media (max-width: 719px) { + html.js .tags-toggle { display: inline-block; } + html.js .chips { display: none; } + html.js .chips.open { display: flex; flex-wrap: wrap; overflow-y: auto; max-height: 40vh; } + } + + /* Compact density: hero + name + one-line teaser. The description and + WITNESSES text stay in the DOM (searchable, screen-reader reachable, + present with JS disabled when the class is never applied) but are + visually collapsed. Applied only by JS via the density-compact class. */ + html.density-compact .grid { gap: 1rem; } + html.density-compact .card-body { padding: 0.65rem 0.9rem 0.7rem; } + html.density-compact .card-body h2 { font-size: 0.88rem; margin-bottom: 0.15rem; } + html.density-compact .teaches { font-size: 0.8rem; color: var(--text-dim); margin-bottom: 0; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + html.density-compact .witnesses { position: absolute; width: 1px; height: 1px; margin: -1px; + padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; } + html.density-compact .card-link { display: none; } + .noresults { color: var(--text-dim); text-align: center; padding: 3rem 1rem; font-size: 0.95rem; } + .noresults .chip { margin-left: 0.6rem; } + .to-top { position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 6; cursor: pointer; + background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); + border-radius: var(--radius); padding: 0.45rem 0.75rem; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; + text-transform: uppercase; opacity: 0; visibility: hidden; + transition: opacity 0.2s, visibility 0.2s, color 0.15s, border-color 0.15s; } + .to-top.show { opacity: 1; visibility: visible; } + .to-top:hover { color: var(--select); border-color: var(--select); } main { max-width: var(--maxw); margin: 0 auto; padding: 1rem 1.25rem 2rem; } .grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; align-items: stretch; } @@ -249,12 +323,20 @@ def assert_alts_survive_dotted_paths(examples: list) -> None: footer .statusbar code { font-family: inherit; text-transform: none; } @media (min-width: 720px) { .grid { grid-template-columns: 1fr 1fr; gap: 1.75rem; } } + @media (min-width: 560px) { html.density-compact .grid { grid-template-columns: 1fr 1fr; gap: 1rem; } } + @media (min-width: 900px) { html.density-compact .grid { grid-template-columns: repeat(3, 1fr); } } + @media (min-width: 1560px) { + html.density-compact .controls-inner, + html.density-compact main { max-width: 1400px; } + html.density-compact .grid { grid-template-columns: repeat(4, 1fr); } + } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } .card { transition: none; } .card:hover { transform: none; } + .to-top { transition: none; } } - + __HEADJS__ @@ -279,21 +361,173 @@ def assert_alts_survive_dotted_paths(examples: list) -> None: """ +# Index-only head script: runs before first paint so the persisted/default +# density never flashes as a full-height detailed page first. Adds the `js` +# class that gates every JS-only affordance (collapsible chips, scroll strip); +# with JS disabled no class is ever applied and every card renders expanded. +INDEX_HEADJS = """ + """ + INDEX_JS = """ (function () { - var chips = document.querySelectorAll('.chip'); - var cards = document.querySelectorAll('.card'); + var de = document.documentElement; + var grid = document.getElementById('grid'); + var cards = Array.prototype.slice.call(grid.querySelectorAll('.card')); + var chipsEl = document.getElementById('chips'); + var chips = Array.prototype.slice.call(chipsEl.querySelectorAll('.chip')); + var q = document.getElementById('q'); + var qClear = document.getElementById('qClear'); + var count = document.getElementById('count'); + var noResults = document.getElementById('noResults'); + var resetFilters = document.getElementById('resetFilters'); + var densityBtns = Array.prototype.slice.call(document.querySelectorAll('.density-btn')); + var tagsToggle = document.getElementById('tagsToggle'); + var toTop = document.getElementById('toTop'); + var total = cards.length; + var LS_KEY = 'bdt-gallery-density'; + var reduced = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; + + // Searchable text is the card's own DOM text: name, teaches, and the + // WITNESSES line (visually collapsed in compact mode, still in the DOM). + var haystacks = cards.map(function (c) { return c.textContent.toLowerCase(); }); + var state = { q: '', tag: '', density: 'compact' }; + + function parseHash() { + var out = {}; + location.hash.replace(/^#/, '').split('&').forEach(function (kv) { + var i = kv.indexOf('='); + if (i < 0) return; + var k = kv.slice(0, i), v = kv.slice(i + 1); + try { v = decodeURIComponent(v); } catch (e) {} + if (k === 'q' || k === 'tag' || k === 'd') out[k] = v; + }); + return out; + } + + function writeHash() { + var parts = []; + if (state.q) parts.push('q=' + encodeURIComponent(state.q)); + if (state.tag) parts.push('tag=' + encodeURIComponent(state.tag)); + parts.push('d=' + state.density); + history.replaceState(null, '', location.pathname + location.search + '#' + parts.join('&')); + } + + function syncChips() { + chips.forEach(function (c) { + c.classList.toggle('active', (c.getAttribute('data-tag') || '') === state.tag); + }); + tagsToggle.classList.toggle('has-active', !!state.tag); + } + + function applyDensity() { + de.classList.toggle('density-compact', state.density === 'compact'); + densityBtns.forEach(function (b) { + var on = b.getAttribute('data-density') === state.density; + b.classList.toggle('active', on); + b.setAttribute('aria-pressed', on ? 'true' : 'false'); + }); + try { localStorage.setItem(LS_KEY, state.density); } catch (e) {} + } + + function applyFilters() { + var query = state.q.trim().toLowerCase(); + var shown = 0; + cards.forEach(function (card, i) { + var tagOK = !state.tag || (card.getAttribute('data-tags') || '').split(' ').indexOf(state.tag) !== -1; + var qOK = !query || haystacks[i].indexOf(query) !== -1; + var show = tagOK && qOK; + card.classList.toggle('hidden', !show); + if (show) shown++; + }); + count.textContent = (query || state.tag) ? (shown + ' of ' + total) : (total + ' examples'); + noResults.hidden = shown !== 0; + qClear.hidden = !state.q; + } + + q.addEventListener('input', function () { + state.q = q.value; + applyFilters(); + writeHash(); + }); + qClear.addEventListener('click', function () { + q.value = ''; state.q = ''; + applyFilters(); writeHash(); q.focus(); + }); chips.forEach(function (chip) { chip.addEventListener('click', function () { - chips.forEach(function (c) { c.classList.remove('active'); }); - chip.classList.add('active'); - var tag = chip.getAttribute('data-tag'); - cards.forEach(function (card) { - var tags = (card.getAttribute('data-tags') || '').split(' '); - card.classList.toggle('hidden', !!tag && tags.indexOf(tag) === -1); - }); + state.tag = chip.getAttribute('data-tag') || ''; + syncChips(); applyFilters(); writeHash(); + }); + }); + densityBtns.forEach(function (b) { + b.addEventListener('click', function () { + state.density = b.getAttribute('data-density'); + applyDensity(); writeHash(); }); }); + resetFilters.addEventListener('click', function () { + state.q = ''; state.tag = ''; q.value = ''; + syncChips(); applyFilters(); writeHash(); q.focus(); + }); + tagsToggle.addEventListener('click', function () { + var open = chipsEl.classList.toggle('open'); + tagsToggle.setAttribute('aria-expanded', open ? 'true' : 'false'); + }); + + document.addEventListener('keydown', function (e) { + if (e.ctrlKey || e.altKey || e.metaKey) return; + var ae = document.activeElement; + var typing = ae && /^(INPUT|TEXTAREA|SELECT)$/.test(ae.tagName); + if (e.key === '/' && !typing) { + e.preventDefault(); + q.focus(); + q.select(); + } else if (e.key === 'Escape' && ae === q) { + q.value = ''; state.q = ''; + applyFilters(); writeHash(); q.blur(); + } + }); + + function onScroll() { toTop.classList.toggle('show', window.scrollY > 600); } + window.addEventListener('scroll', onScroll, { passive: true }); + onScroll(); + toTop.addEventListener('click', function () { + window.scrollTo({ top: 0, behavior: reduced ? 'auto' : 'smooth' }); + }); + + // Restore state: URL hash wins; density falls back to localStorage. + var h = parseHash(); + state.q = h.q || ''; + state.tag = h.tag || ''; + if (h.d === 'compact' || h.d === 'detailed') state.density = h.d; + else { + try { + var s = localStorage.getItem(LS_KEY); + if (s === 'compact' || s === 'detailed') state.density = s; + } catch (e) {} + } + // A tag in the hash that no chip knows would silently show zero cards. + var known = chips.some(function (c) { return (c.getAttribute('data-tag') || '') === state.tag; }); + if (!known) state.tag = ''; + q.value = state.q; + syncChips(); + applyDensity(); + applyFilters(); })(); """ @@ -494,7 +728,7 @@ def resolve(url: str) -> str: def shell(*, title: str, desc: str, canonical: str, og_image: str, site_root: str, back_href: str, back_label: str, repo_url: str, - content: str, page_js: str) -> str: + content: str, page_js: str, head_js: str = "") -> str: return (SHELL .replace("__TITLE__", html.escape(title)) .replace("__DESC__", html.escape(desc, quote=True)) @@ -505,7 +739,8 @@ def shell(*, title: str, desc: str, canonical: str, og_image: str, .replace("__BACKLABEL__", html.escape(back_label)) .replace("__REPO__", html.escape(repo_url, quote=True)) .replace("__CONTENT__", content) - .replace("__PAGEJS__", page_js)) + .replace("__PAGEJS__", page_js) + .replace("__HEADJS__", head_js)) def build_detail(ex: dict, *, base: str, repo_root_url: str, site: str) -> str: @@ -574,17 +809,42 @@ def build_index(data: dict, *, base: str, repo_root_url: str, site: str) -> str: examples = data["examples"] title = data.get("title", "Examples Gallery") desc = data.get("description", "") + total = len(examples) all_tags = sorted({t for ex in examples for t in ex.get("tags", [])}) - chip_html = "" + chips_html = "" if all_tags: chips = [''] chips += [ f'' for t in all_tags ] - chip_html = (' \n") + chips_html = (' \n") + + # Sticky controls bar. Every control is inert but harmless with JS + # disabled: the search box and toggles do nothing, the count already reads + # correctly, and all cards render expanded (the compact class is JS-only). + controls = ( + '
\n' + '
\n' + '
\n' + '
\n' + ' \n' + ' \n' + '
\n' + f' {total} examples\n' + '
\n' + ' \n' + ' \n' + '
\n' + ' \n' + '
\n' + + chips_html + + '
\n' + '
\n' + ) cards = [] for ex in examples: @@ -605,10 +865,14 @@ def build_index(data: dict, *, base: str, repo_root_url: str, site: str) -> str: f"

{html.escape(title)}

\n" f"

{html.escape(desc)}

\n" " \n" - + chip_html - + '
\n
\n' + + controls + + '
\n
\n' + "\n".join(cards) - + "\n
\n
" + + "\n
\n" + + ' \n' + + "
\n" + + ' ' ) og_image = f"{site}/gallery/assets/{page_relative(examples[0]['hero']).split('/')[-1]}" if site else "" @@ -623,6 +887,7 @@ def build_index(data: dict, *, base: str, repo_root_url: str, site: str) -> str: repo_url=repo_root_url, content=content, page_js=INDEX_JS, + head_js=INDEX_HEADJS, ) From 9dd6893b53d8f5063ccd4928246321c3fb2b9f5f Mon Sep 17 00:00:00 2001 From: fOuttaMyPaint Date: Thu, 23 Jul 2026 20:56:17 -0400 Subject: [PATCH 2/2] chore: regenerate gallery pages from updated generator Output of python scripts/build_gallery.py. Index gains the controls bar markup; detail pages differ only in the shared stylesheet block (index-only rules that match nothing on those pages) - markup and behavior unchanged. Signed-off-by: fOuttaMyPaint --- docs/gallery/armature-bend/index.html | 84 ++++- .../gallery/attribute-domain-shear/index.html | 84 ++++- docs/gallery/bmesh-gear/index.html | 84 ++++- docs/gallery/car-mirror-symmetry/index.html | 84 ++++- docs/gallery/collision-hull-proxy/index.html | 84 ++++- docs/gallery/color-attribute-wheel/index.html | 84 ++++- docs/gallery/compositor-glare/index.html | 84 ++++- docs/gallery/curve-bevel-arc/index.html | 84 ++++- docs/gallery/custom-normals-shade/index.html | 84 ++++- docs/gallery/damped-track-aim/index.html | 84 ++++- docs/gallery/degenerate-bevel-weld/index.html | 84 ++++- docs/gallery/depsgraph-export/index.html | 84 ++++- docs/gallery/driver-wave/index.html | 84 ++++- docs/gallery/gltf-export-roundtrip/index.html | 84 ++++- docs/gallery/gltf-skin-roundtrip/index.html | 84 ++++- docs/gallery/gn-instance-grid/index.html | 84 ++++- docs/gallery/gn-sdf-remesh/index.html | 84 ++++- docs/gallery/gp-lineart-contour/index.html | 84 ++++- docs/gallery/grease-pencil-rosette/index.html | 84 ++++- docs/gallery/image-pixels-testcard/index.html | 84 ++++- docs/gallery/index.html | 346 +++++++++++++++--- docs/gallery/light-link-studio/index.html | 84 ++++- docs/gallery/lod-decimate-chain/index.html | 84 ++++- docs/gallery/mesh-hygiene-audit/index.html | 84 ++++- docs/gallery/parent-inverse-orrery/index.html | 84 ++++- docs/gallery/png-exr-alpha/index.html | 84 ++++- docs/gallery/prop-origin-transform/index.html | 84 ++++- docs/gallery/shader-node-group/index.html | 84 ++++- docs/gallery/shape-key-blend/index.html | 84 ++++- .../sky-texture-sun-elevation/index.html | 84 ++++- docs/gallery/soccer-ball-goldberg/index.html | 84 ++++- docs/gallery/swatch-grid/index.html | 84 ++++- docs/gallery/temp-override-join/index.html | 84 ++++- docs/gallery/text-version-stamp/index.html | 84 ++++- docs/gallery/triangulate-tangents/index.html | 84 ++++- docs/gallery/turntable/index.html | 84 ++++- docs/gallery/uv-layer-grid/index.html | 84 ++++- docs/gallery/vertex-weight-limit/index.html | 84 ++++- docs/gallery/vse-cut-list/index.html | 84 ++++- docs/gallery/vse-gamma-cross/index.html | 84 ++++- docs/gallery/wave-displace/index.html | 84 ++++- 41 files changed, 3494 insertions(+), 212 deletions(-) diff --git a/docs/gallery/armature-bend/index.html b/docs/gallery/armature-bend/index.html index dc0a16c..7a1031d 100644 --- a/docs/gallery/armature-bend/index.html +++ b/docs/gallery/armature-bend/index.html @@ -68,14 +68,82 @@ font-size: clamp(2.2rem, 5vw, 3.4rem); letter-spacing: 0.005em; line-height: 0.98; } header.hero p { color: var(--text-dim); max-width: 62ch; margin-top: 0.7rem; font-size: 1rem; } - /* ---- index: filter chips ---- */ - .chips { max-width: var(--maxw); margin: 0 auto; padding: 0 1.25rem 0.25rem; - display: flex; flex-wrap: wrap; gap: 0.5rem; } + /* ---- index: sticky controls (search, density toggle, tag chips) ---- */ + .controls { position: sticky; top: 46px; z-index: 4; + background: color-mix(in srgb, var(--bg) 94%, transparent); + backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); + border-bottom: 1px solid var(--border); } + .controls-inner { max-width: var(--maxw); margin: 0 auto; padding: 0.55rem 1.25rem 0.6rem; + display: flex; flex-direction: column; gap: 0.5rem; } + .controls-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; } + .searchwrap { position: relative; flex: 1 1 240px; min-width: 150px; } + .searchwrap input { width: 100%; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text); border-radius: var(--radius); padding: 0.34rem 1.9rem 0.34rem 0.7rem; + font-family: var(--font-sans); font-size: 0.85rem; line-height: 1.4; } + .searchwrap input:focus { border-color: var(--select); outline: none; } + .searchwrap input::placeholder { color: var(--text-dim); } + .searchwrap input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; } + .q-clear { position: absolute; right: 0.2rem; top: 50%; transform: translateY(-50%); + background: none; border: none; color: var(--text-dim); font-size: 1.05rem; line-height: 1; + cursor: pointer; padding: 0.25rem 0.45rem; border-radius: 3px; } + .q-clear:hover { color: var(--select); } + .count { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; color: var(--text-dim); white-space: nowrap; } + .density { display: flex; border: 1px solid var(--border); border-radius: var(--radius); + overflow: hidden; } + .density-btn { background: var(--surface-2); border: none; color: var(--text-dim); cursor: pointer; + font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; padding: 0.36rem 0.7rem; transition: color 0.15s, background 0.15s; } + .density-btn + .density-btn { border-left: 1px solid var(--border); } + .density-btn:hover { color: var(--select); } + .density-btn.active { background: var(--select); color: #1a1b1e; } + .tags-toggle { display: none; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text-dim); border-radius: 3px; padding: 0.3rem 0.7rem; cursor: pointer; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; text-transform: uppercase; } + .tags-toggle:hover, .tags-toggle.has-active { color: var(--select); border-color: var(--select); } + + /* Tag chips wrap by default (no-JS safe). With JS the row becomes a scroll + strip on wide viewports and collapses behind the Tags toggle on narrow + ones, so the sticky bar never eats the mobile viewport. */ + .chips { display: flex; flex-wrap: wrap; gap: 0.4rem; } + html.js .chips { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 0.25rem; + scrollbar-width: thin; scrollbar-color: var(--border) transparent; } + html.js .chips::-webkit-scrollbar { height: 5px; } + html.js .chips::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } .chip { background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); border-radius: 3px; padding: 0.22rem 0.7rem; font-size: 0.72rem; font-weight: 400; - font-family: var(--font-mono); cursor: pointer; transition: color 0.15s, border-color 0.15s; } + font-family: var(--font-mono); cursor: pointer; white-space: nowrap; flex: 0 0 auto; + transition: color 0.15s, border-color 0.15s; } .chip:hover { color: var(--select); border-color: var(--select); } .chip.active { color: #1a1b1e; background: var(--select); border-color: var(--select); } + @media (max-width: 719px) { + html.js .tags-toggle { display: inline-block; } + html.js .chips { display: none; } + html.js .chips.open { display: flex; flex-wrap: wrap; overflow-y: auto; max-height: 40vh; } + } + + /* Compact density: hero + name + one-line teaser. The description and + WITNESSES text stay in the DOM (searchable, screen-reader reachable, + present with JS disabled when the class is never applied) but are + visually collapsed. Applied only by JS via the density-compact class. */ + html.density-compact .grid { gap: 1rem; } + html.density-compact .card-body { padding: 0.65rem 0.9rem 0.7rem; } + html.density-compact .card-body h2 { font-size: 0.88rem; margin-bottom: 0.15rem; } + html.density-compact .teaches { font-size: 0.8rem; color: var(--text-dim); margin-bottom: 0; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + html.density-compact .witnesses { position: absolute; width: 1px; height: 1px; margin: -1px; + padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; } + html.density-compact .card-link { display: none; } + .noresults { color: var(--text-dim); text-align: center; padding: 3rem 1rem; font-size: 0.95rem; } + .noresults .chip { margin-left: 0.6rem; } + .to-top { position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 6; cursor: pointer; + background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); + border-radius: var(--radius); padding: 0.45rem 0.75rem; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; + text-transform: uppercase; opacity: 0; visibility: hidden; + transition: opacity 0.2s, visibility 0.2s, color 0.15s, border-color 0.15s; } + .to-top.show { opacity: 1; visibility: visible; } + .to-top:hover { color: var(--select); border-color: var(--select); } main { max-width: var(--maxw); margin: 0 auto; padding: 1rem 1.25rem 2rem; } .grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; align-items: stretch; } @@ -153,10 +221,18 @@ footer .statusbar code { font-family: inherit; text-transform: none; } @media (min-width: 720px) { .grid { grid-template-columns: 1fr 1fr; gap: 1.75rem; } } + @media (min-width: 560px) { html.density-compact .grid { grid-template-columns: 1fr 1fr; gap: 1rem; } } + @media (min-width: 900px) { html.density-compact .grid { grid-template-columns: repeat(3, 1fr); } } + @media (min-width: 1560px) { + html.density-compact .controls-inner, + html.density-compact main { max-width: 1400px; } + html.density-compact .grid { grid-template-columns: repeat(4, 1fr); } + } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } .card { transition: none; } .card:hover { transform: none; } + .to-top { transition: none; } } diff --git a/docs/gallery/attribute-domain-shear/index.html b/docs/gallery/attribute-domain-shear/index.html index c8e5813..ac3e0c0 100644 --- a/docs/gallery/attribute-domain-shear/index.html +++ b/docs/gallery/attribute-domain-shear/index.html @@ -68,14 +68,82 @@ font-size: clamp(2.2rem, 5vw, 3.4rem); letter-spacing: 0.005em; line-height: 0.98; } header.hero p { color: var(--text-dim); max-width: 62ch; margin-top: 0.7rem; font-size: 1rem; } - /* ---- index: filter chips ---- */ - .chips { max-width: var(--maxw); margin: 0 auto; padding: 0 1.25rem 0.25rem; - display: flex; flex-wrap: wrap; gap: 0.5rem; } + /* ---- index: sticky controls (search, density toggle, tag chips) ---- */ + .controls { position: sticky; top: 46px; z-index: 4; + background: color-mix(in srgb, var(--bg) 94%, transparent); + backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); + border-bottom: 1px solid var(--border); } + .controls-inner { max-width: var(--maxw); margin: 0 auto; padding: 0.55rem 1.25rem 0.6rem; + display: flex; flex-direction: column; gap: 0.5rem; } + .controls-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; } + .searchwrap { position: relative; flex: 1 1 240px; min-width: 150px; } + .searchwrap input { width: 100%; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text); border-radius: var(--radius); padding: 0.34rem 1.9rem 0.34rem 0.7rem; + font-family: var(--font-sans); font-size: 0.85rem; line-height: 1.4; } + .searchwrap input:focus { border-color: var(--select); outline: none; } + .searchwrap input::placeholder { color: var(--text-dim); } + .searchwrap input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; } + .q-clear { position: absolute; right: 0.2rem; top: 50%; transform: translateY(-50%); + background: none; border: none; color: var(--text-dim); font-size: 1.05rem; line-height: 1; + cursor: pointer; padding: 0.25rem 0.45rem; border-radius: 3px; } + .q-clear:hover { color: var(--select); } + .count { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; color: var(--text-dim); white-space: nowrap; } + .density { display: flex; border: 1px solid var(--border); border-radius: var(--radius); + overflow: hidden; } + .density-btn { background: var(--surface-2); border: none; color: var(--text-dim); cursor: pointer; + font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; padding: 0.36rem 0.7rem; transition: color 0.15s, background 0.15s; } + .density-btn + .density-btn { border-left: 1px solid var(--border); } + .density-btn:hover { color: var(--select); } + .density-btn.active { background: var(--select); color: #1a1b1e; } + .tags-toggle { display: none; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text-dim); border-radius: 3px; padding: 0.3rem 0.7rem; cursor: pointer; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; text-transform: uppercase; } + .tags-toggle:hover, .tags-toggle.has-active { color: var(--select); border-color: var(--select); } + + /* Tag chips wrap by default (no-JS safe). With JS the row becomes a scroll + strip on wide viewports and collapses behind the Tags toggle on narrow + ones, so the sticky bar never eats the mobile viewport. */ + .chips { display: flex; flex-wrap: wrap; gap: 0.4rem; } + html.js .chips { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 0.25rem; + scrollbar-width: thin; scrollbar-color: var(--border) transparent; } + html.js .chips::-webkit-scrollbar { height: 5px; } + html.js .chips::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } .chip { background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); border-radius: 3px; padding: 0.22rem 0.7rem; font-size: 0.72rem; font-weight: 400; - font-family: var(--font-mono); cursor: pointer; transition: color 0.15s, border-color 0.15s; } + font-family: var(--font-mono); cursor: pointer; white-space: nowrap; flex: 0 0 auto; + transition: color 0.15s, border-color 0.15s; } .chip:hover { color: var(--select); border-color: var(--select); } .chip.active { color: #1a1b1e; background: var(--select); border-color: var(--select); } + @media (max-width: 719px) { + html.js .tags-toggle { display: inline-block; } + html.js .chips { display: none; } + html.js .chips.open { display: flex; flex-wrap: wrap; overflow-y: auto; max-height: 40vh; } + } + + /* Compact density: hero + name + one-line teaser. The description and + WITNESSES text stay in the DOM (searchable, screen-reader reachable, + present with JS disabled when the class is never applied) but are + visually collapsed. Applied only by JS via the density-compact class. */ + html.density-compact .grid { gap: 1rem; } + html.density-compact .card-body { padding: 0.65rem 0.9rem 0.7rem; } + html.density-compact .card-body h2 { font-size: 0.88rem; margin-bottom: 0.15rem; } + html.density-compact .teaches { font-size: 0.8rem; color: var(--text-dim); margin-bottom: 0; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + html.density-compact .witnesses { position: absolute; width: 1px; height: 1px; margin: -1px; + padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; } + html.density-compact .card-link { display: none; } + .noresults { color: var(--text-dim); text-align: center; padding: 3rem 1rem; font-size: 0.95rem; } + .noresults .chip { margin-left: 0.6rem; } + .to-top { position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 6; cursor: pointer; + background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); + border-radius: var(--radius); padding: 0.45rem 0.75rem; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; + text-transform: uppercase; opacity: 0; visibility: hidden; + transition: opacity 0.2s, visibility 0.2s, color 0.15s, border-color 0.15s; } + .to-top.show { opacity: 1; visibility: visible; } + .to-top:hover { color: var(--select); border-color: var(--select); } main { max-width: var(--maxw); margin: 0 auto; padding: 1rem 1.25rem 2rem; } .grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; align-items: stretch; } @@ -153,10 +221,18 @@ footer .statusbar code { font-family: inherit; text-transform: none; } @media (min-width: 720px) { .grid { grid-template-columns: 1fr 1fr; gap: 1.75rem; } } + @media (min-width: 560px) { html.density-compact .grid { grid-template-columns: 1fr 1fr; gap: 1rem; } } + @media (min-width: 900px) { html.density-compact .grid { grid-template-columns: repeat(3, 1fr); } } + @media (min-width: 1560px) { + html.density-compact .controls-inner, + html.density-compact main { max-width: 1400px; } + html.density-compact .grid { grid-template-columns: repeat(4, 1fr); } + } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } .card { transition: none; } .card:hover { transform: none; } + .to-top { transition: none; } } diff --git a/docs/gallery/bmesh-gear/index.html b/docs/gallery/bmesh-gear/index.html index 25983f7..bf181c7 100644 --- a/docs/gallery/bmesh-gear/index.html +++ b/docs/gallery/bmesh-gear/index.html @@ -68,14 +68,82 @@ font-size: clamp(2.2rem, 5vw, 3.4rem); letter-spacing: 0.005em; line-height: 0.98; } header.hero p { color: var(--text-dim); max-width: 62ch; margin-top: 0.7rem; font-size: 1rem; } - /* ---- index: filter chips ---- */ - .chips { max-width: var(--maxw); margin: 0 auto; padding: 0 1.25rem 0.25rem; - display: flex; flex-wrap: wrap; gap: 0.5rem; } + /* ---- index: sticky controls (search, density toggle, tag chips) ---- */ + .controls { position: sticky; top: 46px; z-index: 4; + background: color-mix(in srgb, var(--bg) 94%, transparent); + backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); + border-bottom: 1px solid var(--border); } + .controls-inner { max-width: var(--maxw); margin: 0 auto; padding: 0.55rem 1.25rem 0.6rem; + display: flex; flex-direction: column; gap: 0.5rem; } + .controls-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; } + .searchwrap { position: relative; flex: 1 1 240px; min-width: 150px; } + .searchwrap input { width: 100%; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text); border-radius: var(--radius); padding: 0.34rem 1.9rem 0.34rem 0.7rem; + font-family: var(--font-sans); font-size: 0.85rem; line-height: 1.4; } + .searchwrap input:focus { border-color: var(--select); outline: none; } + .searchwrap input::placeholder { color: var(--text-dim); } + .searchwrap input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; } + .q-clear { position: absolute; right: 0.2rem; top: 50%; transform: translateY(-50%); + background: none; border: none; color: var(--text-dim); font-size: 1.05rem; line-height: 1; + cursor: pointer; padding: 0.25rem 0.45rem; border-radius: 3px; } + .q-clear:hover { color: var(--select); } + .count { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; color: var(--text-dim); white-space: nowrap; } + .density { display: flex; border: 1px solid var(--border); border-radius: var(--radius); + overflow: hidden; } + .density-btn { background: var(--surface-2); border: none; color: var(--text-dim); cursor: pointer; + font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; padding: 0.36rem 0.7rem; transition: color 0.15s, background 0.15s; } + .density-btn + .density-btn { border-left: 1px solid var(--border); } + .density-btn:hover { color: var(--select); } + .density-btn.active { background: var(--select); color: #1a1b1e; } + .tags-toggle { display: none; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text-dim); border-radius: 3px; padding: 0.3rem 0.7rem; cursor: pointer; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; text-transform: uppercase; } + .tags-toggle:hover, .tags-toggle.has-active { color: var(--select); border-color: var(--select); } + + /* Tag chips wrap by default (no-JS safe). With JS the row becomes a scroll + strip on wide viewports and collapses behind the Tags toggle on narrow + ones, so the sticky bar never eats the mobile viewport. */ + .chips { display: flex; flex-wrap: wrap; gap: 0.4rem; } + html.js .chips { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 0.25rem; + scrollbar-width: thin; scrollbar-color: var(--border) transparent; } + html.js .chips::-webkit-scrollbar { height: 5px; } + html.js .chips::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } .chip { background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); border-radius: 3px; padding: 0.22rem 0.7rem; font-size: 0.72rem; font-weight: 400; - font-family: var(--font-mono); cursor: pointer; transition: color 0.15s, border-color 0.15s; } + font-family: var(--font-mono); cursor: pointer; white-space: nowrap; flex: 0 0 auto; + transition: color 0.15s, border-color 0.15s; } .chip:hover { color: var(--select); border-color: var(--select); } .chip.active { color: #1a1b1e; background: var(--select); border-color: var(--select); } + @media (max-width: 719px) { + html.js .tags-toggle { display: inline-block; } + html.js .chips { display: none; } + html.js .chips.open { display: flex; flex-wrap: wrap; overflow-y: auto; max-height: 40vh; } + } + + /* Compact density: hero + name + one-line teaser. The description and + WITNESSES text stay in the DOM (searchable, screen-reader reachable, + present with JS disabled when the class is never applied) but are + visually collapsed. Applied only by JS via the density-compact class. */ + html.density-compact .grid { gap: 1rem; } + html.density-compact .card-body { padding: 0.65rem 0.9rem 0.7rem; } + html.density-compact .card-body h2 { font-size: 0.88rem; margin-bottom: 0.15rem; } + html.density-compact .teaches { font-size: 0.8rem; color: var(--text-dim); margin-bottom: 0; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + html.density-compact .witnesses { position: absolute; width: 1px; height: 1px; margin: -1px; + padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; } + html.density-compact .card-link { display: none; } + .noresults { color: var(--text-dim); text-align: center; padding: 3rem 1rem; font-size: 0.95rem; } + .noresults .chip { margin-left: 0.6rem; } + .to-top { position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 6; cursor: pointer; + background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); + border-radius: var(--radius); padding: 0.45rem 0.75rem; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; + text-transform: uppercase; opacity: 0; visibility: hidden; + transition: opacity 0.2s, visibility 0.2s, color 0.15s, border-color 0.15s; } + .to-top.show { opacity: 1; visibility: visible; } + .to-top:hover { color: var(--select); border-color: var(--select); } main { max-width: var(--maxw); margin: 0 auto; padding: 1rem 1.25rem 2rem; } .grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; align-items: stretch; } @@ -153,10 +221,18 @@ footer .statusbar code { font-family: inherit; text-transform: none; } @media (min-width: 720px) { .grid { grid-template-columns: 1fr 1fr; gap: 1.75rem; } } + @media (min-width: 560px) { html.density-compact .grid { grid-template-columns: 1fr 1fr; gap: 1rem; } } + @media (min-width: 900px) { html.density-compact .grid { grid-template-columns: repeat(3, 1fr); } } + @media (min-width: 1560px) { + html.density-compact .controls-inner, + html.density-compact main { max-width: 1400px; } + html.density-compact .grid { grid-template-columns: repeat(4, 1fr); } + } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } .card { transition: none; } .card:hover { transform: none; } + .to-top { transition: none; } } diff --git a/docs/gallery/car-mirror-symmetry/index.html b/docs/gallery/car-mirror-symmetry/index.html index d41ceea..97cac45 100644 --- a/docs/gallery/car-mirror-symmetry/index.html +++ b/docs/gallery/car-mirror-symmetry/index.html @@ -68,14 +68,82 @@ font-size: clamp(2.2rem, 5vw, 3.4rem); letter-spacing: 0.005em; line-height: 0.98; } header.hero p { color: var(--text-dim); max-width: 62ch; margin-top: 0.7rem; font-size: 1rem; } - /* ---- index: filter chips ---- */ - .chips { max-width: var(--maxw); margin: 0 auto; padding: 0 1.25rem 0.25rem; - display: flex; flex-wrap: wrap; gap: 0.5rem; } + /* ---- index: sticky controls (search, density toggle, tag chips) ---- */ + .controls { position: sticky; top: 46px; z-index: 4; + background: color-mix(in srgb, var(--bg) 94%, transparent); + backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); + border-bottom: 1px solid var(--border); } + .controls-inner { max-width: var(--maxw); margin: 0 auto; padding: 0.55rem 1.25rem 0.6rem; + display: flex; flex-direction: column; gap: 0.5rem; } + .controls-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; } + .searchwrap { position: relative; flex: 1 1 240px; min-width: 150px; } + .searchwrap input { width: 100%; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text); border-radius: var(--radius); padding: 0.34rem 1.9rem 0.34rem 0.7rem; + font-family: var(--font-sans); font-size: 0.85rem; line-height: 1.4; } + .searchwrap input:focus { border-color: var(--select); outline: none; } + .searchwrap input::placeholder { color: var(--text-dim); } + .searchwrap input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; } + .q-clear { position: absolute; right: 0.2rem; top: 50%; transform: translateY(-50%); + background: none; border: none; color: var(--text-dim); font-size: 1.05rem; line-height: 1; + cursor: pointer; padding: 0.25rem 0.45rem; border-radius: 3px; } + .q-clear:hover { color: var(--select); } + .count { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; color: var(--text-dim); white-space: nowrap; } + .density { display: flex; border: 1px solid var(--border); border-radius: var(--radius); + overflow: hidden; } + .density-btn { background: var(--surface-2); border: none; color: var(--text-dim); cursor: pointer; + font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; padding: 0.36rem 0.7rem; transition: color 0.15s, background 0.15s; } + .density-btn + .density-btn { border-left: 1px solid var(--border); } + .density-btn:hover { color: var(--select); } + .density-btn.active { background: var(--select); color: #1a1b1e; } + .tags-toggle { display: none; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text-dim); border-radius: 3px; padding: 0.3rem 0.7rem; cursor: pointer; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; text-transform: uppercase; } + .tags-toggle:hover, .tags-toggle.has-active { color: var(--select); border-color: var(--select); } + + /* Tag chips wrap by default (no-JS safe). With JS the row becomes a scroll + strip on wide viewports and collapses behind the Tags toggle on narrow + ones, so the sticky bar never eats the mobile viewport. */ + .chips { display: flex; flex-wrap: wrap; gap: 0.4rem; } + html.js .chips { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 0.25rem; + scrollbar-width: thin; scrollbar-color: var(--border) transparent; } + html.js .chips::-webkit-scrollbar { height: 5px; } + html.js .chips::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } .chip { background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); border-radius: 3px; padding: 0.22rem 0.7rem; font-size: 0.72rem; font-weight: 400; - font-family: var(--font-mono); cursor: pointer; transition: color 0.15s, border-color 0.15s; } + font-family: var(--font-mono); cursor: pointer; white-space: nowrap; flex: 0 0 auto; + transition: color 0.15s, border-color 0.15s; } .chip:hover { color: var(--select); border-color: var(--select); } .chip.active { color: #1a1b1e; background: var(--select); border-color: var(--select); } + @media (max-width: 719px) { + html.js .tags-toggle { display: inline-block; } + html.js .chips { display: none; } + html.js .chips.open { display: flex; flex-wrap: wrap; overflow-y: auto; max-height: 40vh; } + } + + /* Compact density: hero + name + one-line teaser. The description and + WITNESSES text stay in the DOM (searchable, screen-reader reachable, + present with JS disabled when the class is never applied) but are + visually collapsed. Applied only by JS via the density-compact class. */ + html.density-compact .grid { gap: 1rem; } + html.density-compact .card-body { padding: 0.65rem 0.9rem 0.7rem; } + html.density-compact .card-body h2 { font-size: 0.88rem; margin-bottom: 0.15rem; } + html.density-compact .teaches { font-size: 0.8rem; color: var(--text-dim); margin-bottom: 0; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + html.density-compact .witnesses { position: absolute; width: 1px; height: 1px; margin: -1px; + padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; } + html.density-compact .card-link { display: none; } + .noresults { color: var(--text-dim); text-align: center; padding: 3rem 1rem; font-size: 0.95rem; } + .noresults .chip { margin-left: 0.6rem; } + .to-top { position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 6; cursor: pointer; + background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); + border-radius: var(--radius); padding: 0.45rem 0.75rem; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; + text-transform: uppercase; opacity: 0; visibility: hidden; + transition: opacity 0.2s, visibility 0.2s, color 0.15s, border-color 0.15s; } + .to-top.show { opacity: 1; visibility: visible; } + .to-top:hover { color: var(--select); border-color: var(--select); } main { max-width: var(--maxw); margin: 0 auto; padding: 1rem 1.25rem 2rem; } .grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; align-items: stretch; } @@ -153,10 +221,18 @@ footer .statusbar code { font-family: inherit; text-transform: none; } @media (min-width: 720px) { .grid { grid-template-columns: 1fr 1fr; gap: 1.75rem; } } + @media (min-width: 560px) { html.density-compact .grid { grid-template-columns: 1fr 1fr; gap: 1rem; } } + @media (min-width: 900px) { html.density-compact .grid { grid-template-columns: repeat(3, 1fr); } } + @media (min-width: 1560px) { + html.density-compact .controls-inner, + html.density-compact main { max-width: 1400px; } + html.density-compact .grid { grid-template-columns: repeat(4, 1fr); } + } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } .card { transition: none; } .card:hover { transform: none; } + .to-top { transition: none; } } diff --git a/docs/gallery/collision-hull-proxy/index.html b/docs/gallery/collision-hull-proxy/index.html index 5c84f2d..ed49a3c 100644 --- a/docs/gallery/collision-hull-proxy/index.html +++ b/docs/gallery/collision-hull-proxy/index.html @@ -68,14 +68,82 @@ font-size: clamp(2.2rem, 5vw, 3.4rem); letter-spacing: 0.005em; line-height: 0.98; } header.hero p { color: var(--text-dim); max-width: 62ch; margin-top: 0.7rem; font-size: 1rem; } - /* ---- index: filter chips ---- */ - .chips { max-width: var(--maxw); margin: 0 auto; padding: 0 1.25rem 0.25rem; - display: flex; flex-wrap: wrap; gap: 0.5rem; } + /* ---- index: sticky controls (search, density toggle, tag chips) ---- */ + .controls { position: sticky; top: 46px; z-index: 4; + background: color-mix(in srgb, var(--bg) 94%, transparent); + backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); + border-bottom: 1px solid var(--border); } + .controls-inner { max-width: var(--maxw); margin: 0 auto; padding: 0.55rem 1.25rem 0.6rem; + display: flex; flex-direction: column; gap: 0.5rem; } + .controls-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; } + .searchwrap { position: relative; flex: 1 1 240px; min-width: 150px; } + .searchwrap input { width: 100%; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text); border-radius: var(--radius); padding: 0.34rem 1.9rem 0.34rem 0.7rem; + font-family: var(--font-sans); font-size: 0.85rem; line-height: 1.4; } + .searchwrap input:focus { border-color: var(--select); outline: none; } + .searchwrap input::placeholder { color: var(--text-dim); } + .searchwrap input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; } + .q-clear { position: absolute; right: 0.2rem; top: 50%; transform: translateY(-50%); + background: none; border: none; color: var(--text-dim); font-size: 1.05rem; line-height: 1; + cursor: pointer; padding: 0.25rem 0.45rem; border-radius: 3px; } + .q-clear:hover { color: var(--select); } + .count { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; color: var(--text-dim); white-space: nowrap; } + .density { display: flex; border: 1px solid var(--border); border-radius: var(--radius); + overflow: hidden; } + .density-btn { background: var(--surface-2); border: none; color: var(--text-dim); cursor: pointer; + font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; padding: 0.36rem 0.7rem; transition: color 0.15s, background 0.15s; } + .density-btn + .density-btn { border-left: 1px solid var(--border); } + .density-btn:hover { color: var(--select); } + .density-btn.active { background: var(--select); color: #1a1b1e; } + .tags-toggle { display: none; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text-dim); border-radius: 3px; padding: 0.3rem 0.7rem; cursor: pointer; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; text-transform: uppercase; } + .tags-toggle:hover, .tags-toggle.has-active { color: var(--select); border-color: var(--select); } + + /* Tag chips wrap by default (no-JS safe). With JS the row becomes a scroll + strip on wide viewports and collapses behind the Tags toggle on narrow + ones, so the sticky bar never eats the mobile viewport. */ + .chips { display: flex; flex-wrap: wrap; gap: 0.4rem; } + html.js .chips { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 0.25rem; + scrollbar-width: thin; scrollbar-color: var(--border) transparent; } + html.js .chips::-webkit-scrollbar { height: 5px; } + html.js .chips::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } .chip { background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); border-radius: 3px; padding: 0.22rem 0.7rem; font-size: 0.72rem; font-weight: 400; - font-family: var(--font-mono); cursor: pointer; transition: color 0.15s, border-color 0.15s; } + font-family: var(--font-mono); cursor: pointer; white-space: nowrap; flex: 0 0 auto; + transition: color 0.15s, border-color 0.15s; } .chip:hover { color: var(--select); border-color: var(--select); } .chip.active { color: #1a1b1e; background: var(--select); border-color: var(--select); } + @media (max-width: 719px) { + html.js .tags-toggle { display: inline-block; } + html.js .chips { display: none; } + html.js .chips.open { display: flex; flex-wrap: wrap; overflow-y: auto; max-height: 40vh; } + } + + /* Compact density: hero + name + one-line teaser. The description and + WITNESSES text stay in the DOM (searchable, screen-reader reachable, + present with JS disabled when the class is never applied) but are + visually collapsed. Applied only by JS via the density-compact class. */ + html.density-compact .grid { gap: 1rem; } + html.density-compact .card-body { padding: 0.65rem 0.9rem 0.7rem; } + html.density-compact .card-body h2 { font-size: 0.88rem; margin-bottom: 0.15rem; } + html.density-compact .teaches { font-size: 0.8rem; color: var(--text-dim); margin-bottom: 0; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + html.density-compact .witnesses { position: absolute; width: 1px; height: 1px; margin: -1px; + padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; } + html.density-compact .card-link { display: none; } + .noresults { color: var(--text-dim); text-align: center; padding: 3rem 1rem; font-size: 0.95rem; } + .noresults .chip { margin-left: 0.6rem; } + .to-top { position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 6; cursor: pointer; + background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); + border-radius: var(--radius); padding: 0.45rem 0.75rem; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; + text-transform: uppercase; opacity: 0; visibility: hidden; + transition: opacity 0.2s, visibility 0.2s, color 0.15s, border-color 0.15s; } + .to-top.show { opacity: 1; visibility: visible; } + .to-top:hover { color: var(--select); border-color: var(--select); } main { max-width: var(--maxw); margin: 0 auto; padding: 1rem 1.25rem 2rem; } .grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; align-items: stretch; } @@ -153,10 +221,18 @@ footer .statusbar code { font-family: inherit; text-transform: none; } @media (min-width: 720px) { .grid { grid-template-columns: 1fr 1fr; gap: 1.75rem; } } + @media (min-width: 560px) { html.density-compact .grid { grid-template-columns: 1fr 1fr; gap: 1rem; } } + @media (min-width: 900px) { html.density-compact .grid { grid-template-columns: repeat(3, 1fr); } } + @media (min-width: 1560px) { + html.density-compact .controls-inner, + html.density-compact main { max-width: 1400px; } + html.density-compact .grid { grid-template-columns: repeat(4, 1fr); } + } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } .card { transition: none; } .card:hover { transform: none; } + .to-top { transition: none; } } diff --git a/docs/gallery/color-attribute-wheel/index.html b/docs/gallery/color-attribute-wheel/index.html index 2e13a3a..492d2f5 100644 --- a/docs/gallery/color-attribute-wheel/index.html +++ b/docs/gallery/color-attribute-wheel/index.html @@ -68,14 +68,82 @@ font-size: clamp(2.2rem, 5vw, 3.4rem); letter-spacing: 0.005em; line-height: 0.98; } header.hero p { color: var(--text-dim); max-width: 62ch; margin-top: 0.7rem; font-size: 1rem; } - /* ---- index: filter chips ---- */ - .chips { max-width: var(--maxw); margin: 0 auto; padding: 0 1.25rem 0.25rem; - display: flex; flex-wrap: wrap; gap: 0.5rem; } + /* ---- index: sticky controls (search, density toggle, tag chips) ---- */ + .controls { position: sticky; top: 46px; z-index: 4; + background: color-mix(in srgb, var(--bg) 94%, transparent); + backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); + border-bottom: 1px solid var(--border); } + .controls-inner { max-width: var(--maxw); margin: 0 auto; padding: 0.55rem 1.25rem 0.6rem; + display: flex; flex-direction: column; gap: 0.5rem; } + .controls-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; } + .searchwrap { position: relative; flex: 1 1 240px; min-width: 150px; } + .searchwrap input { width: 100%; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text); border-radius: var(--radius); padding: 0.34rem 1.9rem 0.34rem 0.7rem; + font-family: var(--font-sans); font-size: 0.85rem; line-height: 1.4; } + .searchwrap input:focus { border-color: var(--select); outline: none; } + .searchwrap input::placeholder { color: var(--text-dim); } + .searchwrap input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; } + .q-clear { position: absolute; right: 0.2rem; top: 50%; transform: translateY(-50%); + background: none; border: none; color: var(--text-dim); font-size: 1.05rem; line-height: 1; + cursor: pointer; padding: 0.25rem 0.45rem; border-radius: 3px; } + .q-clear:hover { color: var(--select); } + .count { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; color: var(--text-dim); white-space: nowrap; } + .density { display: flex; border: 1px solid var(--border); border-radius: var(--radius); + overflow: hidden; } + .density-btn { background: var(--surface-2); border: none; color: var(--text-dim); cursor: pointer; + font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; padding: 0.36rem 0.7rem; transition: color 0.15s, background 0.15s; } + .density-btn + .density-btn { border-left: 1px solid var(--border); } + .density-btn:hover { color: var(--select); } + .density-btn.active { background: var(--select); color: #1a1b1e; } + .tags-toggle { display: none; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text-dim); border-radius: 3px; padding: 0.3rem 0.7rem; cursor: pointer; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; text-transform: uppercase; } + .tags-toggle:hover, .tags-toggle.has-active { color: var(--select); border-color: var(--select); } + + /* Tag chips wrap by default (no-JS safe). With JS the row becomes a scroll + strip on wide viewports and collapses behind the Tags toggle on narrow + ones, so the sticky bar never eats the mobile viewport. */ + .chips { display: flex; flex-wrap: wrap; gap: 0.4rem; } + html.js .chips { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 0.25rem; + scrollbar-width: thin; scrollbar-color: var(--border) transparent; } + html.js .chips::-webkit-scrollbar { height: 5px; } + html.js .chips::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } .chip { background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); border-radius: 3px; padding: 0.22rem 0.7rem; font-size: 0.72rem; font-weight: 400; - font-family: var(--font-mono); cursor: pointer; transition: color 0.15s, border-color 0.15s; } + font-family: var(--font-mono); cursor: pointer; white-space: nowrap; flex: 0 0 auto; + transition: color 0.15s, border-color 0.15s; } .chip:hover { color: var(--select); border-color: var(--select); } .chip.active { color: #1a1b1e; background: var(--select); border-color: var(--select); } + @media (max-width: 719px) { + html.js .tags-toggle { display: inline-block; } + html.js .chips { display: none; } + html.js .chips.open { display: flex; flex-wrap: wrap; overflow-y: auto; max-height: 40vh; } + } + + /* Compact density: hero + name + one-line teaser. The description and + WITNESSES text stay in the DOM (searchable, screen-reader reachable, + present with JS disabled when the class is never applied) but are + visually collapsed. Applied only by JS via the density-compact class. */ + html.density-compact .grid { gap: 1rem; } + html.density-compact .card-body { padding: 0.65rem 0.9rem 0.7rem; } + html.density-compact .card-body h2 { font-size: 0.88rem; margin-bottom: 0.15rem; } + html.density-compact .teaches { font-size: 0.8rem; color: var(--text-dim); margin-bottom: 0; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + html.density-compact .witnesses { position: absolute; width: 1px; height: 1px; margin: -1px; + padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; } + html.density-compact .card-link { display: none; } + .noresults { color: var(--text-dim); text-align: center; padding: 3rem 1rem; font-size: 0.95rem; } + .noresults .chip { margin-left: 0.6rem; } + .to-top { position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 6; cursor: pointer; + background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); + border-radius: var(--radius); padding: 0.45rem 0.75rem; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; + text-transform: uppercase; opacity: 0; visibility: hidden; + transition: opacity 0.2s, visibility 0.2s, color 0.15s, border-color 0.15s; } + .to-top.show { opacity: 1; visibility: visible; } + .to-top:hover { color: var(--select); border-color: var(--select); } main { max-width: var(--maxw); margin: 0 auto; padding: 1rem 1.25rem 2rem; } .grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; align-items: stretch; } @@ -153,10 +221,18 @@ footer .statusbar code { font-family: inherit; text-transform: none; } @media (min-width: 720px) { .grid { grid-template-columns: 1fr 1fr; gap: 1.75rem; } } + @media (min-width: 560px) { html.density-compact .grid { grid-template-columns: 1fr 1fr; gap: 1rem; } } + @media (min-width: 900px) { html.density-compact .grid { grid-template-columns: repeat(3, 1fr); } } + @media (min-width: 1560px) { + html.density-compact .controls-inner, + html.density-compact main { max-width: 1400px; } + html.density-compact .grid { grid-template-columns: repeat(4, 1fr); } + } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } .card { transition: none; } .card:hover { transform: none; } + .to-top { transition: none; } } diff --git a/docs/gallery/compositor-glare/index.html b/docs/gallery/compositor-glare/index.html index 9277d8a..db5456d 100644 --- a/docs/gallery/compositor-glare/index.html +++ b/docs/gallery/compositor-glare/index.html @@ -68,14 +68,82 @@ font-size: clamp(2.2rem, 5vw, 3.4rem); letter-spacing: 0.005em; line-height: 0.98; } header.hero p { color: var(--text-dim); max-width: 62ch; margin-top: 0.7rem; font-size: 1rem; } - /* ---- index: filter chips ---- */ - .chips { max-width: var(--maxw); margin: 0 auto; padding: 0 1.25rem 0.25rem; - display: flex; flex-wrap: wrap; gap: 0.5rem; } + /* ---- index: sticky controls (search, density toggle, tag chips) ---- */ + .controls { position: sticky; top: 46px; z-index: 4; + background: color-mix(in srgb, var(--bg) 94%, transparent); + backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); + border-bottom: 1px solid var(--border); } + .controls-inner { max-width: var(--maxw); margin: 0 auto; padding: 0.55rem 1.25rem 0.6rem; + display: flex; flex-direction: column; gap: 0.5rem; } + .controls-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; } + .searchwrap { position: relative; flex: 1 1 240px; min-width: 150px; } + .searchwrap input { width: 100%; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text); border-radius: var(--radius); padding: 0.34rem 1.9rem 0.34rem 0.7rem; + font-family: var(--font-sans); font-size: 0.85rem; line-height: 1.4; } + .searchwrap input:focus { border-color: var(--select); outline: none; } + .searchwrap input::placeholder { color: var(--text-dim); } + .searchwrap input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; } + .q-clear { position: absolute; right: 0.2rem; top: 50%; transform: translateY(-50%); + background: none; border: none; color: var(--text-dim); font-size: 1.05rem; line-height: 1; + cursor: pointer; padding: 0.25rem 0.45rem; border-radius: 3px; } + .q-clear:hover { color: var(--select); } + .count { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; color: var(--text-dim); white-space: nowrap; } + .density { display: flex; border: 1px solid var(--border); border-radius: var(--radius); + overflow: hidden; } + .density-btn { background: var(--surface-2); border: none; color: var(--text-dim); cursor: pointer; + font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; padding: 0.36rem 0.7rem; transition: color 0.15s, background 0.15s; } + .density-btn + .density-btn { border-left: 1px solid var(--border); } + .density-btn:hover { color: var(--select); } + .density-btn.active { background: var(--select); color: #1a1b1e; } + .tags-toggle { display: none; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text-dim); border-radius: 3px; padding: 0.3rem 0.7rem; cursor: pointer; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; text-transform: uppercase; } + .tags-toggle:hover, .tags-toggle.has-active { color: var(--select); border-color: var(--select); } + + /* Tag chips wrap by default (no-JS safe). With JS the row becomes a scroll + strip on wide viewports and collapses behind the Tags toggle on narrow + ones, so the sticky bar never eats the mobile viewport. */ + .chips { display: flex; flex-wrap: wrap; gap: 0.4rem; } + html.js .chips { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 0.25rem; + scrollbar-width: thin; scrollbar-color: var(--border) transparent; } + html.js .chips::-webkit-scrollbar { height: 5px; } + html.js .chips::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } .chip { background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); border-radius: 3px; padding: 0.22rem 0.7rem; font-size: 0.72rem; font-weight: 400; - font-family: var(--font-mono); cursor: pointer; transition: color 0.15s, border-color 0.15s; } + font-family: var(--font-mono); cursor: pointer; white-space: nowrap; flex: 0 0 auto; + transition: color 0.15s, border-color 0.15s; } .chip:hover { color: var(--select); border-color: var(--select); } .chip.active { color: #1a1b1e; background: var(--select); border-color: var(--select); } + @media (max-width: 719px) { + html.js .tags-toggle { display: inline-block; } + html.js .chips { display: none; } + html.js .chips.open { display: flex; flex-wrap: wrap; overflow-y: auto; max-height: 40vh; } + } + + /* Compact density: hero + name + one-line teaser. The description and + WITNESSES text stay in the DOM (searchable, screen-reader reachable, + present with JS disabled when the class is never applied) but are + visually collapsed. Applied only by JS via the density-compact class. */ + html.density-compact .grid { gap: 1rem; } + html.density-compact .card-body { padding: 0.65rem 0.9rem 0.7rem; } + html.density-compact .card-body h2 { font-size: 0.88rem; margin-bottom: 0.15rem; } + html.density-compact .teaches { font-size: 0.8rem; color: var(--text-dim); margin-bottom: 0; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + html.density-compact .witnesses { position: absolute; width: 1px; height: 1px; margin: -1px; + padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; } + html.density-compact .card-link { display: none; } + .noresults { color: var(--text-dim); text-align: center; padding: 3rem 1rem; font-size: 0.95rem; } + .noresults .chip { margin-left: 0.6rem; } + .to-top { position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 6; cursor: pointer; + background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); + border-radius: var(--radius); padding: 0.45rem 0.75rem; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; + text-transform: uppercase; opacity: 0; visibility: hidden; + transition: opacity 0.2s, visibility 0.2s, color 0.15s, border-color 0.15s; } + .to-top.show { opacity: 1; visibility: visible; } + .to-top:hover { color: var(--select); border-color: var(--select); } main { max-width: var(--maxw); margin: 0 auto; padding: 1rem 1.25rem 2rem; } .grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; align-items: stretch; } @@ -153,10 +221,18 @@ footer .statusbar code { font-family: inherit; text-transform: none; } @media (min-width: 720px) { .grid { grid-template-columns: 1fr 1fr; gap: 1.75rem; } } + @media (min-width: 560px) { html.density-compact .grid { grid-template-columns: 1fr 1fr; gap: 1rem; } } + @media (min-width: 900px) { html.density-compact .grid { grid-template-columns: repeat(3, 1fr); } } + @media (min-width: 1560px) { + html.density-compact .controls-inner, + html.density-compact main { max-width: 1400px; } + html.density-compact .grid { grid-template-columns: repeat(4, 1fr); } + } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } .card { transition: none; } .card:hover { transform: none; } + .to-top { transition: none; } } diff --git a/docs/gallery/curve-bevel-arc/index.html b/docs/gallery/curve-bevel-arc/index.html index 4f9641c..3bc0b25 100644 --- a/docs/gallery/curve-bevel-arc/index.html +++ b/docs/gallery/curve-bevel-arc/index.html @@ -68,14 +68,82 @@ font-size: clamp(2.2rem, 5vw, 3.4rem); letter-spacing: 0.005em; line-height: 0.98; } header.hero p { color: var(--text-dim); max-width: 62ch; margin-top: 0.7rem; font-size: 1rem; } - /* ---- index: filter chips ---- */ - .chips { max-width: var(--maxw); margin: 0 auto; padding: 0 1.25rem 0.25rem; - display: flex; flex-wrap: wrap; gap: 0.5rem; } + /* ---- index: sticky controls (search, density toggle, tag chips) ---- */ + .controls { position: sticky; top: 46px; z-index: 4; + background: color-mix(in srgb, var(--bg) 94%, transparent); + backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); + border-bottom: 1px solid var(--border); } + .controls-inner { max-width: var(--maxw); margin: 0 auto; padding: 0.55rem 1.25rem 0.6rem; + display: flex; flex-direction: column; gap: 0.5rem; } + .controls-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; } + .searchwrap { position: relative; flex: 1 1 240px; min-width: 150px; } + .searchwrap input { width: 100%; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text); border-radius: var(--radius); padding: 0.34rem 1.9rem 0.34rem 0.7rem; + font-family: var(--font-sans); font-size: 0.85rem; line-height: 1.4; } + .searchwrap input:focus { border-color: var(--select); outline: none; } + .searchwrap input::placeholder { color: var(--text-dim); } + .searchwrap input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; } + .q-clear { position: absolute; right: 0.2rem; top: 50%; transform: translateY(-50%); + background: none; border: none; color: var(--text-dim); font-size: 1.05rem; line-height: 1; + cursor: pointer; padding: 0.25rem 0.45rem; border-radius: 3px; } + .q-clear:hover { color: var(--select); } + .count { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; color: var(--text-dim); white-space: nowrap; } + .density { display: flex; border: 1px solid var(--border); border-radius: var(--radius); + overflow: hidden; } + .density-btn { background: var(--surface-2); border: none; color: var(--text-dim); cursor: pointer; + font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; padding: 0.36rem 0.7rem; transition: color 0.15s, background 0.15s; } + .density-btn + .density-btn { border-left: 1px solid var(--border); } + .density-btn:hover { color: var(--select); } + .density-btn.active { background: var(--select); color: #1a1b1e; } + .tags-toggle { display: none; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text-dim); border-radius: 3px; padding: 0.3rem 0.7rem; cursor: pointer; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; text-transform: uppercase; } + .tags-toggle:hover, .tags-toggle.has-active { color: var(--select); border-color: var(--select); } + + /* Tag chips wrap by default (no-JS safe). With JS the row becomes a scroll + strip on wide viewports and collapses behind the Tags toggle on narrow + ones, so the sticky bar never eats the mobile viewport. */ + .chips { display: flex; flex-wrap: wrap; gap: 0.4rem; } + html.js .chips { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 0.25rem; + scrollbar-width: thin; scrollbar-color: var(--border) transparent; } + html.js .chips::-webkit-scrollbar { height: 5px; } + html.js .chips::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } .chip { background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); border-radius: 3px; padding: 0.22rem 0.7rem; font-size: 0.72rem; font-weight: 400; - font-family: var(--font-mono); cursor: pointer; transition: color 0.15s, border-color 0.15s; } + font-family: var(--font-mono); cursor: pointer; white-space: nowrap; flex: 0 0 auto; + transition: color 0.15s, border-color 0.15s; } .chip:hover { color: var(--select); border-color: var(--select); } .chip.active { color: #1a1b1e; background: var(--select); border-color: var(--select); } + @media (max-width: 719px) { + html.js .tags-toggle { display: inline-block; } + html.js .chips { display: none; } + html.js .chips.open { display: flex; flex-wrap: wrap; overflow-y: auto; max-height: 40vh; } + } + + /* Compact density: hero + name + one-line teaser. The description and + WITNESSES text stay in the DOM (searchable, screen-reader reachable, + present with JS disabled when the class is never applied) but are + visually collapsed. Applied only by JS via the density-compact class. */ + html.density-compact .grid { gap: 1rem; } + html.density-compact .card-body { padding: 0.65rem 0.9rem 0.7rem; } + html.density-compact .card-body h2 { font-size: 0.88rem; margin-bottom: 0.15rem; } + html.density-compact .teaches { font-size: 0.8rem; color: var(--text-dim); margin-bottom: 0; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + html.density-compact .witnesses { position: absolute; width: 1px; height: 1px; margin: -1px; + padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; } + html.density-compact .card-link { display: none; } + .noresults { color: var(--text-dim); text-align: center; padding: 3rem 1rem; font-size: 0.95rem; } + .noresults .chip { margin-left: 0.6rem; } + .to-top { position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 6; cursor: pointer; + background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); + border-radius: var(--radius); padding: 0.45rem 0.75rem; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; + text-transform: uppercase; opacity: 0; visibility: hidden; + transition: opacity 0.2s, visibility 0.2s, color 0.15s, border-color 0.15s; } + .to-top.show { opacity: 1; visibility: visible; } + .to-top:hover { color: var(--select); border-color: var(--select); } main { max-width: var(--maxw); margin: 0 auto; padding: 1rem 1.25rem 2rem; } .grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; align-items: stretch; } @@ -153,10 +221,18 @@ footer .statusbar code { font-family: inherit; text-transform: none; } @media (min-width: 720px) { .grid { grid-template-columns: 1fr 1fr; gap: 1.75rem; } } + @media (min-width: 560px) { html.density-compact .grid { grid-template-columns: 1fr 1fr; gap: 1rem; } } + @media (min-width: 900px) { html.density-compact .grid { grid-template-columns: repeat(3, 1fr); } } + @media (min-width: 1560px) { + html.density-compact .controls-inner, + html.density-compact main { max-width: 1400px; } + html.density-compact .grid { grid-template-columns: repeat(4, 1fr); } + } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } .card { transition: none; } .card:hover { transform: none; } + .to-top { transition: none; } } diff --git a/docs/gallery/custom-normals-shade/index.html b/docs/gallery/custom-normals-shade/index.html index f9193f1..edca46c 100644 --- a/docs/gallery/custom-normals-shade/index.html +++ b/docs/gallery/custom-normals-shade/index.html @@ -68,14 +68,82 @@ font-size: clamp(2.2rem, 5vw, 3.4rem); letter-spacing: 0.005em; line-height: 0.98; } header.hero p { color: var(--text-dim); max-width: 62ch; margin-top: 0.7rem; font-size: 1rem; } - /* ---- index: filter chips ---- */ - .chips { max-width: var(--maxw); margin: 0 auto; padding: 0 1.25rem 0.25rem; - display: flex; flex-wrap: wrap; gap: 0.5rem; } + /* ---- index: sticky controls (search, density toggle, tag chips) ---- */ + .controls { position: sticky; top: 46px; z-index: 4; + background: color-mix(in srgb, var(--bg) 94%, transparent); + backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); + border-bottom: 1px solid var(--border); } + .controls-inner { max-width: var(--maxw); margin: 0 auto; padding: 0.55rem 1.25rem 0.6rem; + display: flex; flex-direction: column; gap: 0.5rem; } + .controls-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; } + .searchwrap { position: relative; flex: 1 1 240px; min-width: 150px; } + .searchwrap input { width: 100%; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text); border-radius: var(--radius); padding: 0.34rem 1.9rem 0.34rem 0.7rem; + font-family: var(--font-sans); font-size: 0.85rem; line-height: 1.4; } + .searchwrap input:focus { border-color: var(--select); outline: none; } + .searchwrap input::placeholder { color: var(--text-dim); } + .searchwrap input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; } + .q-clear { position: absolute; right: 0.2rem; top: 50%; transform: translateY(-50%); + background: none; border: none; color: var(--text-dim); font-size: 1.05rem; line-height: 1; + cursor: pointer; padding: 0.25rem 0.45rem; border-radius: 3px; } + .q-clear:hover { color: var(--select); } + .count { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; color: var(--text-dim); white-space: nowrap; } + .density { display: flex; border: 1px solid var(--border); border-radius: var(--radius); + overflow: hidden; } + .density-btn { background: var(--surface-2); border: none; color: var(--text-dim); cursor: pointer; + font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; padding: 0.36rem 0.7rem; transition: color 0.15s, background 0.15s; } + .density-btn + .density-btn { border-left: 1px solid var(--border); } + .density-btn:hover { color: var(--select); } + .density-btn.active { background: var(--select); color: #1a1b1e; } + .tags-toggle { display: none; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text-dim); border-radius: 3px; padding: 0.3rem 0.7rem; cursor: pointer; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; text-transform: uppercase; } + .tags-toggle:hover, .tags-toggle.has-active { color: var(--select); border-color: var(--select); } + + /* Tag chips wrap by default (no-JS safe). With JS the row becomes a scroll + strip on wide viewports and collapses behind the Tags toggle on narrow + ones, so the sticky bar never eats the mobile viewport. */ + .chips { display: flex; flex-wrap: wrap; gap: 0.4rem; } + html.js .chips { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 0.25rem; + scrollbar-width: thin; scrollbar-color: var(--border) transparent; } + html.js .chips::-webkit-scrollbar { height: 5px; } + html.js .chips::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } .chip { background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); border-radius: 3px; padding: 0.22rem 0.7rem; font-size: 0.72rem; font-weight: 400; - font-family: var(--font-mono); cursor: pointer; transition: color 0.15s, border-color 0.15s; } + font-family: var(--font-mono); cursor: pointer; white-space: nowrap; flex: 0 0 auto; + transition: color 0.15s, border-color 0.15s; } .chip:hover { color: var(--select); border-color: var(--select); } .chip.active { color: #1a1b1e; background: var(--select); border-color: var(--select); } + @media (max-width: 719px) { + html.js .tags-toggle { display: inline-block; } + html.js .chips { display: none; } + html.js .chips.open { display: flex; flex-wrap: wrap; overflow-y: auto; max-height: 40vh; } + } + + /* Compact density: hero + name + one-line teaser. The description and + WITNESSES text stay in the DOM (searchable, screen-reader reachable, + present with JS disabled when the class is never applied) but are + visually collapsed. Applied only by JS via the density-compact class. */ + html.density-compact .grid { gap: 1rem; } + html.density-compact .card-body { padding: 0.65rem 0.9rem 0.7rem; } + html.density-compact .card-body h2 { font-size: 0.88rem; margin-bottom: 0.15rem; } + html.density-compact .teaches { font-size: 0.8rem; color: var(--text-dim); margin-bottom: 0; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + html.density-compact .witnesses { position: absolute; width: 1px; height: 1px; margin: -1px; + padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; } + html.density-compact .card-link { display: none; } + .noresults { color: var(--text-dim); text-align: center; padding: 3rem 1rem; font-size: 0.95rem; } + .noresults .chip { margin-left: 0.6rem; } + .to-top { position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 6; cursor: pointer; + background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); + border-radius: var(--radius); padding: 0.45rem 0.75rem; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; + text-transform: uppercase; opacity: 0; visibility: hidden; + transition: opacity 0.2s, visibility 0.2s, color 0.15s, border-color 0.15s; } + .to-top.show { opacity: 1; visibility: visible; } + .to-top:hover { color: var(--select); border-color: var(--select); } main { max-width: var(--maxw); margin: 0 auto; padding: 1rem 1.25rem 2rem; } .grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; align-items: stretch; } @@ -153,10 +221,18 @@ footer .statusbar code { font-family: inherit; text-transform: none; } @media (min-width: 720px) { .grid { grid-template-columns: 1fr 1fr; gap: 1.75rem; } } + @media (min-width: 560px) { html.density-compact .grid { grid-template-columns: 1fr 1fr; gap: 1rem; } } + @media (min-width: 900px) { html.density-compact .grid { grid-template-columns: repeat(3, 1fr); } } + @media (min-width: 1560px) { + html.density-compact .controls-inner, + html.density-compact main { max-width: 1400px; } + html.density-compact .grid { grid-template-columns: repeat(4, 1fr); } + } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } .card { transition: none; } .card:hover { transform: none; } + .to-top { transition: none; } } diff --git a/docs/gallery/damped-track-aim/index.html b/docs/gallery/damped-track-aim/index.html index aa8e1e5..6f5ec41 100644 --- a/docs/gallery/damped-track-aim/index.html +++ b/docs/gallery/damped-track-aim/index.html @@ -68,14 +68,82 @@ font-size: clamp(2.2rem, 5vw, 3.4rem); letter-spacing: 0.005em; line-height: 0.98; } header.hero p { color: var(--text-dim); max-width: 62ch; margin-top: 0.7rem; font-size: 1rem; } - /* ---- index: filter chips ---- */ - .chips { max-width: var(--maxw); margin: 0 auto; padding: 0 1.25rem 0.25rem; - display: flex; flex-wrap: wrap; gap: 0.5rem; } + /* ---- index: sticky controls (search, density toggle, tag chips) ---- */ + .controls { position: sticky; top: 46px; z-index: 4; + background: color-mix(in srgb, var(--bg) 94%, transparent); + backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); + border-bottom: 1px solid var(--border); } + .controls-inner { max-width: var(--maxw); margin: 0 auto; padding: 0.55rem 1.25rem 0.6rem; + display: flex; flex-direction: column; gap: 0.5rem; } + .controls-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; } + .searchwrap { position: relative; flex: 1 1 240px; min-width: 150px; } + .searchwrap input { width: 100%; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text); border-radius: var(--radius); padding: 0.34rem 1.9rem 0.34rem 0.7rem; + font-family: var(--font-sans); font-size: 0.85rem; line-height: 1.4; } + .searchwrap input:focus { border-color: var(--select); outline: none; } + .searchwrap input::placeholder { color: var(--text-dim); } + .searchwrap input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; } + .q-clear { position: absolute; right: 0.2rem; top: 50%; transform: translateY(-50%); + background: none; border: none; color: var(--text-dim); font-size: 1.05rem; line-height: 1; + cursor: pointer; padding: 0.25rem 0.45rem; border-radius: 3px; } + .q-clear:hover { color: var(--select); } + .count { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; color: var(--text-dim); white-space: nowrap; } + .density { display: flex; border: 1px solid var(--border); border-radius: var(--radius); + overflow: hidden; } + .density-btn { background: var(--surface-2); border: none; color: var(--text-dim); cursor: pointer; + font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; padding: 0.36rem 0.7rem; transition: color 0.15s, background 0.15s; } + .density-btn + .density-btn { border-left: 1px solid var(--border); } + .density-btn:hover { color: var(--select); } + .density-btn.active { background: var(--select); color: #1a1b1e; } + .tags-toggle { display: none; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text-dim); border-radius: 3px; padding: 0.3rem 0.7rem; cursor: pointer; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; text-transform: uppercase; } + .tags-toggle:hover, .tags-toggle.has-active { color: var(--select); border-color: var(--select); } + + /* Tag chips wrap by default (no-JS safe). With JS the row becomes a scroll + strip on wide viewports and collapses behind the Tags toggle on narrow + ones, so the sticky bar never eats the mobile viewport. */ + .chips { display: flex; flex-wrap: wrap; gap: 0.4rem; } + html.js .chips { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 0.25rem; + scrollbar-width: thin; scrollbar-color: var(--border) transparent; } + html.js .chips::-webkit-scrollbar { height: 5px; } + html.js .chips::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } .chip { background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); border-radius: 3px; padding: 0.22rem 0.7rem; font-size: 0.72rem; font-weight: 400; - font-family: var(--font-mono); cursor: pointer; transition: color 0.15s, border-color 0.15s; } + font-family: var(--font-mono); cursor: pointer; white-space: nowrap; flex: 0 0 auto; + transition: color 0.15s, border-color 0.15s; } .chip:hover { color: var(--select); border-color: var(--select); } .chip.active { color: #1a1b1e; background: var(--select); border-color: var(--select); } + @media (max-width: 719px) { + html.js .tags-toggle { display: inline-block; } + html.js .chips { display: none; } + html.js .chips.open { display: flex; flex-wrap: wrap; overflow-y: auto; max-height: 40vh; } + } + + /* Compact density: hero + name + one-line teaser. The description and + WITNESSES text stay in the DOM (searchable, screen-reader reachable, + present with JS disabled when the class is never applied) but are + visually collapsed. Applied only by JS via the density-compact class. */ + html.density-compact .grid { gap: 1rem; } + html.density-compact .card-body { padding: 0.65rem 0.9rem 0.7rem; } + html.density-compact .card-body h2 { font-size: 0.88rem; margin-bottom: 0.15rem; } + html.density-compact .teaches { font-size: 0.8rem; color: var(--text-dim); margin-bottom: 0; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + html.density-compact .witnesses { position: absolute; width: 1px; height: 1px; margin: -1px; + padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; } + html.density-compact .card-link { display: none; } + .noresults { color: var(--text-dim); text-align: center; padding: 3rem 1rem; font-size: 0.95rem; } + .noresults .chip { margin-left: 0.6rem; } + .to-top { position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 6; cursor: pointer; + background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); + border-radius: var(--radius); padding: 0.45rem 0.75rem; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; + text-transform: uppercase; opacity: 0; visibility: hidden; + transition: opacity 0.2s, visibility 0.2s, color 0.15s, border-color 0.15s; } + .to-top.show { opacity: 1; visibility: visible; } + .to-top:hover { color: var(--select); border-color: var(--select); } main { max-width: var(--maxw); margin: 0 auto; padding: 1rem 1.25rem 2rem; } .grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; align-items: stretch; } @@ -153,10 +221,18 @@ footer .statusbar code { font-family: inherit; text-transform: none; } @media (min-width: 720px) { .grid { grid-template-columns: 1fr 1fr; gap: 1.75rem; } } + @media (min-width: 560px) { html.density-compact .grid { grid-template-columns: 1fr 1fr; gap: 1rem; } } + @media (min-width: 900px) { html.density-compact .grid { grid-template-columns: repeat(3, 1fr); } } + @media (min-width: 1560px) { + html.density-compact .controls-inner, + html.density-compact main { max-width: 1400px; } + html.density-compact .grid { grid-template-columns: repeat(4, 1fr); } + } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } .card { transition: none; } .card:hover { transform: none; } + .to-top { transition: none; } } diff --git a/docs/gallery/degenerate-bevel-weld/index.html b/docs/gallery/degenerate-bevel-weld/index.html index 14d45c6..a6dbacf 100644 --- a/docs/gallery/degenerate-bevel-weld/index.html +++ b/docs/gallery/degenerate-bevel-weld/index.html @@ -68,14 +68,82 @@ font-size: clamp(2.2rem, 5vw, 3.4rem); letter-spacing: 0.005em; line-height: 0.98; } header.hero p { color: var(--text-dim); max-width: 62ch; margin-top: 0.7rem; font-size: 1rem; } - /* ---- index: filter chips ---- */ - .chips { max-width: var(--maxw); margin: 0 auto; padding: 0 1.25rem 0.25rem; - display: flex; flex-wrap: wrap; gap: 0.5rem; } + /* ---- index: sticky controls (search, density toggle, tag chips) ---- */ + .controls { position: sticky; top: 46px; z-index: 4; + background: color-mix(in srgb, var(--bg) 94%, transparent); + backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); + border-bottom: 1px solid var(--border); } + .controls-inner { max-width: var(--maxw); margin: 0 auto; padding: 0.55rem 1.25rem 0.6rem; + display: flex; flex-direction: column; gap: 0.5rem; } + .controls-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; } + .searchwrap { position: relative; flex: 1 1 240px; min-width: 150px; } + .searchwrap input { width: 100%; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text); border-radius: var(--radius); padding: 0.34rem 1.9rem 0.34rem 0.7rem; + font-family: var(--font-sans); font-size: 0.85rem; line-height: 1.4; } + .searchwrap input:focus { border-color: var(--select); outline: none; } + .searchwrap input::placeholder { color: var(--text-dim); } + .searchwrap input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; } + .q-clear { position: absolute; right: 0.2rem; top: 50%; transform: translateY(-50%); + background: none; border: none; color: var(--text-dim); font-size: 1.05rem; line-height: 1; + cursor: pointer; padding: 0.25rem 0.45rem; border-radius: 3px; } + .q-clear:hover { color: var(--select); } + .count { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; color: var(--text-dim); white-space: nowrap; } + .density { display: flex; border: 1px solid var(--border); border-radius: var(--radius); + overflow: hidden; } + .density-btn { background: var(--surface-2); border: none; color: var(--text-dim); cursor: pointer; + font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; padding: 0.36rem 0.7rem; transition: color 0.15s, background 0.15s; } + .density-btn + .density-btn { border-left: 1px solid var(--border); } + .density-btn:hover { color: var(--select); } + .density-btn.active { background: var(--select); color: #1a1b1e; } + .tags-toggle { display: none; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text-dim); border-radius: 3px; padding: 0.3rem 0.7rem; cursor: pointer; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; text-transform: uppercase; } + .tags-toggle:hover, .tags-toggle.has-active { color: var(--select); border-color: var(--select); } + + /* Tag chips wrap by default (no-JS safe). With JS the row becomes a scroll + strip on wide viewports and collapses behind the Tags toggle on narrow + ones, so the sticky bar never eats the mobile viewport. */ + .chips { display: flex; flex-wrap: wrap; gap: 0.4rem; } + html.js .chips { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 0.25rem; + scrollbar-width: thin; scrollbar-color: var(--border) transparent; } + html.js .chips::-webkit-scrollbar { height: 5px; } + html.js .chips::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } .chip { background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); border-radius: 3px; padding: 0.22rem 0.7rem; font-size: 0.72rem; font-weight: 400; - font-family: var(--font-mono); cursor: pointer; transition: color 0.15s, border-color 0.15s; } + font-family: var(--font-mono); cursor: pointer; white-space: nowrap; flex: 0 0 auto; + transition: color 0.15s, border-color 0.15s; } .chip:hover { color: var(--select); border-color: var(--select); } .chip.active { color: #1a1b1e; background: var(--select); border-color: var(--select); } + @media (max-width: 719px) { + html.js .tags-toggle { display: inline-block; } + html.js .chips { display: none; } + html.js .chips.open { display: flex; flex-wrap: wrap; overflow-y: auto; max-height: 40vh; } + } + + /* Compact density: hero + name + one-line teaser. The description and + WITNESSES text stay in the DOM (searchable, screen-reader reachable, + present with JS disabled when the class is never applied) but are + visually collapsed. Applied only by JS via the density-compact class. */ + html.density-compact .grid { gap: 1rem; } + html.density-compact .card-body { padding: 0.65rem 0.9rem 0.7rem; } + html.density-compact .card-body h2 { font-size: 0.88rem; margin-bottom: 0.15rem; } + html.density-compact .teaches { font-size: 0.8rem; color: var(--text-dim); margin-bottom: 0; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + html.density-compact .witnesses { position: absolute; width: 1px; height: 1px; margin: -1px; + padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; } + html.density-compact .card-link { display: none; } + .noresults { color: var(--text-dim); text-align: center; padding: 3rem 1rem; font-size: 0.95rem; } + .noresults .chip { margin-left: 0.6rem; } + .to-top { position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 6; cursor: pointer; + background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); + border-radius: var(--radius); padding: 0.45rem 0.75rem; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; + text-transform: uppercase; opacity: 0; visibility: hidden; + transition: opacity 0.2s, visibility 0.2s, color 0.15s, border-color 0.15s; } + .to-top.show { opacity: 1; visibility: visible; } + .to-top:hover { color: var(--select); border-color: var(--select); } main { max-width: var(--maxw); margin: 0 auto; padding: 1rem 1.25rem 2rem; } .grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; align-items: stretch; } @@ -153,10 +221,18 @@ footer .statusbar code { font-family: inherit; text-transform: none; } @media (min-width: 720px) { .grid { grid-template-columns: 1fr 1fr; gap: 1.75rem; } } + @media (min-width: 560px) { html.density-compact .grid { grid-template-columns: 1fr 1fr; gap: 1rem; } } + @media (min-width: 900px) { html.density-compact .grid { grid-template-columns: repeat(3, 1fr); } } + @media (min-width: 1560px) { + html.density-compact .controls-inner, + html.density-compact main { max-width: 1400px; } + html.density-compact .grid { grid-template-columns: repeat(4, 1fr); } + } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } .card { transition: none; } .card:hover { transform: none; } + .to-top { transition: none; } } diff --git a/docs/gallery/depsgraph-export/index.html b/docs/gallery/depsgraph-export/index.html index 58b1bf8..52830ad 100644 --- a/docs/gallery/depsgraph-export/index.html +++ b/docs/gallery/depsgraph-export/index.html @@ -68,14 +68,82 @@ font-size: clamp(2.2rem, 5vw, 3.4rem); letter-spacing: 0.005em; line-height: 0.98; } header.hero p { color: var(--text-dim); max-width: 62ch; margin-top: 0.7rem; font-size: 1rem; } - /* ---- index: filter chips ---- */ - .chips { max-width: var(--maxw); margin: 0 auto; padding: 0 1.25rem 0.25rem; - display: flex; flex-wrap: wrap; gap: 0.5rem; } + /* ---- index: sticky controls (search, density toggle, tag chips) ---- */ + .controls { position: sticky; top: 46px; z-index: 4; + background: color-mix(in srgb, var(--bg) 94%, transparent); + backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); + border-bottom: 1px solid var(--border); } + .controls-inner { max-width: var(--maxw); margin: 0 auto; padding: 0.55rem 1.25rem 0.6rem; + display: flex; flex-direction: column; gap: 0.5rem; } + .controls-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; } + .searchwrap { position: relative; flex: 1 1 240px; min-width: 150px; } + .searchwrap input { width: 100%; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text); border-radius: var(--radius); padding: 0.34rem 1.9rem 0.34rem 0.7rem; + font-family: var(--font-sans); font-size: 0.85rem; line-height: 1.4; } + .searchwrap input:focus { border-color: var(--select); outline: none; } + .searchwrap input::placeholder { color: var(--text-dim); } + .searchwrap input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; } + .q-clear { position: absolute; right: 0.2rem; top: 50%; transform: translateY(-50%); + background: none; border: none; color: var(--text-dim); font-size: 1.05rem; line-height: 1; + cursor: pointer; padding: 0.25rem 0.45rem; border-radius: 3px; } + .q-clear:hover { color: var(--select); } + .count { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; color: var(--text-dim); white-space: nowrap; } + .density { display: flex; border: 1px solid var(--border); border-radius: var(--radius); + overflow: hidden; } + .density-btn { background: var(--surface-2); border: none; color: var(--text-dim); cursor: pointer; + font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; padding: 0.36rem 0.7rem; transition: color 0.15s, background 0.15s; } + .density-btn + .density-btn { border-left: 1px solid var(--border); } + .density-btn:hover { color: var(--select); } + .density-btn.active { background: var(--select); color: #1a1b1e; } + .tags-toggle { display: none; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text-dim); border-radius: 3px; padding: 0.3rem 0.7rem; cursor: pointer; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; text-transform: uppercase; } + .tags-toggle:hover, .tags-toggle.has-active { color: var(--select); border-color: var(--select); } + + /* Tag chips wrap by default (no-JS safe). With JS the row becomes a scroll + strip on wide viewports and collapses behind the Tags toggle on narrow + ones, so the sticky bar never eats the mobile viewport. */ + .chips { display: flex; flex-wrap: wrap; gap: 0.4rem; } + html.js .chips { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 0.25rem; + scrollbar-width: thin; scrollbar-color: var(--border) transparent; } + html.js .chips::-webkit-scrollbar { height: 5px; } + html.js .chips::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } .chip { background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); border-radius: 3px; padding: 0.22rem 0.7rem; font-size: 0.72rem; font-weight: 400; - font-family: var(--font-mono); cursor: pointer; transition: color 0.15s, border-color 0.15s; } + font-family: var(--font-mono); cursor: pointer; white-space: nowrap; flex: 0 0 auto; + transition: color 0.15s, border-color 0.15s; } .chip:hover { color: var(--select); border-color: var(--select); } .chip.active { color: #1a1b1e; background: var(--select); border-color: var(--select); } + @media (max-width: 719px) { + html.js .tags-toggle { display: inline-block; } + html.js .chips { display: none; } + html.js .chips.open { display: flex; flex-wrap: wrap; overflow-y: auto; max-height: 40vh; } + } + + /* Compact density: hero + name + one-line teaser. The description and + WITNESSES text stay in the DOM (searchable, screen-reader reachable, + present with JS disabled when the class is never applied) but are + visually collapsed. Applied only by JS via the density-compact class. */ + html.density-compact .grid { gap: 1rem; } + html.density-compact .card-body { padding: 0.65rem 0.9rem 0.7rem; } + html.density-compact .card-body h2 { font-size: 0.88rem; margin-bottom: 0.15rem; } + html.density-compact .teaches { font-size: 0.8rem; color: var(--text-dim); margin-bottom: 0; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + html.density-compact .witnesses { position: absolute; width: 1px; height: 1px; margin: -1px; + padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; } + html.density-compact .card-link { display: none; } + .noresults { color: var(--text-dim); text-align: center; padding: 3rem 1rem; font-size: 0.95rem; } + .noresults .chip { margin-left: 0.6rem; } + .to-top { position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 6; cursor: pointer; + background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); + border-radius: var(--radius); padding: 0.45rem 0.75rem; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; + text-transform: uppercase; opacity: 0; visibility: hidden; + transition: opacity 0.2s, visibility 0.2s, color 0.15s, border-color 0.15s; } + .to-top.show { opacity: 1; visibility: visible; } + .to-top:hover { color: var(--select); border-color: var(--select); } main { max-width: var(--maxw); margin: 0 auto; padding: 1rem 1.25rem 2rem; } .grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; align-items: stretch; } @@ -153,10 +221,18 @@ footer .statusbar code { font-family: inherit; text-transform: none; } @media (min-width: 720px) { .grid { grid-template-columns: 1fr 1fr; gap: 1.75rem; } } + @media (min-width: 560px) { html.density-compact .grid { grid-template-columns: 1fr 1fr; gap: 1rem; } } + @media (min-width: 900px) { html.density-compact .grid { grid-template-columns: repeat(3, 1fr); } } + @media (min-width: 1560px) { + html.density-compact .controls-inner, + html.density-compact main { max-width: 1400px; } + html.density-compact .grid { grid-template-columns: repeat(4, 1fr); } + } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } .card { transition: none; } .card:hover { transform: none; } + .to-top { transition: none; } } diff --git a/docs/gallery/driver-wave/index.html b/docs/gallery/driver-wave/index.html index 666fd38..12465f4 100644 --- a/docs/gallery/driver-wave/index.html +++ b/docs/gallery/driver-wave/index.html @@ -68,14 +68,82 @@ font-size: clamp(2.2rem, 5vw, 3.4rem); letter-spacing: 0.005em; line-height: 0.98; } header.hero p { color: var(--text-dim); max-width: 62ch; margin-top: 0.7rem; font-size: 1rem; } - /* ---- index: filter chips ---- */ - .chips { max-width: var(--maxw); margin: 0 auto; padding: 0 1.25rem 0.25rem; - display: flex; flex-wrap: wrap; gap: 0.5rem; } + /* ---- index: sticky controls (search, density toggle, tag chips) ---- */ + .controls { position: sticky; top: 46px; z-index: 4; + background: color-mix(in srgb, var(--bg) 94%, transparent); + backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); + border-bottom: 1px solid var(--border); } + .controls-inner { max-width: var(--maxw); margin: 0 auto; padding: 0.55rem 1.25rem 0.6rem; + display: flex; flex-direction: column; gap: 0.5rem; } + .controls-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; } + .searchwrap { position: relative; flex: 1 1 240px; min-width: 150px; } + .searchwrap input { width: 100%; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text); border-radius: var(--radius); padding: 0.34rem 1.9rem 0.34rem 0.7rem; + font-family: var(--font-sans); font-size: 0.85rem; line-height: 1.4; } + .searchwrap input:focus { border-color: var(--select); outline: none; } + .searchwrap input::placeholder { color: var(--text-dim); } + .searchwrap input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; } + .q-clear { position: absolute; right: 0.2rem; top: 50%; transform: translateY(-50%); + background: none; border: none; color: var(--text-dim); font-size: 1.05rem; line-height: 1; + cursor: pointer; padding: 0.25rem 0.45rem; border-radius: 3px; } + .q-clear:hover { color: var(--select); } + .count { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; color: var(--text-dim); white-space: nowrap; } + .density { display: flex; border: 1px solid var(--border); border-radius: var(--radius); + overflow: hidden; } + .density-btn { background: var(--surface-2); border: none; color: var(--text-dim); cursor: pointer; + font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; padding: 0.36rem 0.7rem; transition: color 0.15s, background 0.15s; } + .density-btn + .density-btn { border-left: 1px solid var(--border); } + .density-btn:hover { color: var(--select); } + .density-btn.active { background: var(--select); color: #1a1b1e; } + .tags-toggle { display: none; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text-dim); border-radius: 3px; padding: 0.3rem 0.7rem; cursor: pointer; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; text-transform: uppercase; } + .tags-toggle:hover, .tags-toggle.has-active { color: var(--select); border-color: var(--select); } + + /* Tag chips wrap by default (no-JS safe). With JS the row becomes a scroll + strip on wide viewports and collapses behind the Tags toggle on narrow + ones, so the sticky bar never eats the mobile viewport. */ + .chips { display: flex; flex-wrap: wrap; gap: 0.4rem; } + html.js .chips { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 0.25rem; + scrollbar-width: thin; scrollbar-color: var(--border) transparent; } + html.js .chips::-webkit-scrollbar { height: 5px; } + html.js .chips::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } .chip { background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); border-radius: 3px; padding: 0.22rem 0.7rem; font-size: 0.72rem; font-weight: 400; - font-family: var(--font-mono); cursor: pointer; transition: color 0.15s, border-color 0.15s; } + font-family: var(--font-mono); cursor: pointer; white-space: nowrap; flex: 0 0 auto; + transition: color 0.15s, border-color 0.15s; } .chip:hover { color: var(--select); border-color: var(--select); } .chip.active { color: #1a1b1e; background: var(--select); border-color: var(--select); } + @media (max-width: 719px) { + html.js .tags-toggle { display: inline-block; } + html.js .chips { display: none; } + html.js .chips.open { display: flex; flex-wrap: wrap; overflow-y: auto; max-height: 40vh; } + } + + /* Compact density: hero + name + one-line teaser. The description and + WITNESSES text stay in the DOM (searchable, screen-reader reachable, + present with JS disabled when the class is never applied) but are + visually collapsed. Applied only by JS via the density-compact class. */ + html.density-compact .grid { gap: 1rem; } + html.density-compact .card-body { padding: 0.65rem 0.9rem 0.7rem; } + html.density-compact .card-body h2 { font-size: 0.88rem; margin-bottom: 0.15rem; } + html.density-compact .teaches { font-size: 0.8rem; color: var(--text-dim); margin-bottom: 0; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + html.density-compact .witnesses { position: absolute; width: 1px; height: 1px; margin: -1px; + padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; } + html.density-compact .card-link { display: none; } + .noresults { color: var(--text-dim); text-align: center; padding: 3rem 1rem; font-size: 0.95rem; } + .noresults .chip { margin-left: 0.6rem; } + .to-top { position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 6; cursor: pointer; + background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); + border-radius: var(--radius); padding: 0.45rem 0.75rem; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; + text-transform: uppercase; opacity: 0; visibility: hidden; + transition: opacity 0.2s, visibility 0.2s, color 0.15s, border-color 0.15s; } + .to-top.show { opacity: 1; visibility: visible; } + .to-top:hover { color: var(--select); border-color: var(--select); } main { max-width: var(--maxw); margin: 0 auto; padding: 1rem 1.25rem 2rem; } .grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; align-items: stretch; } @@ -153,10 +221,18 @@ footer .statusbar code { font-family: inherit; text-transform: none; } @media (min-width: 720px) { .grid { grid-template-columns: 1fr 1fr; gap: 1.75rem; } } + @media (min-width: 560px) { html.density-compact .grid { grid-template-columns: 1fr 1fr; gap: 1rem; } } + @media (min-width: 900px) { html.density-compact .grid { grid-template-columns: repeat(3, 1fr); } } + @media (min-width: 1560px) { + html.density-compact .controls-inner, + html.density-compact main { max-width: 1400px; } + html.density-compact .grid { grid-template-columns: repeat(4, 1fr); } + } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } .card { transition: none; } .card:hover { transform: none; } + .to-top { transition: none; } } diff --git a/docs/gallery/gltf-export-roundtrip/index.html b/docs/gallery/gltf-export-roundtrip/index.html index 409e3ed..0c1b860 100644 --- a/docs/gallery/gltf-export-roundtrip/index.html +++ b/docs/gallery/gltf-export-roundtrip/index.html @@ -68,14 +68,82 @@ font-size: clamp(2.2rem, 5vw, 3.4rem); letter-spacing: 0.005em; line-height: 0.98; } header.hero p { color: var(--text-dim); max-width: 62ch; margin-top: 0.7rem; font-size: 1rem; } - /* ---- index: filter chips ---- */ - .chips { max-width: var(--maxw); margin: 0 auto; padding: 0 1.25rem 0.25rem; - display: flex; flex-wrap: wrap; gap: 0.5rem; } + /* ---- index: sticky controls (search, density toggle, tag chips) ---- */ + .controls { position: sticky; top: 46px; z-index: 4; + background: color-mix(in srgb, var(--bg) 94%, transparent); + backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); + border-bottom: 1px solid var(--border); } + .controls-inner { max-width: var(--maxw); margin: 0 auto; padding: 0.55rem 1.25rem 0.6rem; + display: flex; flex-direction: column; gap: 0.5rem; } + .controls-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; } + .searchwrap { position: relative; flex: 1 1 240px; min-width: 150px; } + .searchwrap input { width: 100%; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text); border-radius: var(--radius); padding: 0.34rem 1.9rem 0.34rem 0.7rem; + font-family: var(--font-sans); font-size: 0.85rem; line-height: 1.4; } + .searchwrap input:focus { border-color: var(--select); outline: none; } + .searchwrap input::placeholder { color: var(--text-dim); } + .searchwrap input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; } + .q-clear { position: absolute; right: 0.2rem; top: 50%; transform: translateY(-50%); + background: none; border: none; color: var(--text-dim); font-size: 1.05rem; line-height: 1; + cursor: pointer; padding: 0.25rem 0.45rem; border-radius: 3px; } + .q-clear:hover { color: var(--select); } + .count { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; color: var(--text-dim); white-space: nowrap; } + .density { display: flex; border: 1px solid var(--border); border-radius: var(--radius); + overflow: hidden; } + .density-btn { background: var(--surface-2); border: none; color: var(--text-dim); cursor: pointer; + font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; padding: 0.36rem 0.7rem; transition: color 0.15s, background 0.15s; } + .density-btn + .density-btn { border-left: 1px solid var(--border); } + .density-btn:hover { color: var(--select); } + .density-btn.active { background: var(--select); color: #1a1b1e; } + .tags-toggle { display: none; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text-dim); border-radius: 3px; padding: 0.3rem 0.7rem; cursor: pointer; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; text-transform: uppercase; } + .tags-toggle:hover, .tags-toggle.has-active { color: var(--select); border-color: var(--select); } + + /* Tag chips wrap by default (no-JS safe). With JS the row becomes a scroll + strip on wide viewports and collapses behind the Tags toggle on narrow + ones, so the sticky bar never eats the mobile viewport. */ + .chips { display: flex; flex-wrap: wrap; gap: 0.4rem; } + html.js .chips { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 0.25rem; + scrollbar-width: thin; scrollbar-color: var(--border) transparent; } + html.js .chips::-webkit-scrollbar { height: 5px; } + html.js .chips::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } .chip { background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); border-radius: 3px; padding: 0.22rem 0.7rem; font-size: 0.72rem; font-weight: 400; - font-family: var(--font-mono); cursor: pointer; transition: color 0.15s, border-color 0.15s; } + font-family: var(--font-mono); cursor: pointer; white-space: nowrap; flex: 0 0 auto; + transition: color 0.15s, border-color 0.15s; } .chip:hover { color: var(--select); border-color: var(--select); } .chip.active { color: #1a1b1e; background: var(--select); border-color: var(--select); } + @media (max-width: 719px) { + html.js .tags-toggle { display: inline-block; } + html.js .chips { display: none; } + html.js .chips.open { display: flex; flex-wrap: wrap; overflow-y: auto; max-height: 40vh; } + } + + /* Compact density: hero + name + one-line teaser. The description and + WITNESSES text stay in the DOM (searchable, screen-reader reachable, + present with JS disabled when the class is never applied) but are + visually collapsed. Applied only by JS via the density-compact class. */ + html.density-compact .grid { gap: 1rem; } + html.density-compact .card-body { padding: 0.65rem 0.9rem 0.7rem; } + html.density-compact .card-body h2 { font-size: 0.88rem; margin-bottom: 0.15rem; } + html.density-compact .teaches { font-size: 0.8rem; color: var(--text-dim); margin-bottom: 0; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + html.density-compact .witnesses { position: absolute; width: 1px; height: 1px; margin: -1px; + padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; } + html.density-compact .card-link { display: none; } + .noresults { color: var(--text-dim); text-align: center; padding: 3rem 1rem; font-size: 0.95rem; } + .noresults .chip { margin-left: 0.6rem; } + .to-top { position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 6; cursor: pointer; + background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); + border-radius: var(--radius); padding: 0.45rem 0.75rem; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; + text-transform: uppercase; opacity: 0; visibility: hidden; + transition: opacity 0.2s, visibility 0.2s, color 0.15s, border-color 0.15s; } + .to-top.show { opacity: 1; visibility: visible; } + .to-top:hover { color: var(--select); border-color: var(--select); } main { max-width: var(--maxw); margin: 0 auto; padding: 1rem 1.25rem 2rem; } .grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; align-items: stretch; } @@ -153,10 +221,18 @@ footer .statusbar code { font-family: inherit; text-transform: none; } @media (min-width: 720px) { .grid { grid-template-columns: 1fr 1fr; gap: 1.75rem; } } + @media (min-width: 560px) { html.density-compact .grid { grid-template-columns: 1fr 1fr; gap: 1rem; } } + @media (min-width: 900px) { html.density-compact .grid { grid-template-columns: repeat(3, 1fr); } } + @media (min-width: 1560px) { + html.density-compact .controls-inner, + html.density-compact main { max-width: 1400px; } + html.density-compact .grid { grid-template-columns: repeat(4, 1fr); } + } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } .card { transition: none; } .card:hover { transform: none; } + .to-top { transition: none; } } diff --git a/docs/gallery/gltf-skin-roundtrip/index.html b/docs/gallery/gltf-skin-roundtrip/index.html index 51296e0..26dd9f0 100644 --- a/docs/gallery/gltf-skin-roundtrip/index.html +++ b/docs/gallery/gltf-skin-roundtrip/index.html @@ -68,14 +68,82 @@ font-size: clamp(2.2rem, 5vw, 3.4rem); letter-spacing: 0.005em; line-height: 0.98; } header.hero p { color: var(--text-dim); max-width: 62ch; margin-top: 0.7rem; font-size: 1rem; } - /* ---- index: filter chips ---- */ - .chips { max-width: var(--maxw); margin: 0 auto; padding: 0 1.25rem 0.25rem; - display: flex; flex-wrap: wrap; gap: 0.5rem; } + /* ---- index: sticky controls (search, density toggle, tag chips) ---- */ + .controls { position: sticky; top: 46px; z-index: 4; + background: color-mix(in srgb, var(--bg) 94%, transparent); + backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); + border-bottom: 1px solid var(--border); } + .controls-inner { max-width: var(--maxw); margin: 0 auto; padding: 0.55rem 1.25rem 0.6rem; + display: flex; flex-direction: column; gap: 0.5rem; } + .controls-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; } + .searchwrap { position: relative; flex: 1 1 240px; min-width: 150px; } + .searchwrap input { width: 100%; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text); border-radius: var(--radius); padding: 0.34rem 1.9rem 0.34rem 0.7rem; + font-family: var(--font-sans); font-size: 0.85rem; line-height: 1.4; } + .searchwrap input:focus { border-color: var(--select); outline: none; } + .searchwrap input::placeholder { color: var(--text-dim); } + .searchwrap input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; } + .q-clear { position: absolute; right: 0.2rem; top: 50%; transform: translateY(-50%); + background: none; border: none; color: var(--text-dim); font-size: 1.05rem; line-height: 1; + cursor: pointer; padding: 0.25rem 0.45rem; border-radius: 3px; } + .q-clear:hover { color: var(--select); } + .count { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; color: var(--text-dim); white-space: nowrap; } + .density { display: flex; border: 1px solid var(--border); border-radius: var(--radius); + overflow: hidden; } + .density-btn { background: var(--surface-2); border: none; color: var(--text-dim); cursor: pointer; + font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; padding: 0.36rem 0.7rem; transition: color 0.15s, background 0.15s; } + .density-btn + .density-btn { border-left: 1px solid var(--border); } + .density-btn:hover { color: var(--select); } + .density-btn.active { background: var(--select); color: #1a1b1e; } + .tags-toggle { display: none; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text-dim); border-radius: 3px; padding: 0.3rem 0.7rem; cursor: pointer; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; text-transform: uppercase; } + .tags-toggle:hover, .tags-toggle.has-active { color: var(--select); border-color: var(--select); } + + /* Tag chips wrap by default (no-JS safe). With JS the row becomes a scroll + strip on wide viewports and collapses behind the Tags toggle on narrow + ones, so the sticky bar never eats the mobile viewport. */ + .chips { display: flex; flex-wrap: wrap; gap: 0.4rem; } + html.js .chips { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 0.25rem; + scrollbar-width: thin; scrollbar-color: var(--border) transparent; } + html.js .chips::-webkit-scrollbar { height: 5px; } + html.js .chips::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } .chip { background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); border-radius: 3px; padding: 0.22rem 0.7rem; font-size: 0.72rem; font-weight: 400; - font-family: var(--font-mono); cursor: pointer; transition: color 0.15s, border-color 0.15s; } + font-family: var(--font-mono); cursor: pointer; white-space: nowrap; flex: 0 0 auto; + transition: color 0.15s, border-color 0.15s; } .chip:hover { color: var(--select); border-color: var(--select); } .chip.active { color: #1a1b1e; background: var(--select); border-color: var(--select); } + @media (max-width: 719px) { + html.js .tags-toggle { display: inline-block; } + html.js .chips { display: none; } + html.js .chips.open { display: flex; flex-wrap: wrap; overflow-y: auto; max-height: 40vh; } + } + + /* Compact density: hero + name + one-line teaser. The description and + WITNESSES text stay in the DOM (searchable, screen-reader reachable, + present with JS disabled when the class is never applied) but are + visually collapsed. Applied only by JS via the density-compact class. */ + html.density-compact .grid { gap: 1rem; } + html.density-compact .card-body { padding: 0.65rem 0.9rem 0.7rem; } + html.density-compact .card-body h2 { font-size: 0.88rem; margin-bottom: 0.15rem; } + html.density-compact .teaches { font-size: 0.8rem; color: var(--text-dim); margin-bottom: 0; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + html.density-compact .witnesses { position: absolute; width: 1px; height: 1px; margin: -1px; + padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; } + html.density-compact .card-link { display: none; } + .noresults { color: var(--text-dim); text-align: center; padding: 3rem 1rem; font-size: 0.95rem; } + .noresults .chip { margin-left: 0.6rem; } + .to-top { position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 6; cursor: pointer; + background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); + border-radius: var(--radius); padding: 0.45rem 0.75rem; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; + text-transform: uppercase; opacity: 0; visibility: hidden; + transition: opacity 0.2s, visibility 0.2s, color 0.15s, border-color 0.15s; } + .to-top.show { opacity: 1; visibility: visible; } + .to-top:hover { color: var(--select); border-color: var(--select); } main { max-width: var(--maxw); margin: 0 auto; padding: 1rem 1.25rem 2rem; } .grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; align-items: stretch; } @@ -153,10 +221,18 @@ footer .statusbar code { font-family: inherit; text-transform: none; } @media (min-width: 720px) { .grid { grid-template-columns: 1fr 1fr; gap: 1.75rem; } } + @media (min-width: 560px) { html.density-compact .grid { grid-template-columns: 1fr 1fr; gap: 1rem; } } + @media (min-width: 900px) { html.density-compact .grid { grid-template-columns: repeat(3, 1fr); } } + @media (min-width: 1560px) { + html.density-compact .controls-inner, + html.density-compact main { max-width: 1400px; } + html.density-compact .grid { grid-template-columns: repeat(4, 1fr); } + } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } .card { transition: none; } .card:hover { transform: none; } + .to-top { transition: none; } } diff --git a/docs/gallery/gn-instance-grid/index.html b/docs/gallery/gn-instance-grid/index.html index a60b19e..4f69e10 100644 --- a/docs/gallery/gn-instance-grid/index.html +++ b/docs/gallery/gn-instance-grid/index.html @@ -68,14 +68,82 @@ font-size: clamp(2.2rem, 5vw, 3.4rem); letter-spacing: 0.005em; line-height: 0.98; } header.hero p { color: var(--text-dim); max-width: 62ch; margin-top: 0.7rem; font-size: 1rem; } - /* ---- index: filter chips ---- */ - .chips { max-width: var(--maxw); margin: 0 auto; padding: 0 1.25rem 0.25rem; - display: flex; flex-wrap: wrap; gap: 0.5rem; } + /* ---- index: sticky controls (search, density toggle, tag chips) ---- */ + .controls { position: sticky; top: 46px; z-index: 4; + background: color-mix(in srgb, var(--bg) 94%, transparent); + backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); + border-bottom: 1px solid var(--border); } + .controls-inner { max-width: var(--maxw); margin: 0 auto; padding: 0.55rem 1.25rem 0.6rem; + display: flex; flex-direction: column; gap: 0.5rem; } + .controls-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; } + .searchwrap { position: relative; flex: 1 1 240px; min-width: 150px; } + .searchwrap input { width: 100%; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text); border-radius: var(--radius); padding: 0.34rem 1.9rem 0.34rem 0.7rem; + font-family: var(--font-sans); font-size: 0.85rem; line-height: 1.4; } + .searchwrap input:focus { border-color: var(--select); outline: none; } + .searchwrap input::placeholder { color: var(--text-dim); } + .searchwrap input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; } + .q-clear { position: absolute; right: 0.2rem; top: 50%; transform: translateY(-50%); + background: none; border: none; color: var(--text-dim); font-size: 1.05rem; line-height: 1; + cursor: pointer; padding: 0.25rem 0.45rem; border-radius: 3px; } + .q-clear:hover { color: var(--select); } + .count { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; color: var(--text-dim); white-space: nowrap; } + .density { display: flex; border: 1px solid var(--border); border-radius: var(--radius); + overflow: hidden; } + .density-btn { background: var(--surface-2); border: none; color: var(--text-dim); cursor: pointer; + font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; padding: 0.36rem 0.7rem; transition: color 0.15s, background 0.15s; } + .density-btn + .density-btn { border-left: 1px solid var(--border); } + .density-btn:hover { color: var(--select); } + .density-btn.active { background: var(--select); color: #1a1b1e; } + .tags-toggle { display: none; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text-dim); border-radius: 3px; padding: 0.3rem 0.7rem; cursor: pointer; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; text-transform: uppercase; } + .tags-toggle:hover, .tags-toggle.has-active { color: var(--select); border-color: var(--select); } + + /* Tag chips wrap by default (no-JS safe). With JS the row becomes a scroll + strip on wide viewports and collapses behind the Tags toggle on narrow + ones, so the sticky bar never eats the mobile viewport. */ + .chips { display: flex; flex-wrap: wrap; gap: 0.4rem; } + html.js .chips { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 0.25rem; + scrollbar-width: thin; scrollbar-color: var(--border) transparent; } + html.js .chips::-webkit-scrollbar { height: 5px; } + html.js .chips::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } .chip { background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); border-radius: 3px; padding: 0.22rem 0.7rem; font-size: 0.72rem; font-weight: 400; - font-family: var(--font-mono); cursor: pointer; transition: color 0.15s, border-color 0.15s; } + font-family: var(--font-mono); cursor: pointer; white-space: nowrap; flex: 0 0 auto; + transition: color 0.15s, border-color 0.15s; } .chip:hover { color: var(--select); border-color: var(--select); } .chip.active { color: #1a1b1e; background: var(--select); border-color: var(--select); } + @media (max-width: 719px) { + html.js .tags-toggle { display: inline-block; } + html.js .chips { display: none; } + html.js .chips.open { display: flex; flex-wrap: wrap; overflow-y: auto; max-height: 40vh; } + } + + /* Compact density: hero + name + one-line teaser. The description and + WITNESSES text stay in the DOM (searchable, screen-reader reachable, + present with JS disabled when the class is never applied) but are + visually collapsed. Applied only by JS via the density-compact class. */ + html.density-compact .grid { gap: 1rem; } + html.density-compact .card-body { padding: 0.65rem 0.9rem 0.7rem; } + html.density-compact .card-body h2 { font-size: 0.88rem; margin-bottom: 0.15rem; } + html.density-compact .teaches { font-size: 0.8rem; color: var(--text-dim); margin-bottom: 0; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + html.density-compact .witnesses { position: absolute; width: 1px; height: 1px; margin: -1px; + padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; } + html.density-compact .card-link { display: none; } + .noresults { color: var(--text-dim); text-align: center; padding: 3rem 1rem; font-size: 0.95rem; } + .noresults .chip { margin-left: 0.6rem; } + .to-top { position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 6; cursor: pointer; + background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); + border-radius: var(--radius); padding: 0.45rem 0.75rem; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; + text-transform: uppercase; opacity: 0; visibility: hidden; + transition: opacity 0.2s, visibility 0.2s, color 0.15s, border-color 0.15s; } + .to-top.show { opacity: 1; visibility: visible; } + .to-top:hover { color: var(--select); border-color: var(--select); } main { max-width: var(--maxw); margin: 0 auto; padding: 1rem 1.25rem 2rem; } .grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; align-items: stretch; } @@ -153,10 +221,18 @@ footer .statusbar code { font-family: inherit; text-transform: none; } @media (min-width: 720px) { .grid { grid-template-columns: 1fr 1fr; gap: 1.75rem; } } + @media (min-width: 560px) { html.density-compact .grid { grid-template-columns: 1fr 1fr; gap: 1rem; } } + @media (min-width: 900px) { html.density-compact .grid { grid-template-columns: repeat(3, 1fr); } } + @media (min-width: 1560px) { + html.density-compact .controls-inner, + html.density-compact main { max-width: 1400px; } + html.density-compact .grid { grid-template-columns: repeat(4, 1fr); } + } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } .card { transition: none; } .card:hover { transform: none; } + .to-top { transition: none; } } diff --git a/docs/gallery/gn-sdf-remesh/index.html b/docs/gallery/gn-sdf-remesh/index.html index 7f853ec..18bad45 100644 --- a/docs/gallery/gn-sdf-remesh/index.html +++ b/docs/gallery/gn-sdf-remesh/index.html @@ -68,14 +68,82 @@ font-size: clamp(2.2rem, 5vw, 3.4rem); letter-spacing: 0.005em; line-height: 0.98; } header.hero p { color: var(--text-dim); max-width: 62ch; margin-top: 0.7rem; font-size: 1rem; } - /* ---- index: filter chips ---- */ - .chips { max-width: var(--maxw); margin: 0 auto; padding: 0 1.25rem 0.25rem; - display: flex; flex-wrap: wrap; gap: 0.5rem; } + /* ---- index: sticky controls (search, density toggle, tag chips) ---- */ + .controls { position: sticky; top: 46px; z-index: 4; + background: color-mix(in srgb, var(--bg) 94%, transparent); + backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); + border-bottom: 1px solid var(--border); } + .controls-inner { max-width: var(--maxw); margin: 0 auto; padding: 0.55rem 1.25rem 0.6rem; + display: flex; flex-direction: column; gap: 0.5rem; } + .controls-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; } + .searchwrap { position: relative; flex: 1 1 240px; min-width: 150px; } + .searchwrap input { width: 100%; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text); border-radius: var(--radius); padding: 0.34rem 1.9rem 0.34rem 0.7rem; + font-family: var(--font-sans); font-size: 0.85rem; line-height: 1.4; } + .searchwrap input:focus { border-color: var(--select); outline: none; } + .searchwrap input::placeholder { color: var(--text-dim); } + .searchwrap input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; } + .q-clear { position: absolute; right: 0.2rem; top: 50%; transform: translateY(-50%); + background: none; border: none; color: var(--text-dim); font-size: 1.05rem; line-height: 1; + cursor: pointer; padding: 0.25rem 0.45rem; border-radius: 3px; } + .q-clear:hover { color: var(--select); } + .count { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; color: var(--text-dim); white-space: nowrap; } + .density { display: flex; border: 1px solid var(--border); border-radius: var(--radius); + overflow: hidden; } + .density-btn { background: var(--surface-2); border: none; color: var(--text-dim); cursor: pointer; + font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; padding: 0.36rem 0.7rem; transition: color 0.15s, background 0.15s; } + .density-btn + .density-btn { border-left: 1px solid var(--border); } + .density-btn:hover { color: var(--select); } + .density-btn.active { background: var(--select); color: #1a1b1e; } + .tags-toggle { display: none; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text-dim); border-radius: 3px; padding: 0.3rem 0.7rem; cursor: pointer; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; text-transform: uppercase; } + .tags-toggle:hover, .tags-toggle.has-active { color: var(--select); border-color: var(--select); } + + /* Tag chips wrap by default (no-JS safe). With JS the row becomes a scroll + strip on wide viewports and collapses behind the Tags toggle on narrow + ones, so the sticky bar never eats the mobile viewport. */ + .chips { display: flex; flex-wrap: wrap; gap: 0.4rem; } + html.js .chips { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 0.25rem; + scrollbar-width: thin; scrollbar-color: var(--border) transparent; } + html.js .chips::-webkit-scrollbar { height: 5px; } + html.js .chips::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } .chip { background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); border-radius: 3px; padding: 0.22rem 0.7rem; font-size: 0.72rem; font-weight: 400; - font-family: var(--font-mono); cursor: pointer; transition: color 0.15s, border-color 0.15s; } + font-family: var(--font-mono); cursor: pointer; white-space: nowrap; flex: 0 0 auto; + transition: color 0.15s, border-color 0.15s; } .chip:hover { color: var(--select); border-color: var(--select); } .chip.active { color: #1a1b1e; background: var(--select); border-color: var(--select); } + @media (max-width: 719px) { + html.js .tags-toggle { display: inline-block; } + html.js .chips { display: none; } + html.js .chips.open { display: flex; flex-wrap: wrap; overflow-y: auto; max-height: 40vh; } + } + + /* Compact density: hero + name + one-line teaser. The description and + WITNESSES text stay in the DOM (searchable, screen-reader reachable, + present with JS disabled when the class is never applied) but are + visually collapsed. Applied only by JS via the density-compact class. */ + html.density-compact .grid { gap: 1rem; } + html.density-compact .card-body { padding: 0.65rem 0.9rem 0.7rem; } + html.density-compact .card-body h2 { font-size: 0.88rem; margin-bottom: 0.15rem; } + html.density-compact .teaches { font-size: 0.8rem; color: var(--text-dim); margin-bottom: 0; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + html.density-compact .witnesses { position: absolute; width: 1px; height: 1px; margin: -1px; + padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; } + html.density-compact .card-link { display: none; } + .noresults { color: var(--text-dim); text-align: center; padding: 3rem 1rem; font-size: 0.95rem; } + .noresults .chip { margin-left: 0.6rem; } + .to-top { position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 6; cursor: pointer; + background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); + border-radius: var(--radius); padding: 0.45rem 0.75rem; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; + text-transform: uppercase; opacity: 0; visibility: hidden; + transition: opacity 0.2s, visibility 0.2s, color 0.15s, border-color 0.15s; } + .to-top.show { opacity: 1; visibility: visible; } + .to-top:hover { color: var(--select); border-color: var(--select); } main { max-width: var(--maxw); margin: 0 auto; padding: 1rem 1.25rem 2rem; } .grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; align-items: stretch; } @@ -153,10 +221,18 @@ footer .statusbar code { font-family: inherit; text-transform: none; } @media (min-width: 720px) { .grid { grid-template-columns: 1fr 1fr; gap: 1.75rem; } } + @media (min-width: 560px) { html.density-compact .grid { grid-template-columns: 1fr 1fr; gap: 1rem; } } + @media (min-width: 900px) { html.density-compact .grid { grid-template-columns: repeat(3, 1fr); } } + @media (min-width: 1560px) { + html.density-compact .controls-inner, + html.density-compact main { max-width: 1400px; } + html.density-compact .grid { grid-template-columns: repeat(4, 1fr); } + } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } .card { transition: none; } .card:hover { transform: none; } + .to-top { transition: none; } } diff --git a/docs/gallery/gp-lineart-contour/index.html b/docs/gallery/gp-lineart-contour/index.html index cb1f03e..3f351d1 100644 --- a/docs/gallery/gp-lineart-contour/index.html +++ b/docs/gallery/gp-lineart-contour/index.html @@ -68,14 +68,82 @@ font-size: clamp(2.2rem, 5vw, 3.4rem); letter-spacing: 0.005em; line-height: 0.98; } header.hero p { color: var(--text-dim); max-width: 62ch; margin-top: 0.7rem; font-size: 1rem; } - /* ---- index: filter chips ---- */ - .chips { max-width: var(--maxw); margin: 0 auto; padding: 0 1.25rem 0.25rem; - display: flex; flex-wrap: wrap; gap: 0.5rem; } + /* ---- index: sticky controls (search, density toggle, tag chips) ---- */ + .controls { position: sticky; top: 46px; z-index: 4; + background: color-mix(in srgb, var(--bg) 94%, transparent); + backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); + border-bottom: 1px solid var(--border); } + .controls-inner { max-width: var(--maxw); margin: 0 auto; padding: 0.55rem 1.25rem 0.6rem; + display: flex; flex-direction: column; gap: 0.5rem; } + .controls-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; } + .searchwrap { position: relative; flex: 1 1 240px; min-width: 150px; } + .searchwrap input { width: 100%; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text); border-radius: var(--radius); padding: 0.34rem 1.9rem 0.34rem 0.7rem; + font-family: var(--font-sans); font-size: 0.85rem; line-height: 1.4; } + .searchwrap input:focus { border-color: var(--select); outline: none; } + .searchwrap input::placeholder { color: var(--text-dim); } + .searchwrap input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; } + .q-clear { position: absolute; right: 0.2rem; top: 50%; transform: translateY(-50%); + background: none; border: none; color: var(--text-dim); font-size: 1.05rem; line-height: 1; + cursor: pointer; padding: 0.25rem 0.45rem; border-radius: 3px; } + .q-clear:hover { color: var(--select); } + .count { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; color: var(--text-dim); white-space: nowrap; } + .density { display: flex; border: 1px solid var(--border); border-radius: var(--radius); + overflow: hidden; } + .density-btn { background: var(--surface-2); border: none; color: var(--text-dim); cursor: pointer; + font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; padding: 0.36rem 0.7rem; transition: color 0.15s, background 0.15s; } + .density-btn + .density-btn { border-left: 1px solid var(--border); } + .density-btn:hover { color: var(--select); } + .density-btn.active { background: var(--select); color: #1a1b1e; } + .tags-toggle { display: none; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text-dim); border-radius: 3px; padding: 0.3rem 0.7rem; cursor: pointer; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; text-transform: uppercase; } + .tags-toggle:hover, .tags-toggle.has-active { color: var(--select); border-color: var(--select); } + + /* Tag chips wrap by default (no-JS safe). With JS the row becomes a scroll + strip on wide viewports and collapses behind the Tags toggle on narrow + ones, so the sticky bar never eats the mobile viewport. */ + .chips { display: flex; flex-wrap: wrap; gap: 0.4rem; } + html.js .chips { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 0.25rem; + scrollbar-width: thin; scrollbar-color: var(--border) transparent; } + html.js .chips::-webkit-scrollbar { height: 5px; } + html.js .chips::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } .chip { background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); border-radius: 3px; padding: 0.22rem 0.7rem; font-size: 0.72rem; font-weight: 400; - font-family: var(--font-mono); cursor: pointer; transition: color 0.15s, border-color 0.15s; } + font-family: var(--font-mono); cursor: pointer; white-space: nowrap; flex: 0 0 auto; + transition: color 0.15s, border-color 0.15s; } .chip:hover { color: var(--select); border-color: var(--select); } .chip.active { color: #1a1b1e; background: var(--select); border-color: var(--select); } + @media (max-width: 719px) { + html.js .tags-toggle { display: inline-block; } + html.js .chips { display: none; } + html.js .chips.open { display: flex; flex-wrap: wrap; overflow-y: auto; max-height: 40vh; } + } + + /* Compact density: hero + name + one-line teaser. The description and + WITNESSES text stay in the DOM (searchable, screen-reader reachable, + present with JS disabled when the class is never applied) but are + visually collapsed. Applied only by JS via the density-compact class. */ + html.density-compact .grid { gap: 1rem; } + html.density-compact .card-body { padding: 0.65rem 0.9rem 0.7rem; } + html.density-compact .card-body h2 { font-size: 0.88rem; margin-bottom: 0.15rem; } + html.density-compact .teaches { font-size: 0.8rem; color: var(--text-dim); margin-bottom: 0; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + html.density-compact .witnesses { position: absolute; width: 1px; height: 1px; margin: -1px; + padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; } + html.density-compact .card-link { display: none; } + .noresults { color: var(--text-dim); text-align: center; padding: 3rem 1rem; font-size: 0.95rem; } + .noresults .chip { margin-left: 0.6rem; } + .to-top { position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 6; cursor: pointer; + background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); + border-radius: var(--radius); padding: 0.45rem 0.75rem; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; + text-transform: uppercase; opacity: 0; visibility: hidden; + transition: opacity 0.2s, visibility 0.2s, color 0.15s, border-color 0.15s; } + .to-top.show { opacity: 1; visibility: visible; } + .to-top:hover { color: var(--select); border-color: var(--select); } main { max-width: var(--maxw); margin: 0 auto; padding: 1rem 1.25rem 2rem; } .grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; align-items: stretch; } @@ -153,10 +221,18 @@ footer .statusbar code { font-family: inherit; text-transform: none; } @media (min-width: 720px) { .grid { grid-template-columns: 1fr 1fr; gap: 1.75rem; } } + @media (min-width: 560px) { html.density-compact .grid { grid-template-columns: 1fr 1fr; gap: 1rem; } } + @media (min-width: 900px) { html.density-compact .grid { grid-template-columns: repeat(3, 1fr); } } + @media (min-width: 1560px) { + html.density-compact .controls-inner, + html.density-compact main { max-width: 1400px; } + html.density-compact .grid { grid-template-columns: repeat(4, 1fr); } + } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } .card { transition: none; } .card:hover { transform: none; } + .to-top { transition: none; } } diff --git a/docs/gallery/grease-pencil-rosette/index.html b/docs/gallery/grease-pencil-rosette/index.html index ef5eba9..9450d65 100644 --- a/docs/gallery/grease-pencil-rosette/index.html +++ b/docs/gallery/grease-pencil-rosette/index.html @@ -68,14 +68,82 @@ font-size: clamp(2.2rem, 5vw, 3.4rem); letter-spacing: 0.005em; line-height: 0.98; } header.hero p { color: var(--text-dim); max-width: 62ch; margin-top: 0.7rem; font-size: 1rem; } - /* ---- index: filter chips ---- */ - .chips { max-width: var(--maxw); margin: 0 auto; padding: 0 1.25rem 0.25rem; - display: flex; flex-wrap: wrap; gap: 0.5rem; } + /* ---- index: sticky controls (search, density toggle, tag chips) ---- */ + .controls { position: sticky; top: 46px; z-index: 4; + background: color-mix(in srgb, var(--bg) 94%, transparent); + backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); + border-bottom: 1px solid var(--border); } + .controls-inner { max-width: var(--maxw); margin: 0 auto; padding: 0.55rem 1.25rem 0.6rem; + display: flex; flex-direction: column; gap: 0.5rem; } + .controls-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; } + .searchwrap { position: relative; flex: 1 1 240px; min-width: 150px; } + .searchwrap input { width: 100%; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text); border-radius: var(--radius); padding: 0.34rem 1.9rem 0.34rem 0.7rem; + font-family: var(--font-sans); font-size: 0.85rem; line-height: 1.4; } + .searchwrap input:focus { border-color: var(--select); outline: none; } + .searchwrap input::placeholder { color: var(--text-dim); } + .searchwrap input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; } + .q-clear { position: absolute; right: 0.2rem; top: 50%; transform: translateY(-50%); + background: none; border: none; color: var(--text-dim); font-size: 1.05rem; line-height: 1; + cursor: pointer; padding: 0.25rem 0.45rem; border-radius: 3px; } + .q-clear:hover { color: var(--select); } + .count { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; color: var(--text-dim); white-space: nowrap; } + .density { display: flex; border: 1px solid var(--border); border-radius: var(--radius); + overflow: hidden; } + .density-btn { background: var(--surface-2); border: none; color: var(--text-dim); cursor: pointer; + font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; padding: 0.36rem 0.7rem; transition: color 0.15s, background 0.15s; } + .density-btn + .density-btn { border-left: 1px solid var(--border); } + .density-btn:hover { color: var(--select); } + .density-btn.active { background: var(--select); color: #1a1b1e; } + .tags-toggle { display: none; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text-dim); border-radius: 3px; padding: 0.3rem 0.7rem; cursor: pointer; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; text-transform: uppercase; } + .tags-toggle:hover, .tags-toggle.has-active { color: var(--select); border-color: var(--select); } + + /* Tag chips wrap by default (no-JS safe). With JS the row becomes a scroll + strip on wide viewports and collapses behind the Tags toggle on narrow + ones, so the sticky bar never eats the mobile viewport. */ + .chips { display: flex; flex-wrap: wrap; gap: 0.4rem; } + html.js .chips { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 0.25rem; + scrollbar-width: thin; scrollbar-color: var(--border) transparent; } + html.js .chips::-webkit-scrollbar { height: 5px; } + html.js .chips::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } .chip { background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); border-radius: 3px; padding: 0.22rem 0.7rem; font-size: 0.72rem; font-weight: 400; - font-family: var(--font-mono); cursor: pointer; transition: color 0.15s, border-color 0.15s; } + font-family: var(--font-mono); cursor: pointer; white-space: nowrap; flex: 0 0 auto; + transition: color 0.15s, border-color 0.15s; } .chip:hover { color: var(--select); border-color: var(--select); } .chip.active { color: #1a1b1e; background: var(--select); border-color: var(--select); } + @media (max-width: 719px) { + html.js .tags-toggle { display: inline-block; } + html.js .chips { display: none; } + html.js .chips.open { display: flex; flex-wrap: wrap; overflow-y: auto; max-height: 40vh; } + } + + /* Compact density: hero + name + one-line teaser. The description and + WITNESSES text stay in the DOM (searchable, screen-reader reachable, + present with JS disabled when the class is never applied) but are + visually collapsed. Applied only by JS via the density-compact class. */ + html.density-compact .grid { gap: 1rem; } + html.density-compact .card-body { padding: 0.65rem 0.9rem 0.7rem; } + html.density-compact .card-body h2 { font-size: 0.88rem; margin-bottom: 0.15rem; } + html.density-compact .teaches { font-size: 0.8rem; color: var(--text-dim); margin-bottom: 0; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + html.density-compact .witnesses { position: absolute; width: 1px; height: 1px; margin: -1px; + padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; } + html.density-compact .card-link { display: none; } + .noresults { color: var(--text-dim); text-align: center; padding: 3rem 1rem; font-size: 0.95rem; } + .noresults .chip { margin-left: 0.6rem; } + .to-top { position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 6; cursor: pointer; + background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); + border-radius: var(--radius); padding: 0.45rem 0.75rem; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; + text-transform: uppercase; opacity: 0; visibility: hidden; + transition: opacity 0.2s, visibility 0.2s, color 0.15s, border-color 0.15s; } + .to-top.show { opacity: 1; visibility: visible; } + .to-top:hover { color: var(--select); border-color: var(--select); } main { max-width: var(--maxw); margin: 0 auto; padding: 1rem 1.25rem 2rem; } .grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; align-items: stretch; } @@ -153,10 +221,18 @@ footer .statusbar code { font-family: inherit; text-transform: none; } @media (min-width: 720px) { .grid { grid-template-columns: 1fr 1fr; gap: 1.75rem; } } + @media (min-width: 560px) { html.density-compact .grid { grid-template-columns: 1fr 1fr; gap: 1rem; } } + @media (min-width: 900px) { html.density-compact .grid { grid-template-columns: repeat(3, 1fr); } } + @media (min-width: 1560px) { + html.density-compact .controls-inner, + html.density-compact main { max-width: 1400px; } + html.density-compact .grid { grid-template-columns: repeat(4, 1fr); } + } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } .card { transition: none; } .card:hover { transform: none; } + .to-top { transition: none; } } diff --git a/docs/gallery/image-pixels-testcard/index.html b/docs/gallery/image-pixels-testcard/index.html index 05c1f80..c220454 100644 --- a/docs/gallery/image-pixels-testcard/index.html +++ b/docs/gallery/image-pixels-testcard/index.html @@ -68,14 +68,82 @@ font-size: clamp(2.2rem, 5vw, 3.4rem); letter-spacing: 0.005em; line-height: 0.98; } header.hero p { color: var(--text-dim); max-width: 62ch; margin-top: 0.7rem; font-size: 1rem; } - /* ---- index: filter chips ---- */ - .chips { max-width: var(--maxw); margin: 0 auto; padding: 0 1.25rem 0.25rem; - display: flex; flex-wrap: wrap; gap: 0.5rem; } + /* ---- index: sticky controls (search, density toggle, tag chips) ---- */ + .controls { position: sticky; top: 46px; z-index: 4; + background: color-mix(in srgb, var(--bg) 94%, transparent); + backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); + border-bottom: 1px solid var(--border); } + .controls-inner { max-width: var(--maxw); margin: 0 auto; padding: 0.55rem 1.25rem 0.6rem; + display: flex; flex-direction: column; gap: 0.5rem; } + .controls-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; } + .searchwrap { position: relative; flex: 1 1 240px; min-width: 150px; } + .searchwrap input { width: 100%; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text); border-radius: var(--radius); padding: 0.34rem 1.9rem 0.34rem 0.7rem; + font-family: var(--font-sans); font-size: 0.85rem; line-height: 1.4; } + .searchwrap input:focus { border-color: var(--select); outline: none; } + .searchwrap input::placeholder { color: var(--text-dim); } + .searchwrap input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; } + .q-clear { position: absolute; right: 0.2rem; top: 50%; transform: translateY(-50%); + background: none; border: none; color: var(--text-dim); font-size: 1.05rem; line-height: 1; + cursor: pointer; padding: 0.25rem 0.45rem; border-radius: 3px; } + .q-clear:hover { color: var(--select); } + .count { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; color: var(--text-dim); white-space: nowrap; } + .density { display: flex; border: 1px solid var(--border); border-radius: var(--radius); + overflow: hidden; } + .density-btn { background: var(--surface-2); border: none; color: var(--text-dim); cursor: pointer; + font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; padding: 0.36rem 0.7rem; transition: color 0.15s, background 0.15s; } + .density-btn + .density-btn { border-left: 1px solid var(--border); } + .density-btn:hover { color: var(--select); } + .density-btn.active { background: var(--select); color: #1a1b1e; } + .tags-toggle { display: none; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text-dim); border-radius: 3px; padding: 0.3rem 0.7rem; cursor: pointer; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; text-transform: uppercase; } + .tags-toggle:hover, .tags-toggle.has-active { color: var(--select); border-color: var(--select); } + + /* Tag chips wrap by default (no-JS safe). With JS the row becomes a scroll + strip on wide viewports and collapses behind the Tags toggle on narrow + ones, so the sticky bar never eats the mobile viewport. */ + .chips { display: flex; flex-wrap: wrap; gap: 0.4rem; } + html.js .chips { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 0.25rem; + scrollbar-width: thin; scrollbar-color: var(--border) transparent; } + html.js .chips::-webkit-scrollbar { height: 5px; } + html.js .chips::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } .chip { background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); border-radius: 3px; padding: 0.22rem 0.7rem; font-size: 0.72rem; font-weight: 400; - font-family: var(--font-mono); cursor: pointer; transition: color 0.15s, border-color 0.15s; } + font-family: var(--font-mono); cursor: pointer; white-space: nowrap; flex: 0 0 auto; + transition: color 0.15s, border-color 0.15s; } .chip:hover { color: var(--select); border-color: var(--select); } .chip.active { color: #1a1b1e; background: var(--select); border-color: var(--select); } + @media (max-width: 719px) { + html.js .tags-toggle { display: inline-block; } + html.js .chips { display: none; } + html.js .chips.open { display: flex; flex-wrap: wrap; overflow-y: auto; max-height: 40vh; } + } + + /* Compact density: hero + name + one-line teaser. The description and + WITNESSES text stay in the DOM (searchable, screen-reader reachable, + present with JS disabled when the class is never applied) but are + visually collapsed. Applied only by JS via the density-compact class. */ + html.density-compact .grid { gap: 1rem; } + html.density-compact .card-body { padding: 0.65rem 0.9rem 0.7rem; } + html.density-compact .card-body h2 { font-size: 0.88rem; margin-bottom: 0.15rem; } + html.density-compact .teaches { font-size: 0.8rem; color: var(--text-dim); margin-bottom: 0; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + html.density-compact .witnesses { position: absolute; width: 1px; height: 1px; margin: -1px; + padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; } + html.density-compact .card-link { display: none; } + .noresults { color: var(--text-dim); text-align: center; padding: 3rem 1rem; font-size: 0.95rem; } + .noresults .chip { margin-left: 0.6rem; } + .to-top { position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 6; cursor: pointer; + background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); + border-radius: var(--radius); padding: 0.45rem 0.75rem; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; + text-transform: uppercase; opacity: 0; visibility: hidden; + transition: opacity 0.2s, visibility 0.2s, color 0.15s, border-color 0.15s; } + .to-top.show { opacity: 1; visibility: visible; } + .to-top:hover { color: var(--select); border-color: var(--select); } main { max-width: var(--maxw); margin: 0 auto; padding: 1rem 1.25rem 2rem; } .grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; align-items: stretch; } @@ -153,10 +221,18 @@ footer .statusbar code { font-family: inherit; text-transform: none; } @media (min-width: 720px) { .grid { grid-template-columns: 1fr 1fr; gap: 1.75rem; } } + @media (min-width: 560px) { html.density-compact .grid { grid-template-columns: 1fr 1fr; gap: 1rem; } } + @media (min-width: 900px) { html.density-compact .grid { grid-template-columns: repeat(3, 1fr); } } + @media (min-width: 1560px) { + html.density-compact .controls-inner, + html.density-compact main { max-width: 1400px; } + html.density-compact .grid { grid-template-columns: repeat(4, 1fr); } + } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } .card { transition: none; } .card:hover { transform: none; } + .to-top { transition: none; } } diff --git a/docs/gallery/index.html b/docs/gallery/index.html index 81e72e3..c2d7d6a 100644 --- a/docs/gallery/index.html +++ b/docs/gallery/index.html @@ -68,14 +68,82 @@ font-size: clamp(2.2rem, 5vw, 3.4rem); letter-spacing: 0.005em; line-height: 0.98; } header.hero p { color: var(--text-dim); max-width: 62ch; margin-top: 0.7rem; font-size: 1rem; } - /* ---- index: filter chips ---- */ - .chips { max-width: var(--maxw); margin: 0 auto; padding: 0 1.25rem 0.25rem; - display: flex; flex-wrap: wrap; gap: 0.5rem; } + /* ---- index: sticky controls (search, density toggle, tag chips) ---- */ + .controls { position: sticky; top: 46px; z-index: 4; + background: color-mix(in srgb, var(--bg) 94%, transparent); + backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); + border-bottom: 1px solid var(--border); } + .controls-inner { max-width: var(--maxw); margin: 0 auto; padding: 0.55rem 1.25rem 0.6rem; + display: flex; flex-direction: column; gap: 0.5rem; } + .controls-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; } + .searchwrap { position: relative; flex: 1 1 240px; min-width: 150px; } + .searchwrap input { width: 100%; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text); border-radius: var(--radius); padding: 0.34rem 1.9rem 0.34rem 0.7rem; + font-family: var(--font-sans); font-size: 0.85rem; line-height: 1.4; } + .searchwrap input:focus { border-color: var(--select); outline: none; } + .searchwrap input::placeholder { color: var(--text-dim); } + .searchwrap input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; } + .q-clear { position: absolute; right: 0.2rem; top: 50%; transform: translateY(-50%); + background: none; border: none; color: var(--text-dim); font-size: 1.05rem; line-height: 1; + cursor: pointer; padding: 0.25rem 0.45rem; border-radius: 3px; } + .q-clear:hover { color: var(--select); } + .count { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; color: var(--text-dim); white-space: nowrap; } + .density { display: flex; border: 1px solid var(--border); border-radius: var(--radius); + overflow: hidden; } + .density-btn { background: var(--surface-2); border: none; color: var(--text-dim); cursor: pointer; + font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; + text-transform: uppercase; padding: 0.36rem 0.7rem; transition: color 0.15s, background 0.15s; } + .density-btn + .density-btn { border-left: 1px solid var(--border); } + .density-btn:hover { color: var(--select); } + .density-btn.active { background: var(--select); color: #1a1b1e; } + .tags-toggle { display: none; background: var(--surface-2); border: 1px solid var(--border); + color: var(--text-dim); border-radius: 3px; padding: 0.3rem 0.7rem; cursor: pointer; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; text-transform: uppercase; } + .tags-toggle:hover, .tags-toggle.has-active { color: var(--select); border-color: var(--select); } + + /* Tag chips wrap by default (no-JS safe). With JS the row becomes a scroll + strip on wide viewports and collapses behind the Tags toggle on narrow + ones, so the sticky bar never eats the mobile viewport. */ + .chips { display: flex; flex-wrap: wrap; gap: 0.4rem; } + html.js .chips { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 0.25rem; + scrollbar-width: thin; scrollbar-color: var(--border) transparent; } + html.js .chips::-webkit-scrollbar { height: 5px; } + html.js .chips::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } .chip { background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); border-radius: 3px; padding: 0.22rem 0.7rem; font-size: 0.72rem; font-weight: 400; - font-family: var(--font-mono); cursor: pointer; transition: color 0.15s, border-color 0.15s; } + font-family: var(--font-mono); cursor: pointer; white-space: nowrap; flex: 0 0 auto; + transition: color 0.15s, border-color 0.15s; } .chip:hover { color: var(--select); border-color: var(--select); } .chip.active { color: #1a1b1e; background: var(--select); border-color: var(--select); } + @media (max-width: 719px) { + html.js .tags-toggle { display: inline-block; } + html.js .chips { display: none; } + html.js .chips.open { display: flex; flex-wrap: wrap; overflow-y: auto; max-height: 40vh; } + } + + /* Compact density: hero + name + one-line teaser. The description and + WITNESSES text stay in the DOM (searchable, screen-reader reachable, + present with JS disabled when the class is never applied) but are + visually collapsed. Applied only by JS via the density-compact class. */ + html.density-compact .grid { gap: 1rem; } + html.density-compact .card-body { padding: 0.65rem 0.9rem 0.7rem; } + html.density-compact .card-body h2 { font-size: 0.88rem; margin-bottom: 0.15rem; } + html.density-compact .teaches { font-size: 0.8rem; color: var(--text-dim); margin-bottom: 0; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + html.density-compact .witnesses { position: absolute; width: 1px; height: 1px; margin: -1px; + padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; } + html.density-compact .card-link { display: none; } + .noresults { color: var(--text-dim); text-align: center; padding: 3rem 1rem; font-size: 0.95rem; } + .noresults .chip { margin-left: 0.6rem; } + .to-top { position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 6; cursor: pointer; + background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); + border-radius: var(--radius); padding: 0.45rem 0.75rem; + font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; + text-transform: uppercase; opacity: 0; visibility: hidden; + transition: opacity 0.2s, visibility 0.2s, color 0.15s, border-color 0.15s; } + .to-top.show { opacity: 1; visibility: visible; } + .to-top:hover { color: var(--select); border-color: var(--select); } main { max-width: var(--maxw); margin: 0 auto; padding: 1rem 1.25rem 2rem; } .grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; align-items: stretch; } @@ -153,12 +221,36 @@ footer .statusbar code { font-family: inherit; text-transform: none; } @media (min-width: 720px) { .grid { grid-template-columns: 1fr 1fr; gap: 1.75rem; } } + @media (min-width: 560px) { html.density-compact .grid { grid-template-columns: 1fr 1fr; gap: 1rem; } } + @media (min-width: 900px) { html.density-compact .grid { grid-template-columns: repeat(3, 1fr); } } + @media (min-width: 1560px) { + html.density-compact .controls-inner, + html.density-compact main { max-width: 1400px; } + html.density-compact .grid { grid-template-columns: repeat(4, 1fr); } + } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } .card { transition: none; } .card:hover { transform: none; } + .to-top { transition: none; } } + @@ -172,47 +264,64 @@

Examples Gallery

Runnable, smoke-gated Blender Python examples — each executed headless on Blender 4.5 LTS and 5.1, so every render reflects code that actually runs.

-