Skip to content

Commit 087bdf1

Browse files
authored
feat: add search, density toggle, and sticky controls to gallery index (#113)
* 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 <TMhospitalitystrategies@gmail.com> * 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 <TMhospitalitystrategies@gmail.com> --------- Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com>
1 parent 1684a81 commit 087bdf1

42 files changed

Lines changed: 3781 additions & 234 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎docs/gallery/armature-bend/index.html‎

Lines changed: 80 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,82 @@
6868
font-size: clamp(2.2rem, 5vw, 3.4rem); letter-spacing: 0.005em; line-height: 0.98; }
6969
header.hero p { color: var(--text-dim); max-width: 62ch; margin-top: 0.7rem; font-size: 1rem; }
7070

71-
/* ---- index: filter chips ---- */
72-
.chips { max-width: var(--maxw); margin: 0 auto; padding: 0 1.25rem 0.25rem;
73-
display: flex; flex-wrap: wrap; gap: 0.5rem; }
71+
/* ---- index: sticky controls (search, density toggle, tag chips) ---- */
72+
.controls { position: sticky; top: 46px; z-index: 4;
73+
background: color-mix(in srgb, var(--bg) 94%, transparent);
74+
backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
75+
border-bottom: 1px solid var(--border); }
76+
.controls-inner { max-width: var(--maxw); margin: 0 auto; padding: 0.55rem 1.25rem 0.6rem;
77+
display: flex; flex-direction: column; gap: 0.5rem; }
78+
.controls-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
79+
.searchwrap { position: relative; flex: 1 1 240px; min-width: 150px; }
80+
.searchwrap input { width: 100%; background: var(--surface-2); border: 1px solid var(--border);
81+
color: var(--text); border-radius: var(--radius); padding: 0.34rem 1.9rem 0.34rem 0.7rem;
82+
font-family: var(--font-sans); font-size: 0.85rem; line-height: 1.4; }
83+
.searchwrap input:focus { border-color: var(--select); outline: none; }
84+
.searchwrap input::placeholder { color: var(--text-dim); }
85+
.searchwrap input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }
86+
.q-clear { position: absolute; right: 0.2rem; top: 50%; transform: translateY(-50%);
87+
background: none; border: none; color: var(--text-dim); font-size: 1.05rem; line-height: 1;
88+
cursor: pointer; padding: 0.25rem 0.45rem; border-radius: 3px; }
89+
.q-clear:hover { color: var(--select); }
90+
.count { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em;
91+
text-transform: uppercase; color: var(--text-dim); white-space: nowrap; }
92+
.density { display: flex; border: 1px solid var(--border); border-radius: var(--radius);
93+
overflow: hidden; }
94+
.density-btn { background: var(--surface-2); border: none; color: var(--text-dim); cursor: pointer;
95+
font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em;
96+
text-transform: uppercase; padding: 0.36rem 0.7rem; transition: color 0.15s, background 0.15s; }
97+
.density-btn + .density-btn { border-left: 1px solid var(--border); }
98+
.density-btn:hover { color: var(--select); }
99+
.density-btn.active { background: var(--select); color: #1a1b1e; }
100+
.tags-toggle { display: none; background: var(--surface-2); border: 1px solid var(--border);
101+
color: var(--text-dim); border-radius: 3px; padding: 0.3rem 0.7rem; cursor: pointer;
102+
font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; text-transform: uppercase; }
103+
.tags-toggle:hover, .tags-toggle.has-active { color: var(--select); border-color: var(--select); }
104+
105+
/* Tag chips wrap by default (no-JS safe). With JS the row becomes a scroll
106+
strip on wide viewports and collapses behind the Tags toggle on narrow
107+
ones, so the sticky bar never eats the mobile viewport. */
108+
.chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }
109+
html.js .chips { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 0.25rem;
110+
scrollbar-width: thin; scrollbar-color: var(--border) transparent; }
111+
html.js .chips::-webkit-scrollbar { height: 5px; }
112+
html.js .chips::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
74113
.chip { background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim);
75114
border-radius: 3px; padding: 0.22rem 0.7rem; font-size: 0.72rem; font-weight: 400;
76-
font-family: var(--font-mono); cursor: pointer; transition: color 0.15s, border-color 0.15s; }
115+
font-family: var(--font-mono); cursor: pointer; white-space: nowrap; flex: 0 0 auto;
116+
transition: color 0.15s, border-color 0.15s; }
77117
.chip:hover { color: var(--select); border-color: var(--select); }
78118
.chip.active { color: #1a1b1e; background: var(--select); border-color: var(--select); }
119+
@media (max-width: 719px) {
120+
html.js .tags-toggle { display: inline-block; }
121+
html.js .chips { display: none; }
122+
html.js .chips.open { display: flex; flex-wrap: wrap; overflow-y: auto; max-height: 40vh; }
123+
}
124+
125+
/* Compact density: hero + name + one-line teaser. The description and
126+
WITNESSES text stay in the DOM (searchable, screen-reader reachable,
127+
present with JS disabled when the class is never applied) but are
128+
visually collapsed. Applied only by JS via the density-compact class. */
129+
html.density-compact .grid { gap: 1rem; }
130+
html.density-compact .card-body { padding: 0.65rem 0.9rem 0.7rem; }
131+
html.density-compact .card-body h2 { font-size: 0.88rem; margin-bottom: 0.15rem; }
132+
html.density-compact .teaches { font-size: 0.8rem; color: var(--text-dim); margin-bottom: 0;
133+
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
134+
html.density-compact .witnesses { position: absolute; width: 1px; height: 1px; margin: -1px;
135+
padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
136+
html.density-compact .card-link { display: none; }
137+
.noresults { color: var(--text-dim); text-align: center; padding: 3rem 1rem; font-size: 0.95rem; }
138+
.noresults .chip { margin-left: 0.6rem; }
139+
.to-top { position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 6; cursor: pointer;
140+
background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim);
141+
border-radius: var(--radius); padding: 0.45rem 0.75rem;
142+
font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em;
143+
text-transform: uppercase; opacity: 0; visibility: hidden;
144+
transition: opacity 0.2s, visibility 0.2s, color 0.15s, border-color 0.15s; }
145+
.to-top.show { opacity: 1; visibility: visible; }
146+
.to-top:hover { color: var(--select); border-color: var(--select); }
79147

80148
main { max-width: var(--maxw); margin: 0 auto; padding: 1rem 1.25rem 2rem; }
81149
.grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; align-items: stretch; }
@@ -153,10 +221,18 @@
153221
footer .statusbar code { font-family: inherit; text-transform: none; }
154222

155223
@media (min-width: 720px) { .grid { grid-template-columns: 1fr 1fr; gap: 1.75rem; } }
224+
@media (min-width: 560px) { html.density-compact .grid { grid-template-columns: 1fr 1fr; gap: 1rem; } }
225+
@media (min-width: 900px) { html.density-compact .grid { grid-template-columns: repeat(3, 1fr); } }
226+
@media (min-width: 1560px) {
227+
html.density-compact .controls-inner,
228+
html.density-compact main { max-width: 1400px; }
229+
html.density-compact .grid { grid-template-columns: repeat(4, 1fr); }
230+
}
156231
@media (prefers-reduced-motion: reduce) {
157232
html { scroll-behavior: auto; }
158233
.card { transition: none; }
159234
.card:hover { transform: none; }
235+
.to-top { transition: none; }
160236
}
161237
</style>
162238
</head>

‎docs/gallery/attribute-domain-shear/index.html‎

Lines changed: 80 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,82 @@
6868
font-size: clamp(2.2rem, 5vw, 3.4rem); letter-spacing: 0.005em; line-height: 0.98; }
6969
header.hero p { color: var(--text-dim); max-width: 62ch; margin-top: 0.7rem; font-size: 1rem; }
7070

