fabrica

hanna/fabrica

fix(web): collapsible language bar above the tree; key badge order

6100f0d · hanna committed on 2026-07-25

Move the language bar above the file tree (under the repo search) and make it a
collapsible <details> — the stripe is always shown, the named legend expands on
click. Reset the legend list's default left padding. Reorder the key row so the
verified/unverified badge sits between the type badge and the key name.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed by hannaSSH key fingerprint: SHA256:4g9cWhkAAw8gwqhJUcVbSnGEvdGwklW+1Aa/fMUu59k
3 files changed · +25 −13UnifiedSplit
assets/base.css +11 −3
@@ -601,9 +601,16 @@ body.drawer-open .drawer-backdrop {
601601 flex: none;
602602 }
603603
604-/* Repository language breakdown bar. */
604+/* Repository language breakdown bar (a collapsible <details>). */
605605 .lang-bar-wrap {
606- margin: 1rem 0;
606+ margin: 0 0 1rem;
607+}
608+.lang-bar-summary {
609+ list-style: none;
610+ cursor: pointer;
611+}
612+.lang-bar-summary::-webkit-details-marker {
613+ display: none;
607614 }
608615 .lang-bar {
609616 display: flex;
@@ -620,10 +627,11 @@ body.drawer-open .drawer-backdrop {
620627 }
621628 .lang-legend {
622629 list-style: none;
630+ padding: 0;
623631 display: flex;
624632 flex-wrap: wrap;
625633 gap: 0.35rem 1.1rem;
626- margin-top: 0.75rem;
634+ margin: 0.75rem 0 0;
627635 }
628636 .lang-legend li {
629637 display: flex;
crates/web/src/pages.rs +1 −1
@@ -1859,12 +1859,12 @@ fn keys_section(csrf: &str, keys: &[model::Key]) -> Markup {
18591859 div class="entry-row-body" {
18601860 span class="entry-row-title" {
18611861 span class="badge" { (k.kind.as_str()) }
1862- " " (k.name.as_deref().unwrap_or("(unnamed)"))
18631862 @if k.verified() {
18641863 " " span class="badge badge-verified" { "verified" }
18651864 } @else {
18661865 " " span class="badge badge-unverified" { "unverified" }
18671866 }
1867+ " " (k.name.as_deref().unwrap_or("(unnamed)"))
18681868 }
18691869 div class="entry-row-meta muted mono" { (k.fingerprint) }
18701870 @if !k.verified() {
crates/web/src/repo.rs +13 −9
@@ -463,15 +463,19 @@ fn language_bar(langs: &[crate::languages::LangStat]) -> Markup {
463463 let (head, tail) = langs.split_at(langs.len().min(MAX_SHOWN));
464464 let other: f64 = tail.iter().map(|l| l.percent).sum();
465465 let pct = |p: f64| format!("{p:.1}%");
466+ // A `<details>`: the bar is the always-visible summary; the legend expands on
467+ // click and works without JavaScript.
466468 html! {
467- div class="card lang-bar-wrap" {
468- div class="lang-bar" aria-hidden="true" {
469- @for l in head {
470- span class="lang-seg"
471- style=(format!("width:{:.2}%;background:{}", l.percent, l.color)) {}
472- }
473- @if other > 0.0 {
474- span class="lang-seg" style=(format!("width:{other:.2}%;background:#8b8b8b")) {}
469+ details class="card lang-bar-wrap" {
470+ summary class="lang-bar-summary" title="Show languages" {
471+ div class="lang-bar" aria-hidden="true" {
472+ @for l in head {
473+ span class="lang-seg"
474+ style=(format!("width:{:.2}%;background:{}", l.percent, l.color)) {}
475+ }
476+ @if other > 0.0 {
477+ span class="lang-seg" style=(format!("width:{other:.2}%;background:#8b8b8b")) {}
478+ }
475479 }
476480 }
477481 ul class="lang-legend" {
@@ -535,10 +539,10 @@ async fn repo_home(
535539 aria-label="Search this repository";
536540 button class="btn" type="submit" { (icon(Icon::Search)) span { "Search" } }
537541 }
538- (tree_table(&ctx.owner.username, &ctx.repo.path, &rev_name, "", &entries))
539542 @if !langs.is_empty() {
540543 (language_bar(&langs))
541544 }
545+ (tree_table(&ctx.owner.username, &ctx.repo.path, &rev_name, "", &entries))
542546 @if let Some(rendered) = readme {
543547 div class="card markdown readme" { (rendered) }
544548 }