library: portal ornaments + inter-shelf divider

Each shelf gets a dingbat keyed by portal slug: laurel (research),
quill (nonfiction), open book (fiction), lyre (poetry), plus the
existing clef / ai / tech / trefoil glyphs for the remaining four.
Rendered via mask-image with currentColor so a single SVG per
portal inherits whatever color its heading carries. Between rendered
shelves, a centered fleuron flanked by thin rules (library-divider.svg)
sits via CSS adjacent-sibling so hidden sections leave no orphan
dividers. The template swaps its Unicode placeholder for a
data-ornament span, wires a '\$library-intro\$' slot above the shelves,
and renders a "More on this shelf →" link when has-more gates fire.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Levi Neuwirth 2026-04-20 21:20:35 -04:00
parent c877d8c9c6
commit 0221603766
7 changed files with 173 additions and 8 deletions

View File

@ -164,6 +164,109 @@
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
} }
/* Per-shelf ornament sitting before the heading text. SVGs in
/images/dingbats/ painted via mask-image so they inherit
currentColor ( the same muted tone as the heading). */
.library-section-ornament {
display: inline-block;
width: 1em;
height: 1em;
margin-right: 0.5em;
background-color: currentColor;
mask-repeat: no-repeat;
mask-position: center;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
-webkit-mask-position: center;
-webkit-mask-size: contain;
vertical-align: -0.12em;
}
.library-section-ornament[data-ornament="research"] {
mask-image: url('/images/dingbats/research.svg');
-webkit-mask-image: url('/images/dingbats/research.svg');
}
.library-section-ornament[data-ornament="nonfiction"] {
mask-image: url('/images/dingbats/nonfiction.svg');
-webkit-mask-image: url('/images/dingbats/nonfiction.svg');
}
.library-section-ornament[data-ornament="fiction"] {
mask-image: url('/images/dingbats/fiction.svg');
-webkit-mask-image: url('/images/dingbats/fiction.svg');
}
.library-section-ornament[data-ornament="poetry"] {
mask-image: url('/images/dingbats/poetry.svg');
-webkit-mask-image: url('/images/dingbats/poetry.svg');
}
.library-section-ornament[data-ornament="music"] {
mask-image: url('/images/dingbats/clef.svg');
-webkit-mask-image: url('/images/dingbats/clef.svg');
}
.library-section-ornament[data-ornament="ai"] {
mask-image: url('/images/dingbats/ai.svg');
-webkit-mask-image: url('/images/dingbats/ai.svg');
}
.library-section-ornament[data-ornament="tech"] {
mask-image: url('/images/dingbats/tech.svg');
-webkit-mask-image: url('/images/dingbats/tech.svg');
}
.library-section-ornament[data-ornament="miscellany"] {
mask-image: url('/images/dingbats/trefoil.svg');
-webkit-mask-image: url('/images/dingbats/trefoil.svg');
}
/* Inter-shelf divider. Adjacent-sibling selector renders between
actually-emitted sections a section hidden by its $if$ gate
leaves no orphan divider because it never hits the DOM. */
.library-section + .library-section::before {
content: "";
display: block;
width: 240px;
max-width: 60%;
height: 24px;
margin: 2.5rem auto;
color: var(--text-faint);
background-color: currentColor;
mask-image: url('/images/dingbats/library-divider.svg');
-webkit-mask-image: url('/images/dingbats/library-divider.svg');
mask-repeat: no-repeat;
mask-position: center;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
-webkit-mask-position: center;
-webkit-mask-size: contain;
}
/* "More on this shelf →" link, shown when the portal has more
items than the shelf's cap. Right-aligned, italic, subdued. */
.library-more {
text-align: right;
font-family: var(--font-serif);
font-style: italic;
font-size: 0.9rem;
color: var(--text-muted);
margin: 0.65rem 0 0;
}
.library-more a {
color: inherit;
text-decoration: underline;
text-decoration-color: var(--border);
text-decoration-thickness: 0.08em;
text-underline-offset: 0.2em;
transition: color var(--transition-fast), text-decoration-color var(--transition-fast);
}
.library-more a:hover {
color: var(--text);
text-decoration-color: var(--border-muted);
}
/* Leading blockquote above the shelves, lifted from content/library.md. */
.library-intro {
margin: 0.5rem 0 2rem;
}
.library-section h2 a { .library-section h2 a {
color: inherit; color: inherit;
text-decoration: none; text-decoration: none;

View File

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path d="M3 6 L 11.5 5 L 11.5 20 L 3 19 Z"/>
<path d="M21 6 L 12.5 5 L 12.5 20 L 21 19 Z"/>
<path d="M11.5 5 L 12.5 5 L 12.5 20 L 11.5 20 Z" fill-opacity="0.55"/>
</svg>

After

Width:  |  Height:  |  Size: 257 B

View File

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 24" fill="currentColor">
<rect x="20" y="11.5" width="80" height="1"/>
<circle cx="106" cy="12" r="1.5"/>
<path fill-rule="evenodd" d="M120 4 L 126 12 L 120 20 L 114 12 Z M120 8.5 L 123 12 L 120 15.5 L 117 12 Z"/>
<circle cx="134" cy="12" r="1.5"/>
<rect x="140" y="11.5" width="80" height="1"/>
</svg>

After

Width:  |  Height:  |  Size: 370 B

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path d="M20 2 L 22 4 L 10 16 L 7 19 L 4 20 L 5 17 L 8 14 L 18 4 Z"/>
<path d="M15 7 L 13 7 L 14 5 Z"/>
<path d="M18 4 L 16 4 L 17 2 Z"/>
<path d="M12 10 L 10 10 L 11 8 Z"/>
</svg>

After

Width:  |  Height:  |  Size: 270 B

View File

@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path d="M5 4 C 3 9, 3 16, 6 20 L 8 20 C 5 16, 5 9, 7 5 Z"/>
<path d="M19 4 C 21 9, 21 16, 18 20 L 16 20 C 19 16, 19 9, 17 5 Z"/>
<rect x="4.5" y="3" width="15" height="2" rx="0.8"/>
<rect x="9.8" y="5" width="0.7" height="15"/>
<rect x="11.65" y="5" width="0.7" height="15"/>
<rect x="13.5" y="5" width="0.7" height="15"/>
</svg>

After

Width:  |  Height:  |  Size: 424 B

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path d="M6 3 C 4 9, 5 17, 11 21 L 11 19 C 7 16, 6 10, 7 5 Z"/>
<path d="M18 3 C 20 9, 19 17, 13 21 L 13 19 C 17 16, 18 10, 17 5 Z"/>
<rect x="9.5" y="20" width="5" height="1.5" rx="0.6"/>
<circle cx="12" cy="12" r="1"/>
</svg>

After

Width:  |  Height:  |  Size: 317 B

View File

@ -1,95 +1,125 @@
<div id="markdownBody"> <div id="markdownBody">
<h1 class="page-title">Library</h1> <h1 class="page-title">Library</h1>
$if(library-intro)$
<div class="library-intro">
$library-intro$
</div>
$endif$
<nav class="library-see-also" aria-label="See also"> <nav class="library-see-also" aria-label="See also">
<a href="/new.html">New</a><span class="library-see-also-sep" aria-hidden="true">·</span><a href="/commonplace.html">Commonplace</a><span class="library-see-also-sep" aria-hidden="true">·</span><a href="/colophon.html">Colophon</a><span class="library-see-also-sep" aria-hidden="true">·</span><a href="/bibliography/">Bibliography</a> <a href="/new.html">New</a><span class="library-see-also-sep" aria-hidden="true">·</span><a href="/commonplace.html">Commonplace</a><span class="library-see-also-sep" aria-hidden="true">·</span><a href="/colophon.html">Colophon</a><span class="library-see-also-sep" aria-hidden="true">·</span><a href="/bibliography/">Bibliography</a>
</nav> </nav>
$if(research-entries)$ $if(research-entries)$
<section class="library-section"> <section class="library-section">
<h2 id="research"><a href="/research/">Research</a></h2> <h2 id="research"><span class="library-section-ornament" data-ornament="research" aria-hidden="true"></span><a href="/research/">Research</a></h2>
<ul class="item-card-list"> <ul class="item-card-list">
$for(research-entries)$ $for(research-entries)$
$partial("templates/partials/item-card.html")$ $partial("templates/partials/item-card.html")$
$endfor$ $endfor$
</ul> </ul>
$if(research-has-more)$
<p class="library-more"><a href="/research/">More on this shelf &rarr;</a></p>
$endif$
</section> </section>
$endif$ $endif$
$if(nonfiction-entries)$ $if(nonfiction-entries)$
<section class="library-section"> <section class="library-section">
<h2 id="nonfiction"><a href="/nonfiction/">Nonfiction</a></h2> <h2 id="nonfiction"><span class="library-section-ornament" data-ornament="nonfiction" aria-hidden="true"></span><a href="/nonfiction/">Nonfiction</a></h2>
<ul class="item-card-list"> <ul class="item-card-list">
$for(nonfiction-entries)$ $for(nonfiction-entries)$
$partial("templates/partials/item-card.html")$ $partial("templates/partials/item-card.html")$
$endfor$ $endfor$
</ul> </ul>
$if(nonfiction-has-more)$
<p class="library-more"><a href="/nonfiction/">More on this shelf &rarr;</a></p>
$endif$
</section> </section>
$endif$ $endif$
$if(fiction-entries)$ $if(fiction-entries)$
<section class="library-section"> <section class="library-section">
<h2 id="fiction"><a href="/fiction/">Fiction</a></h2> <h2 id="fiction"><span class="library-section-ornament" data-ornament="fiction" aria-hidden="true"></span><a href="/fiction/">Fiction</a></h2>
<ul class="item-card-list"> <ul class="item-card-list">
$for(fiction-entries)$ $for(fiction-entries)$
$partial("templates/partials/item-card.html")$ $partial("templates/partials/item-card.html")$
$endfor$ $endfor$
</ul> </ul>
$if(fiction-has-more)$
<p class="library-more"><a href="/fiction/">More on this shelf &rarr;</a></p>
$endif$
</section> </section>
$endif$ $endif$
$if(poetry-entries)$ $if(poetry-entries)$
<section class="library-section"> <section class="library-section">
<h2 id="poetry"><a href="/poetry/">Poetry</a></h2> <h2 id="poetry"><span class="library-section-ornament" data-ornament="poetry" aria-hidden="true"></span><a href="/poetry/">Poetry</a></h2>
<ul class="item-card-list"> <ul class="item-card-list">
$for(poetry-entries)$ $for(poetry-entries)$
$partial("templates/partials/item-card.html")$ $partial("templates/partials/item-card.html")$
$endfor$ $endfor$
</ul> </ul>
$if(poetry-has-more)$
<p class="library-more"><a href="/poetry/">More on this shelf &rarr;</a></p>
$endif$
</section> </section>
$endif$ $endif$
$if(music-entries)$ $if(music-entries)$
<section class="library-section"> <section class="library-section">
<h2 id="music"><a href="/music/">Music</a></h2> <h2 id="music"><span class="library-section-ornament" data-ornament="music" aria-hidden="true"></span><a href="/music/">Music</a></h2>
<ul class="item-card-list"> <ul class="item-card-list">
$for(music-entries)$ $for(music-entries)$
$partial("templates/partials/item-card.html")$ $partial("templates/partials/item-card.html")$
$endfor$ $endfor$
</ul> </ul>
$if(music-has-more)$
<p class="library-more"><a href="/music/">More on this shelf &rarr;</a></p>
$endif$
</section> </section>
$endif$ $endif$
$if(ai-entries)$ $if(ai-entries)$
<section class="library-section"> <section class="library-section">
<h2 id="ai"><a href="/ai/">AI</a></h2> <h2 id="ai"><span class="library-section-ornament" data-ornament="ai" aria-hidden="true"></span><a href="/ai/">AI</a></h2>
<ul class="item-card-list"> <ul class="item-card-list">
$for(ai-entries)$ $for(ai-entries)$
$partial("templates/partials/item-card.html")$ $partial("templates/partials/item-card.html")$
$endfor$ $endfor$
</ul> </ul>
$if(ai-has-more)$
<p class="library-more"><a href="/ai/">More on this shelf &rarr;</a></p>
$endif$
</section> </section>
$endif$ $endif$
$if(tech-entries)$ $if(tech-entries)$
<section class="library-section"> <section class="library-section">
<h2 id="tech"><a href="/tech/">Tech</a></h2> <h2 id="tech"><span class="library-section-ornament" data-ornament="tech" aria-hidden="true"></span><a href="/tech/">Tech</a></h2>
<ul class="item-card-list"> <ul class="item-card-list">
$for(tech-entries)$ $for(tech-entries)$
$partial("templates/partials/item-card.html")$ $partial("templates/partials/item-card.html")$
$endfor$ $endfor$
</ul> </ul>
$if(tech-has-more)$
<p class="library-more"><a href="/tech/">More on this shelf &rarr;</a></p>
$endif$
</section> </section>
$endif$ $endif$
$if(miscellany-entries)$ $if(miscellany-entries)$
<section class="library-section"> <section class="library-section">
<h2 id="miscellany"><a href="/miscellany/">Miscellany</a></h2> <h2 id="miscellany"><span class="library-section-ornament" data-ornament="miscellany" aria-hidden="true"></span><a href="/miscellany/">Miscellany</a></h2>
<ul class="item-card-list"> <ul class="item-card-list">
$for(miscellany-entries)$ $for(miscellany-entries)$
$partial("templates/partials/item-card.html")$ $partial("templates/partials/item-card.html")$
$endfor$ $endfor$
</ul> </ul>
$if(miscellany-has-more)$
<p class="library-more"><a href="/miscellany/">More on this shelf &rarr;</a></p>
$endif$
</section> </section>
$endif$ $endif$