levineuwirth.org/static/css/gallery.css

538 lines
13 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* gallery.css — Exhibit system (always-visible inline blocks with overlay
expansion) and annotation system (static or collapsible callout boxes). */
/* ============================================================
EXHIBIT WRAPPER
Base styles shared by all exhibit types.
============================================================ */
.exhibit {
position: relative;
display: block;
margin: 1.65rem 0;
padding: 0; /* reset <figure> default margin */
}
/* ============================================================
MATH FOCUSABLE
Wrapper injected around every .katex-display by gallery.js.
Clicking anywhere on the wrapper opens the overlay.
============================================================ */
.math-focusable {
position: relative;
display: block;
cursor: zoom-in;
}
/* Expand glyph — decorative affordance, not interactive */
.exhibit-expand {
position: absolute;
top: 50%;
right: 0.4rem;
transform: translateY(-50%);
font-family: var(--font-sans);
font-size: 0.75rem;
line-height: 1;
color: var(--text-faint);
padding: 0.2rem 0.35rem;
border-radius: 2px;
pointer-events: none; /* click handled by wrapper */
opacity: 0;
transition: opacity var(--transition-fast);
user-select: none;
}
.math-focusable:hover .exhibit-expand {
opacity: 1;
}
/* Caption tooltip — appears above the math on hover, like alt text */
.math-focusable[data-caption]::after {
content: attr(data-caption);
position: absolute;
bottom: calc(100% + 0.5rem);
left: 50%;
transform: translateX(-50%);
background: var(--bg);
border: 1px solid var(--border);
border-radius: 2px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.07);
padding: 0.3rem 0.65rem;
font-family: var(--font-sans);
font-size: 0.72rem;
font-style: italic;
color: var(--text-muted);
/* Explicit width prevents the box from collapsing to a single-word column */
width: min(440px, 70vw);
white-space: normal;
text-align: center;
pointer-events: none;
opacity: 0;
transition: opacity var(--transition-fast);
z-index: 10;
}
.math-focusable[data-caption]:hover::after {
opacity: 1;
}
/* ============================================================
PROOF EXHIBIT
Always-visible inline block. Header is a non-interactive label.
============================================================ */
.exhibit--proof .exhibit-header {
display: flex;
align-items: baseline;
gap: 0.5rem;
padding-bottom: 0.45rem;
margin-bottom: 0.6rem;
border-bottom: 1px solid var(--border-muted);
}
.exhibit--proof .exhibit-header-label {
font-style: italic;
font-weight: 600;
color: var(--text);
flex-shrink: 0;
}
.exhibit--proof .exhibit-header-name {
font-family: var(--font-sans);
font-size: 0.82rem;
color: var(--text-muted);
}
/* ============================================================
ANNOTATION
Editorial callout boxes: definitions, notes, remarks, warnings.
Two variants: static (always visible) and collapsible.
============================================================ */
.annotation {
position: relative;
margin: 1.65rem 0;
border: 1px solid var(--border);
border-radius: 2px;
}
.annotation-header {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 0.5rem;
padding: 0.5rem 0.85rem;
border-bottom: 1px solid var(--border);
background: var(--bg-offset);
}
.annotation-label {
font-family: var(--font-sans);
font-size: 0.68rem;
font-weight: 600;
font-variant-caps: all-small-caps;
letter-spacing: 0.06em;
color: var(--text-faint);
flex-shrink: 0;
}
.annotation-name {
font-family: var(--font-sans);
font-size: 0.78rem;
color: var(--text-muted);
font-style: italic;
flex: 1;
min-width: 0;
}
.annotation-toggle {
background: none;
border: none;
cursor: pointer;
font-family: var(--font-sans);
font-size: 0.68rem;
color: var(--text-faint);
padding: 0;
line-height: 1;
flex-shrink: 0;
transition: color var(--transition-fast);
white-space: nowrap;
}
.annotation-toggle:hover {
color: var(--text-muted);
}
.annotation-body {
padding: 0.825rem 0.85rem;
}
/* Collapsible variant: body hidden until toggled.
Only max-height transitions — padding applies instantly so that
scrollHeight reads the correct full height before the animation starts. */
.annotation--collapsible .annotation-body {
overflow: hidden;
max-height: 0;
padding-top: 0;
padding-bottom: 0;
transition: max-height 0.35s ease;
}
.annotation--collapsible.is-open .annotation-body {
padding-top: 0.825rem;
padding-bottom: 0.825rem;
}
/* Static variant: always open, no toggle */
.annotation--static .annotation-header {
border-bottom: 1px solid var(--border);
}
/* ============================================================
OVERLAY
Full-screen dark stage. Content floats inside — no panel box.
Modeled on Gwern's image-focus: the overlay IS the backdrop,
content is centered directly within it.
============================================================ */
#gallery-overlay {
position: fixed;
inset: 0;
z-index: 500;
background: rgba(0, 0, 0, 0.92);
cursor: zoom-out;
display: flex;
align-items: center;
justify-content: center;
}
#gallery-overlay[hidden] {
display: none;
}
/* Content stage — large light area centered in the dark screen.
Fixed width (not max-width) so JS can detect overflow for font fitting. */
#gallery-overlay-body {
background: var(--bg);
padding: 3.5rem 4.5rem;
width: 88vw;
max-height: 80vh;
overflow: hidden; /* JS shrinks font until content fits; auto as last resort */
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 0 80px rgba(0, 0, 0, 0.5);
cursor: default; /* undo zoom-out from parent */
}
#gallery-overlay-body .katex-display {
/* font-size is set entirely by JS after measuring — no CSS value here */
overflow-x: hidden;
margin: 0;
}
/* Close — top right of screen */
#gallery-overlay-close {
position: absolute;
top: 1.1rem;
right: 1.5rem;
background: none;
border: none;
cursor: pointer;
font-family: var(--font-sans);
font-size: 1.5rem;
color: rgba(255, 255, 255, 0.5);
padding: 0.3rem 0.5rem;
line-height: 1;
transition: color var(--transition-fast);
}
#gallery-overlay-close:hover {
color: rgba(255, 255, 255, 0.95);
}
/* Group name — top center of screen */
#gallery-overlay-name {
position: absolute;
top: 1.5rem;
left: 50%;
transform: translateX(-50%);
font-family: var(--font-sans);
font-size: 0.9rem;
font-weight: 600;
font-variant-caps: all-small-caps;
letter-spacing: 0.09em;
color: rgba(255, 255, 255, 0.5);
white-space: nowrap;
pointer-events: none;
}
/* Caption — bottom center of screen, above counter */
#gallery-overlay-caption {
position: absolute;
bottom: 3.5rem;
left: 50%;
transform: translateX(-50%);
font-family: var(--font-sans);
font-size: 1rem;
font-style: italic;
color: rgba(255, 255, 255, 0.65);
text-align: center;
max-width: 60vw;
pointer-events: none;
}
#gallery-overlay-caption:empty,
#gallery-overlay-caption[hidden] {
display: none;
}
/* Counter — bottom center of screen */
#gallery-overlay-counter {
position: absolute;
bottom: 1.5rem;
left: 50%;
transform: translateX(-50%);
font-family: var(--font-sans);
font-size: 0.82rem;
color: rgba(255, 255, 255, 0.38);
pointer-events: none;
white-space: nowrap;
}
/* Nav buttons — floating at the screen edges, vertically centered */
.gallery-nav-btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
cursor: pointer;
font-family: var(--font-sans);
font-size: 2.25rem;
color: rgba(255, 255, 255, 0.35);
padding: 0.75rem 1rem;
line-height: 1;
transition: color var(--transition-fast);
}
.gallery-nav-btn:hover:not(:disabled) {
color: rgba(255, 255, 255, 0.9);
}
.gallery-nav-btn:disabled {
opacity: 0;
pointer-events: none;
}
#gallery-overlay-prev { left: 1rem; }
#gallery-overlay-next { right: 1rem; }
/* ============================================================
SCORE FRAGMENT
Inline SVG music notation. Integrates with the gallery focusable
and named-exhibit systems. No header — caption only.
============================================================ */
.score-fragment {
position: relative;
display: block;
margin: 2rem 0;
cursor: zoom-in;
}
/* Reset #markdownBody figure box styles — must use ID+class to beat
the specificity of `#markdownBody figure` (0,1,1 vs 0,1,0). */
#markdownBody figure.score-fragment {
background: none;
border: none;
padding: 0;
box-shadow: none;
max-width: 100%; /* override fit-content so SVG fills the column */
margin: 2rem 0; /* override the base 3.3rem auto */
}
.score-fragment-inner {
display: block;
line-height: 0; /* collapse inline gap below the SVG block */
}
/* Make the LilyPond SVG responsive and theme-aware.
CSS width/height override the SVG's own width/height presentation
attributes. color: var(--text) is inherited by currentColor fills/strokes
(set by Filters.Score at build time). */
.score-fragment-inner svg {
width: 100%;
height: auto;
display: block;
color: var(--text);
}
/* Remove LilyPond's white background rectangle */
.score-fragment-inner svg > rect:first-child {
fill: none;
}
/* Expand glyph: top-right corner, not vertically centred */
.score-fragment .exhibit-expand {
top: 0.5rem;
transform: none;
}
.score-fragment:hover .exhibit-expand {
opacity: 1;
}
.score-caption {
font-family: var(--font-sans);
font-size: 0.82rem;
font-style: italic;
color: var(--text-muted);
text-align: center;
margin-top: 0.65rem;
line-height: 1.4;
}
/* ============================================================
OVERLAY — SCORE MODE
Tighter horizontal padding; SVG fills available width.
============================================================ */
#gallery-overlay-body.is-score {
padding: 2rem 1.5rem;
/* Caption sits at bottom: 3.5rem in the viewport. The body is flex-centered,
so its bottom edge = (100vh + body-height) / 2. To guarantee clearance,
body-height must be ≤ 100vh 2 × caption-offset some margin.
calc(100vh 12rem) keeps the bottom edge ≥ 6rem above the viewport bottom
regardless of screen size. */
max-height: calc(100vh - 12rem);
}
#gallery-overlay-body.is-score svg {
width: 100%;
height: auto;
max-height: calc(100vh - 16rem); /* body height minus 2rem padding each side */
display: block;
color: var(--text);
}
#gallery-overlay-body.is-score svg > rect:first-child {
fill: none;
}
/* ============================================================
TOC EXHIBIT INTEGRATION
============================================================ */
/* Compact inline exhibit links under a heading entry */
.toc-exhibits-inline {
font-size: 0.69rem;
color: var(--text-faint);
line-height: 1.4;
margin-top: 0.1rem;
padding-left: 0.75rem;
}
.toc-exhibits-inline a {
display: flex;
align-items: baseline;
gap: 0.4em;
color: var(--text-faint);
text-decoration: none;
padding: 0.1rem 0;
font-size: 0.74rem;
transition: color var(--transition-fast);
}
.toc-exhibits-inline a:hover {
color: var(--text-muted);
}
/* Contained Herein — collapsible global exhibit index */
.toc-contained {
margin-top: 0.9rem;
padding-top: 0.6rem;
border-top: 1px solid var(--border);
}
.toc-contained-toggle {
display: flex;
align-items: center;
gap: 0.35em;
background: none;
border: none;
cursor: pointer;
font-family: var(--font-sans);
font-size: 0.69rem;
font-weight: 600;
font-variant-caps: all-small-caps;
letter-spacing: 0.06em;
color: var(--text-faint);
padding: 0;
line-height: 1.5;
width: 100%;
text-align: left;
transition: color var(--transition-fast);
}
.toc-contained-toggle:hover {
color: var(--text-muted);
}
.toc-contained-arrow {
font-size: 0.55rem;
display: inline-block;
transition: transform 0.15s ease;
flex-shrink: 0;
}
.toc-contained.is-open .toc-contained-arrow {
transform: rotate(90deg);
}
.toc-contained-list {
list-style: none;
padding: 0;
margin: 0.35rem 0 0 0;
display: none;
}
.toc-contained.is-open .toc-contained-list {
display: block;
}
.toc-contained-list li {
margin: 0.2rem 0;
}
.toc-contained-list a {
display: flex;
align-items: baseline;
gap: 0.4em;
font-size: 0.72rem;
color: var(--text-faint);
text-decoration: none;
transition: color var(--transition-fast);
}
.toc-contained-list a:hover {
color: var(--text-muted);
}
.toc-exhibit-type-badge {
font-size: 0.6rem;
font-variant-caps: all-small-caps;
letter-spacing: 0.05em;
color: var(--text-faint);
flex-shrink: 0;
}