71-
/* ---- index: filter chips ---- */
72-
.chips { max-width: var(--maxw); margin: 0 auto; padding: 0 1.25rem 0.25rem;
73-
display: flex; flex-wrap: wrap; gap: 0.5rem; }
71+
/* ---- index: sticky controls (search, density toggle, tag chips) ---- */
72+
.controls { position: sticky; top: 46px; z-index: 4;
73+
background: color-mix(in srgb, var(--bg) 94%, transparent);
74+
backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
75+
border-bottom: 1px solid var(--border); }
76+
.controls-inner { max-width: var(--maxw); margin: 0 auto; padding: 0.55rem 1.25rem 0.6rem;
77+
display: flex; flex-direction: column; gap: 0.5rem; }
78+
.controls-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
79+
.searchwrap { position: relative; flex: 1 1 240px; min-width: 150px; }
80+
.searchwrap input { width: 100%; background: var(--surface-2); border: 1px solid var(--border);
81+
color: var(--text); border-radius: var(--radius); padding: 0.34rem 1.9rem 0.34rem 0.7rem;
82+
font-family: var(--font-sans); font-size: 0.85rem; line-height: 1.4; }
83+
.searchwrap input:focus { border-color: var(--select); outline: none; }
84+
.searchwrap input::placeholder { color: var(--text-dim); }
85+
.searchwrap input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }
86+
.q-clear { position: absolute; right: 0.2rem; top: 50%; transform: translateY(-50%);
87+
background: none; border: none; color: var(--text-dim); font-size: 1.05rem; line-height: 1;
88+
cursor: pointer; padding: 0.25rem 0.45rem; border-radius: 3px; }
89+
.q-clear:hover { color: var(--select); }
90+
.count { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em;
91+
text-transform: uppercase; color: var(--text-dim); white-space: nowrap; }
92+
.density { display: flex; border: 1px solid var(--border); border-radius: var(--radius);
93+
overflow: hidden; }
94+
.density-btn { background: var(--surface-2); border: none; color: var(--text-dim); cursor: pointer;
95+
font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em;
96+
text-transform: uppercase; padding: 0.36rem 0.7rem; transition: color 0.15s, background 0.15s; }
97+
.density-btn + .density-btn { border-left: 1px solid var(--border); }
98+
.density-btn:hover { color: var(--select); }
99+
.density-btn.active { background: var(--select); color: #1a1b1e; }
100+
.tags-toggle { display: none; background: var(--surface-2); border: 1px solid var(--border);
101+
color: var(--text-dim); border-radius: 3px; padding: 0.3rem 0.7rem; cursor: pointer;
102+
font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; text-transform: uppercase; }
103+
.tags-toggle:hover, .tags-toggle.has-active { color: var(--select); border-color: var(--select); }
104+
105+
/* Tag chips wrap by default (no-JS safe). With JS the row becomes a scroll
106+
strip on wide viewports and collapses behind the Tags toggle on narrow
107+
ones, so the sticky bar never eats the mobile viewport. */
108+
.chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }
109+
html.js .chips { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 0.25rem;
110+
scrollbar-width: thin; scrollbar-color: var(--border) transparent; }
111+
html.js .chips::-webkit-scrollbar { height: 5px; }
112+
html.js .chips::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
74113
.chip { background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim);
75114
border-radius: 3px; padding: 0.22rem 0.7rem; font-size: 0.72rem; font-weight: 400;
76-
font-family: var(--font-mono); cursor: pointer; transition: color 0.15s, border-color 0.15s; }
115+
font-family: var(--font-mono); cursor: pointer; white-space: nowrap; flex: 0 0 auto;
116+
transition: color 0.15s, border-color 0.15s; }
77117
.chip:hover { color: var(--select); border-color: var(--select); }
78118
.chip.active { color: #1a1b1e; background: var(--select); border-color: var(--select); }
119+
@media (max-width: 719px) {
120+
html.js .tags-toggle { display: inline-block; }
121+
html.js .chips { display: none; }
122+
html.js .chips.open { display: flex; flex-wrap: wrap; overflow-y: auto; max-height: 40vh; }
123+
}
124+
125+
/* Compact density: hero + name + one-line teaser. The description and
126+
WITNESSES text stay in the DOM (searchable, screen-reader reachable,
127+
present with JS disabled when the class is never applied) but are
128+
visually collapsed. Applied only by JS via the density-compact class. */
129+
html.density-compact .grid { gap: 1rem; }
130+
html.density-compact .card-body { padding: 0.65rem 0.9rem 0.7rem; }
131+
html.density-compact .card-body h2 { font-size: 0.88rem; margin-bottom: 0.15rem; }
132+
html.density-compact .teaches { font-size: 0.8rem; color: var(--text-dim); margin-bottom: 0;
133+
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
134+
html.density-compact .witnesses { position: absolute; width: 1px; height: 1px; margin: -1px;
135+
padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
136+
html.density-compact .card-link { display: none; }
137+
.noresults { color: var(--text-dim); text-align: center; padding: 3rem 1rem; font-size: 0.95rem; }
138+
.noresults .chip { margin-left: 0.6rem; }
139+
.to-top { position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 6; cursor: pointer;
140+
background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim);
141+
border-radius: var(--radius); padding: 0.45rem 0.75rem;
142+
font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em;
143+
text-transform: uppercase; opacity: 0; visibility: hidden;
144+
transition: opacity 0.2s, visibility 0.2s, color 0.15s, border-color 0.15s; }
145+
.to-top.show { opacity: 1; visibility: visible; }
146+
.to-top:hover { color: var(--select); border-color: var(--select); }
79147

80148
main { max-width: var(--maxw); margin: 0 auto; padding: 1rem 1.25rem 2rem; }
81149
.grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; align-items: stretch; }
@@ -153,10 +221,18 @@
153221
footer .statusbar code { font-family: inherit; text-transform: none; }
154222

155223
@media (min-width: 720px) { .grid { grid-template-columns: 1fr 1fr; gap: 1.75rem; } }
224+
@media (min-width: 560px) { html.density-compact .grid { grid-template-columns: 1fr 1fr; gap: 1rem; } }
225+
@media (min-width: 900px) { html.density-compact .grid { grid-template-columns: repeat(3, 1fr); } }
226+
@media (min-width: 1560px) {
227+
html.density-compact .controls-inner,
228+
html.density-compact main { max-width: 1400px; }
229+
html.density-compact .grid { grid-template-columns: repeat(4, 1fr); }
230+
}
156231
@media (prefers-reduced-motion: reduce) {
157232
html { scroll-behavior: auto; }
158233
.card { transition: none; }
159234
.card:hover { transform: none; }
235+
.to-top { transition: none; }
160236
}
161237
</style>
162238
</head>

0 commit comments

Comments
 (0)