Add fantasyland book
This commit is contained in:
parent
3d11026429
commit
4f735bac3c
3 changed files with 18198 additions and 0 deletions
17605
index.html
Normal file
17605
index.html
Normal file
File diff suppressed because it is too large
Load diff
296
script.js
Normal file
296
script.js
Normal file
|
|
@ -0,0 +1,296 @@
|
||||||
|
const cleanTerm = (text) => text.replace(/\s+/g, " ").replace(/\.$/, "").trim();
|
||||||
|
|
||||||
|
const slugify = (text) =>
|
||||||
|
text
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/[^a-z0-9]+/g, "-")
|
||||||
|
.replace(/^-+|-+$/g, "");
|
||||||
|
|
||||||
|
const entrySpans = Array.from(document.querySelectorAll("span.none2"));
|
||||||
|
const entryMap = new Map();
|
||||||
|
|
||||||
|
entrySpans.forEach((span, index) => {
|
||||||
|
const term = cleanTerm(span.textContent);
|
||||||
|
if (!term) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const id = `entry-${slugify(term)}`;
|
||||||
|
span.setAttribute("id", id);
|
||||||
|
span.classList.add("entry-title");
|
||||||
|
span.style.setProperty("--entry-delay", `${Math.min(index * 0.01, 0.4)}s`);
|
||||||
|
entryMap.set(term, id);
|
||||||
|
});
|
||||||
|
|
||||||
|
const tocList = document.getElementById("toc-list");
|
||||||
|
const tocLetters = document.getElementById("toc-letters");
|
||||||
|
const termEntries = Array.from(entryMap.entries()).sort(([a], [b]) =>
|
||||||
|
a.localeCompare(b),
|
||||||
|
);
|
||||||
|
|
||||||
|
const firstByLetter = new Map();
|
||||||
|
termEntries.forEach(([term, id]) => {
|
||||||
|
const letter = term[0] ? term[0].toUpperCase() : "#";
|
||||||
|
if (!firstByLetter.has(letter)) {
|
||||||
|
firstByLetter.set(letter, id);
|
||||||
|
}
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = `#${id}`;
|
||||||
|
link.textContent = term;
|
||||||
|
const item = document.createElement("li");
|
||||||
|
item.appendChild(link);
|
||||||
|
tocList.appendChild(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
Array.from(firstByLetter.entries())
|
||||||
|
.sort(([a], [b]) => a.localeCompare(b))
|
||||||
|
.forEach(([letter, id]) => {
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = `#${id}`;
|
||||||
|
link.textContent = letter;
|
||||||
|
tocLetters.appendChild(link);
|
||||||
|
});
|
||||||
|
|
||||||
|
const searchInput = document.getElementById("toc-search");
|
||||||
|
searchInput.addEventListener("input", (event) => {
|
||||||
|
const query = event.target.value.trim().toLowerCase();
|
||||||
|
Array.from(tocList.querySelectorAll("li")).forEach((item) => {
|
||||||
|
const text = item.textContent.toLowerCase();
|
||||||
|
item.style.display = text.includes(query) ? "" : "none";
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const specialCaseMap = {
|
||||||
|
"A BARD": "BARDS",
|
||||||
|
"A COURTIER": "COURTIERS",
|
||||||
|
"A DEMON": "DEMONS",
|
||||||
|
"A GODDESS OR GOD": "GODDESSES AND GODS",
|
||||||
|
"A HERBWOMAN": "HERBWOMAN",
|
||||||
|
"A KING'S": "KINGS",
|
||||||
|
"A MAGIC USER": "MAGIC USERS",
|
||||||
|
"A SEER": "SEER",
|
||||||
|
"A WIZARD": "WIZARDS",
|
||||||
|
AMBUSHED: "AMBUSHES",
|
||||||
|
ASSASSIN: "ASSASSINS, GUILD OF",
|
||||||
|
ASSASSINS: "ASSASSINS, GUILD OF",
|
||||||
|
BACKLASHES: "BACKLASH OF MAGIC",
|
||||||
|
BALLAD: "BALLAD, How to Compose a",
|
||||||
|
BALLADS: "BALLAD, How to Compose a",
|
||||||
|
BLOCKED: "MOUNTAIN PASS, BLOCKED",
|
||||||
|
CHILD: "CHILDREN",
|
||||||
|
CURSED: "CURSES",
|
||||||
|
ETERNAL: "ETERNAL QUEST",
|
||||||
|
"EVIL SPELLS": "SPELLS",
|
||||||
|
FEMALE: "SLAVES, FEMALE,",
|
||||||
|
"FELLOW TRAVELLER": "FELLOW TRAVELERS",
|
||||||
|
"FELLOW TRAVELLERS": "FELLOW TRAVELERS",
|
||||||
|
"GAY MA G E": "GAY MAGE",
|
||||||
|
"GIFT OF SIGHT": "SIGHT",
|
||||||
|
GOD: "GODDESSES AND GODS",
|
||||||
|
GODDESS: "GODDESSES AND GODS",
|
||||||
|
"GODDESS OR GOD": "GODDESSES AND GODS",
|
||||||
|
GODS: "GODDESSES AND GODS",
|
||||||
|
"GOOD KING": "KINGS",
|
||||||
|
"GOOD SPY": "SPIES",
|
||||||
|
"GOOD WIZARDS": "WIZARDS",
|
||||||
|
"GUILD OF": "ASSASSINS, GUILD OF",
|
||||||
|
HERBWOMEN: "HERBWOMAN",
|
||||||
|
HERO: "HEROES",
|
||||||
|
"HIDDEN CITY": "HIDDEN KINGDOM",
|
||||||
|
"HOMESPUN ROBES": "ROBES",
|
||||||
|
"HOW TO INTERACT WITH WIZARDS": "WIZARDS",
|
||||||
|
IMPORT: "IMPORT/EXPORT",
|
||||||
|
"JOURNEY CAKE": "WAYBREAD OR JOURNEY CAKE",
|
||||||
|
"KING'": "KINGS",
|
||||||
|
"LOST LAND": "LOST LANDS,",
|
||||||
|
"LOST LANDS": "LOST LANDS,",
|
||||||
|
MAGELIGHT: "MAGELIGHT OR MAGEFIRE",
|
||||||
|
MALE: "SLAVES, MALE,",
|
||||||
|
"MERCHANT'": "MERCHANTS",
|
||||||
|
MINION: "MINIONS OF THE DARK LORD",
|
||||||
|
"MINION OF THE DARK LORD": "MINIONS OF THE DARK LORD",
|
||||||
|
MINIONS: "MINIONS OF THE DARK LORD",
|
||||||
|
"MORE ON RIVERS": "RIVERS",
|
||||||
|
"MORE ON RIVERS RIVERS": "RIVERS",
|
||||||
|
MOON: "MOON(S)",
|
||||||
|
"MOUNTAIN PASS": "MOUNTAIN PASS, BLOCKED",
|
||||||
|
"NORTHERN BARBARIANS DWELL": "NORTHERN BARBARIANS",
|
||||||
|
PANCELTIC: "PANCELTIC TOURS",
|
||||||
|
"PANCELTIC TOUR": "PANCELTIC TOURS",
|
||||||
|
PENTAGRAM: "PENTAGRAM OR PENTACLE",
|
||||||
|
PENTAGRAMS: "PENTAGRAM OR PENTACLE",
|
||||||
|
"POWER-": "POWER",
|
||||||
|
"PRACTICE RING": "PRACTICE RING OR COMBAT RING",
|
||||||
|
PRIESTESSES: "HIGH PRIESTESSES",
|
||||||
|
"SECRET VALLEY": "VALLEYS",
|
||||||
|
RIVERBOAT: "RIVERS",
|
||||||
|
"REEK OF WRONG NESS": "REEK OF WRONGNESS",
|
||||||
|
SLAVE: "GALLEY SLAVE",
|
||||||
|
SLAVES: "SLAVES, MALE,",
|
||||||
|
"STANDING STONES": "STONE CIRCLES",
|
||||||
|
THIEVES: "THIEVES’ GUILD",
|
||||||
|
"TOWN COUNCIL": "COUNCIL",
|
||||||
|
"TOUR COMPANIONS": "COMPANIONS",
|
||||||
|
TROTS: "TROTS, THE",
|
||||||
|
WAYBREAD: "WAYBREAD OR JOURNEY CAKE",
|
||||||
|
"WITCH LIGHT": "WITCHLIGHT",
|
||||||
|
"WIZARD'": "WIZARDS",
|
||||||
|
"WIZARD'S": "WIZARDS",
|
||||||
|
"WIZARD'S BREEDING PROGRAMME": "BREEDING PROGRAMMES",
|
||||||
|
"WIZARD'S STAFF": "STAFFS",
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolveTerm = (term) => {
|
||||||
|
const normalized = term.replace(/’/g, "'");
|
||||||
|
const special = specialCaseMap[normalized];
|
||||||
|
if (special && entryMap.has(special)) {
|
||||||
|
return special;
|
||||||
|
}
|
||||||
|
if (entryMap.has(term)) {
|
||||||
|
return term;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lower = term.toLowerCase();
|
||||||
|
const candidates = [];
|
||||||
|
|
||||||
|
// Singularize
|
||||||
|
if (lower.endsWith("ies")) {
|
||||||
|
candidates.push(term.slice(0, -3) + "Y");
|
||||||
|
}
|
||||||
|
if (lower.endsWith("ves")) {
|
||||||
|
candidates.push(term.slice(0, -3) + "F");
|
||||||
|
candidates.push(term.slice(0, -3) + "FE");
|
||||||
|
}
|
||||||
|
if (lower.endsWith("es")) {
|
||||||
|
candidates.push(term.slice(0, -2));
|
||||||
|
}
|
||||||
|
if (lower.endsWith("s")) {
|
||||||
|
candidates.push(term.slice(0, -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pluralize
|
||||||
|
if (lower.endsWith("y") && !/[aeiou]y$/.test(lower)) {
|
||||||
|
candidates.push(term.slice(0, -1) + "IES");
|
||||||
|
}
|
||||||
|
if (lower.endsWith("f")) {
|
||||||
|
candidates.push(term.slice(0, -1) + "VES");
|
||||||
|
}
|
||||||
|
if (lower.endsWith("fe")) {
|
||||||
|
candidates.push(term.slice(0, -2) + "VES");
|
||||||
|
}
|
||||||
|
candidates.push(`${term}S`);
|
||||||
|
if (/(s|x|z|ch|sh)$/i.test(lower)) {
|
||||||
|
candidates.push(`${term}ES`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return candidates.find((candidate) => entryMap.has(candidate)) || null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const linkifyTextNode = (node) => {
|
||||||
|
const text = node.nodeValue;
|
||||||
|
if (!text || !/[A-Z]{2}/.test(text)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const matches = [];
|
||||||
|
const regex = /\b[A-Z][A-Z0-9'’\-]*(?:\s+[A-Z][A-Z0-9'’\-]*)*\b/g;
|
||||||
|
let match;
|
||||||
|
while ((match = regex.exec(text)) !== null) {
|
||||||
|
const raw = match[0];
|
||||||
|
const cleaned = cleanTerm(raw);
|
||||||
|
const resolved = resolveTerm(cleaned);
|
||||||
|
if (resolved) {
|
||||||
|
matches.push({
|
||||||
|
start: match.index,
|
||||||
|
end: match.index + raw.length,
|
||||||
|
term: resolved,
|
||||||
|
raw,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!matches.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefer longer matches to avoid linking a short term inside a longer one.
|
||||||
|
const byLength = matches
|
||||||
|
.slice()
|
||||||
|
.sort((a, b) => b.end - b.start - (a.end - a.start) || a.start - b.start);
|
||||||
|
|
||||||
|
const selected = [];
|
||||||
|
byLength.forEach((candidate) => {
|
||||||
|
const overlaps = selected.some(
|
||||||
|
(picked) => candidate.start < picked.end && candidate.end > picked.start,
|
||||||
|
);
|
||||||
|
if (!overlaps) {
|
||||||
|
selected.push(candidate);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
selected.sort((a, b) => a.start - b.start);
|
||||||
|
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
let lastIndex = 0;
|
||||||
|
selected.forEach(({ start, end, term }) => {
|
||||||
|
if (start > lastIndex) {
|
||||||
|
fragment.appendChild(
|
||||||
|
document.createTextNode(text.slice(lastIndex, start)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = `#${entryMap.get(term)}`;
|
||||||
|
link.textContent = text.slice(start, end);
|
||||||
|
link.className = "entry-link";
|
||||||
|
fragment.appendChild(link);
|
||||||
|
lastIndex = end;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (lastIndex < text.length) {
|
||||||
|
fragment.appendChild(document.createTextNode(text.slice(lastIndex)));
|
||||||
|
}
|
||||||
|
|
||||||
|
node.parentNode.replaceChild(fragment, node);
|
||||||
|
};
|
||||||
|
|
||||||
|
const walker = document.createTreeWalker(
|
||||||
|
document.getElementById("content"),
|
||||||
|
NodeFilter.SHOW_TEXT,
|
||||||
|
{
|
||||||
|
acceptNode: (node) => {
|
||||||
|
const parent = node.parentElement;
|
||||||
|
if (!parent) {
|
||||||
|
return NodeFilter.FILTER_REJECT;
|
||||||
|
}
|
||||||
|
if (parent.closest("a, script, style")) {
|
||||||
|
return NodeFilter.FILTER_REJECT;
|
||||||
|
}
|
||||||
|
if (parent.closest("span.none2")) {
|
||||||
|
return NodeFilter.FILTER_REJECT;
|
||||||
|
}
|
||||||
|
return NodeFilter.FILTER_ACCEPT;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const nodesToLinkify = [];
|
||||||
|
while (walker.nextNode()) {
|
||||||
|
nodesToLinkify.push(walker.currentNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
nodesToLinkify.forEach((node) => linkifyTextNode(node));
|
||||||
|
|
||||||
|
// TOC collapse/expand toggle
|
||||||
|
const tocToggle = document.createElement("button");
|
||||||
|
tocToggle.className = "toc-toggle";
|
||||||
|
tocToggle.setAttribute("aria-label", "Toggle sidebar");
|
||||||
|
tocToggle.setAttribute("title", "Toggle sidebar");
|
||||||
|
document.body.appendChild(tocToggle);
|
||||||
|
|
||||||
|
const toc = document.querySelector(".toc");
|
||||||
|
const layout = document.querySelector(".layout");
|
||||||
|
|
||||||
|
tocToggle.addEventListener("click", () => {
|
||||||
|
toc.classList.toggle("collapsed");
|
||||||
|
layout.classList.toggle("toc-hidden");
|
||||||
|
document.body.classList.toggle("toc-collapsed");
|
||||||
|
});
|
||||||
297
style.css
Normal file
297
style.css
Normal file
|
|
@ -0,0 +1,297 @@
|
||||||
|
:root {
|
||||||
|
--ink: #251f1a;
|
||||||
|
--ink-soft: #4b3f34;
|
||||||
|
--paper: #f5efe6;
|
||||||
|
--paper-deep: #e9ddcc;
|
||||||
|
--accent: #b6522f;
|
||||||
|
--accent-deep: #7a2e1a;
|
||||||
|
--nav-bg: #efe4d6;
|
||||||
|
--highlight: #f3c98b;
|
||||||
|
--shadow: rgba(45, 34, 26, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--ink);
|
||||||
|
background:
|
||||||
|
radial-gradient(
|
||||||
|
circle at 20% 20%,
|
||||||
|
rgba(255, 255, 255, 0.55),
|
||||||
|
transparent 45%
|
||||||
|
),
|
||||||
|
radial-gradient(
|
||||||
|
circle at 80% 0%,
|
||||||
|
rgba(236, 196, 140, 0.35),
|
||||||
|
transparent 50%
|
||||||
|
),
|
||||||
|
linear-gradient(135deg, #fbf7f2 0%, #efe0cb 50%, #f8f1e7 100%);
|
||||||
|
font-family: "Avenir Next", "Gill Sans", "Trebuchet MS", sans-serif;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero {
|
||||||
|
padding: 48px 6vw 32px;
|
||||||
|
background:
|
||||||
|
linear-gradient(120deg, rgba(182, 82, 47, 0.12), transparent 55%),
|
||||||
|
linear-gradient(220deg, rgba(122, 46, 26, 0.18), transparent 45%);
|
||||||
|
border-bottom: 1px solid rgba(63, 44, 31, 0.2);
|
||||||
|
animation: fade-up 0.8s ease both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero h1 {
|
||||||
|
font-family:
|
||||||
|
"Iowan Old Style", "Palatino Linotype", "Book Antiqua", Garamond, serif;
|
||||||
|
font-size: clamp(2.4rem, 4vw, 3.6rem);
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
margin: 0 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero p {
|
||||||
|
margin: 0;
|
||||||
|
max-width: 54ch;
|
||||||
|
color: var(--ink-soft);
|
||||||
|
font-size: 1.05rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(240px, 320px) minmax(0, 1fr);
|
||||||
|
gap: 28px;
|
||||||
|
padding: 32px 6vw 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc {
|
||||||
|
background: var(--nav-bg);
|
||||||
|
border-radius: 18px;
|
||||||
|
padding: 24px;
|
||||||
|
box-shadow: 0 18px 40px var(--shadow);
|
||||||
|
position: sticky;
|
||||||
|
top: 24px;
|
||||||
|
max-height: calc(100vh - 48px);
|
||||||
|
overflow: auto;
|
||||||
|
animation: fade-up 0.8s ease 0.1s both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc h2 {
|
||||||
|
margin-top: 0;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--accent-deep);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 1px solid rgba(70, 50, 36, 0.2);
|
||||||
|
font-size: 0.95rem;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
background: #fffaf3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-letters {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 6px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-letters a {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--accent-deep);
|
||||||
|
padding: 4px 6px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: rgba(182, 82, 47, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-letters a:hover {
|
||||||
|
background: rgba(182, 82, 47, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-list {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
display: grid;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-list a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--ink);
|
||||||
|
padding: 6px 8px;
|
||||||
|
border-radius: 10px;
|
||||||
|
display: block;
|
||||||
|
transition:
|
||||||
|
background 0.2s ease,
|
||||||
|
color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-list a:hover,
|
||||||
|
.toc-list a:focus {
|
||||||
|
background: rgba(182, 82, 47, 0.15);
|
||||||
|
color: var(--accent-deep);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
background: rgba(255, 255, 255, 0.6);
|
||||||
|
border-radius: 24px;
|
||||||
|
padding: 32px 36px;
|
||||||
|
box-shadow: 0 20px 55px var(--shadow);
|
||||||
|
animation: fade-up 0.8s ease 0.2s both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content h2,
|
||||||
|
.content h3 {
|
||||||
|
font-family:
|
||||||
|
"Iowan Old Style", "Palatino Linotype", "Book Antiqua", Garamond, serif;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
color: var(--accent-deep);
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry-title {
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--accent-deep);
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
animation: fade-up 0.6s ease both;
|
||||||
|
animation-delay: var(--entry-delay, 0s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry-title:target {
|
||||||
|
background: var(--highlight);
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry-link {
|
||||||
|
color: var(--accent);
|
||||||
|
text-decoration: none;
|
||||||
|
border-bottom: 1px dashed rgba(122, 46, 26, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry-link:hover {
|
||||||
|
color: var(--accent-deep);
|
||||||
|
border-bottom-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note {
|
||||||
|
font-style: italic;
|
||||||
|
color: var(--ink-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-up {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(16px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Collapse/expand toggle */
|
||||||
|
.toc-toggle {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
z-index: 100;
|
||||||
|
background: var(--accent);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0 8px 8px 0;
|
||||||
|
padding: 12px 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
box-shadow: 2px 2px 8px var(--shadow);
|
||||||
|
transition:
|
||||||
|
background 0.2s ease,
|
||||||
|
left 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-toggle:hover {
|
||||||
|
background: var(--accent-deep);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-toggle::before {
|
||||||
|
content: "◀";
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc.collapsed + .content ~ .toc-toggle::before,
|
||||||
|
body.toc-collapsed .toc-toggle::before {
|
||||||
|
content: "▶";
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc {
|
||||||
|
transition:
|
||||||
|
margin-left 0.3s ease,
|
||||||
|
opacity 0.3s ease,
|
||||||
|
width 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc.collapsed {
|
||||||
|
margin-left: -340px;
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout {
|
||||||
|
transition: grid-template-columns 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout.toc-hidden {
|
||||||
|
grid-template-columns: 0 minmax(0, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 980px) {
|
||||||
|
.layout {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc {
|
||||||
|
position: relative;
|
||||||
|
max-height: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc.collapsed {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-top: -100%;
|
||||||
|
height: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout.toc-hidden {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-toggle {
|
||||||
|
top: auto;
|
||||||
|
bottom: 20px;
|
||||||
|
left: 20px;
|
||||||
|
transform: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-toggle::before {
|
||||||
|
content: "▲";
|
||||||
|
}
|
||||||
|
|
||||||
|
body.toc-collapsed .toc-toggle::before {
|
||||||
|
content: "▼";
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue