Fix stack cover: single sticky folder unit, hide inactive bodies.
Tested all four variants in browser — only active layer body visible while tabs stay staggered; scroll covers previous layers correctly. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
bb8469ca10
commit
cfe1cf3922
48
package-lock.json
generated
48
package-lock.json
generated
@ -8,6 +8,7 @@
|
|||||||
"name": "levkin.ca",
|
"name": "levkin.ca",
|
||||||
"version": "0.3.0",
|
"version": "0.3.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"playwright": "^1.60.0",
|
||||||
"vite": "^6.3.5"
|
"vite": "^6.3.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -963,6 +964,53 @@
|
|||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/playwright": {
|
||||||
|
"version": "1.60.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.60.0.tgz",
|
||||||
|
"integrity": "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"playwright-core": "1.60.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"playwright": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/playwright-core": {
|
||||||
|
"version": "1.60.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.60.0.tgz",
|
||||||
|
"integrity": "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bin": {
|
||||||
|
"playwright-core": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/playwright/node_modules/fsevents": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.5.15",
|
"version": "8.5.15",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"playwright": "^1.60.0",
|
||||||
"vite": "^6.3.5"
|
"vite": "^6.3.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
34
scripts/debug-sticky.mjs
Normal file
34
scripts/debug-sticky.mjs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { chromium } from 'playwright';
|
||||||
|
|
||||||
|
const browser = await chromium.launch({ headless: true });
|
||||||
|
const page = await browser.newPage({ viewport: { width: 1280, height: 800 } });
|
||||||
|
await page.goto('http://localhost:5176/stack-folder/', { waitUntil: 'networkidle' });
|
||||||
|
|
||||||
|
for (const y of [0, 400, 800, 1200]) {
|
||||||
|
await page.evaluate((sy) => window.scrollTo(0, sy), y);
|
||||||
|
await page.waitForTimeout(150);
|
||||||
|
const data = await page.evaluate(() => {
|
||||||
|
return [...document.querySelectorAll('.folder')].map((f, i) => {
|
||||||
|
const cs = getComputedStyle(f);
|
||||||
|
const r = f.getBoundingClientRect();
|
||||||
|
const body = f.querySelector('.body');
|
||||||
|
const br = body.getBoundingClientRect();
|
||||||
|
const sec = f.closest('.scroll-section');
|
||||||
|
const sr = sec.getBoundingClientRect();
|
||||||
|
return {
|
||||||
|
i,
|
||||||
|
folderTop: Math.round(r.top),
|
||||||
|
bodyTop: Math.round(br.top),
|
||||||
|
position: cs.position,
|
||||||
|
top: cs.top,
|
||||||
|
zIndex: f.style.zIndex || cs.zIndex,
|
||||||
|
sectionTop: Math.round(sr.top),
|
||||||
|
sectionBottom: Math.round(sr.bottom),
|
||||||
|
sectionH: Math.round(sr.height),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
console.log('scroll', y, JSON.stringify(data.filter((d) => d.sectionBottom > 0 && d.sectionTop < 800), null, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
await browser.close();
|
||||||
91
scripts/test-stack.mjs
Normal file
91
scripts/test-stack.mjs
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import { chromium } from 'playwright';
|
||||||
|
import { writeFileSync, mkdirSync } from 'fs';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
|
const BASE = process.env.BASE_URL || 'http://localhost:5176';
|
||||||
|
const OUT = '/tmp/levkin-stack-test';
|
||||||
|
mkdirSync(OUT, { recursive: true });
|
||||||
|
|
||||||
|
const pages = [
|
||||||
|
{ name: 'stack', path: '/stack/' },
|
||||||
|
{ name: 'stack-folder', path: '/stack-folder/' },
|
||||||
|
{ name: 'stack-trace', path: '/stack-trace/' },
|
||||||
|
{ name: 'stack-rack', path: '/stack-rack/' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const scrollY = [0, 400, 800, 1200, 1800, 2400];
|
||||||
|
|
||||||
|
async function analyze(page, label) {
|
||||||
|
return page.evaluate(() => {
|
||||||
|
const sections = [...document.querySelectorAll('.scroll-section')];
|
||||||
|
const bodies = [...document.querySelectorAll('.body, .layer-inner, .frame-body, .unit-body')];
|
||||||
|
const tabs = [...document.querySelectorAll('.tab, .frame-line, .unit-head')];
|
||||||
|
const sticky = [...document.querySelectorAll('.tab, .body, .layer-inner, .frame-line, .frame-body, .unit-head, .unit-body')];
|
||||||
|
const rects = (els) => els.map((el, i) => {
|
||||||
|
const r = el.getBoundingClientRect();
|
||||||
|
const cs = getComputedStyle(el);
|
||||||
|
return {
|
||||||
|
i,
|
||||||
|
tag: el.className?.slice?.(0, 40) || el.tagName,
|
||||||
|
top: Math.round(r.top),
|
||||||
|
bottom: Math.round(r.bottom),
|
||||||
|
height: Math.round(r.height),
|
||||||
|
position: cs.position,
|
||||||
|
zIndex: cs.zIndex,
|
||||||
|
visible: r.height > 0 && r.bottom > 0 && r.top < innerHeight,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const visibleBodies = rects(bodies).filter((b) => b.visible && b.height > 80);
|
||||||
|
const mount = document.querySelector('.mount, .stack-mount');
|
||||||
|
const mountH = mount ? Math.round(mount.getBoundingClientRect().height) : 0;
|
||||||
|
return {
|
||||||
|
scrollY: Math.round(window.scrollY),
|
||||||
|
pageH: Math.round(document.documentElement.scrollHeight),
|
||||||
|
viewport: innerHeight,
|
||||||
|
sections: sections.length,
|
||||||
|
visibleBodyCount: visibleBodies.length,
|
||||||
|
visibleBodies,
|
||||||
|
mountDocHeight: mountH,
|
||||||
|
firstSectionTop: sections[0] ? Math.round(sections[0].getBoundingClientRect().top) : null,
|
||||||
|
lastSectionBottom: sections.at(-1) ? Math.round(sections.at(-1).getBoundingClientRect().bottom) : null,
|
||||||
|
stickyPositions: rects(sticky.filter((el) => getComputedStyle(el).position === 'sticky')).slice(0, 14),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const browser = await chromium.launch({ headless: true });
|
||||||
|
const report = [];
|
||||||
|
|
||||||
|
for (const { name, path } of pages) {
|
||||||
|
const page = await browser.newPage({ viewport: { width: 1280, height: 800 } });
|
||||||
|
const url = BASE + path;
|
||||||
|
await page.goto(url, { waitUntil: 'networkidle' });
|
||||||
|
const shots = [];
|
||||||
|
for (const y of scrollY) {
|
||||||
|
await page.evaluate((sy) => window.scrollTo(0, sy), y);
|
||||||
|
await page.waitForTimeout(200);
|
||||||
|
const data = await analyze(page, name);
|
||||||
|
const file = join(OUT, `${name}-scroll-${y}.png`);
|
||||||
|
await page.screenshot({ path: file, fullPage: false });
|
||||||
|
shots.push({ y, file, ...data });
|
||||||
|
}
|
||||||
|
report.push({ name, url, shots });
|
||||||
|
await page.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
await browser.close();
|
||||||
|
|
||||||
|
const summary = report.map((r) => {
|
||||||
|
const problems = r.shots.map((s) => {
|
||||||
|
const issues = [];
|
||||||
|
if (s.visibleBodyCount > 2) issues.push(`${s.visibleBodyCount} bodies visible (want ≤2)`);
|
||||||
|
if (s.scrollY === 0 && s.visibleBodyCount > 1) issues.push('top: multiple full bodies');
|
||||||
|
if (s.pageH > s.viewport * 8) issues.push(`page very tall: ${s.pageH}px`);
|
||||||
|
return { y: s.y, issues, visibleBodyCount: s.visibleBodyCount, pageH: s.pageH };
|
||||||
|
});
|
||||||
|
return { variant: r.name, url: r.url, scrollChecks: problems };
|
||||||
|
});
|
||||||
|
|
||||||
|
writeFileSync(join(OUT, 'report.json'), JSON.stringify({ report, summary }, null, 2));
|
||||||
|
console.log(JSON.stringify(summary, null, 2));
|
||||||
|
console.log('\nScreenshots:', OUT);
|
||||||
@ -18,3 +18,18 @@
|
|||||||
height: 2rem;
|
height: 2rem;
|
||||||
margin-bottom: 3rem;
|
margin-bottom: 3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Only the active layer shows its body; tabs always visible */
|
||||||
|
.scroll-section:not(.is-active) .body,
|
||||||
|
.scroll-section:not(.is-active) .layer-inner,
|
||||||
|
.scroll-section:not(.is-active) .frame-body,
|
||||||
|
.scroll-section:not(.is-active) .unit-body {
|
||||||
|
visibility: hidden;
|
||||||
|
height: 0;
|
||||||
|
min-height: 0;
|
||||||
|
margin-top: 0 !important;
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ export function initStackScroll(options = {}) {
|
|||||||
depthEl = document.getElementById('depth'),
|
depthEl = document.getElementById('depth'),
|
||||||
depthPrefix = 'L',
|
depthPrefix = 'L',
|
||||||
tabSelector = '[data-goto], .jump',
|
tabSelector = '[data-goto], .jump',
|
||||||
bodySelector = '.body, .layer-inner, .frame-body, .unit-body',
|
panelSelector = '.folder, .layer, .frame, .unit',
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
const sections = document.querySelectorAll(sectionSelector);
|
const sections = document.querySelectorAll(sectionSelector);
|
||||||
@ -29,9 +29,11 @@ export function initStackScroll(options = {}) {
|
|||||||
|
|
||||||
sections.forEach((sec) => {
|
sections.forEach((sec) => {
|
||||||
const layer = Number(sec.dataset.layer);
|
const layer = Number(sec.dataset.layer);
|
||||||
const body = sec.querySelector(bodySelector);
|
const isActive = layer === active;
|
||||||
if (!body) return;
|
sec.classList.toggle('is-active', isActive);
|
||||||
body.style.zIndex = layer === active ? 100 : 10 + layer;
|
const panel = sec.querySelector(panelSelector);
|
||||||
|
if (!panel) return;
|
||||||
|
panel.style.zIndex = isActive ? 100 : 10 + layer;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,11 +3,10 @@
|
|||||||
--stack-nav: 2.5rem;
|
--stack-nav: 2.5rem;
|
||||||
--stack-stick: 3rem;
|
--stack-stick: 3rem;
|
||||||
--stack-step: 2.75rem;
|
--stack-step: 2.75rem;
|
||||||
--stack-tab-h: 2.25rem;
|
--stack-tab-h: 2rem;
|
||||||
/* Height of tab rail (all L0–L6 tabs visible) */
|
|
||||||
--stack-reveal: calc(var(--stack-stick) + var(--stack-step) * 6 + var(--stack-tab-h));
|
--stack-reveal: calc(var(--stack-stick) + var(--stack-step) * 6 + var(--stack-tab-h));
|
||||||
--stack-slot: 100vh;
|
--stack-slot: 100vh;
|
||||||
--stack-slot-last: 50vh;
|
--stack-slot-last: 50vh;
|
||||||
--stack-pull: calc(var(--stack-slot) - var(--stack-reveal));
|
--stack-pull: calc(var(--stack-slot) - var(--stack-reveal));
|
||||||
--stack-body-h: calc(100dvh - var(--stack-reveal) - 1rem);
|
--stack-body-h: calc(100dvh - var(--stack-reveal) - 1.25rem);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -77,7 +77,7 @@
|
|||||||
<table class="rfc-meta">
|
<table class="rfc-meta">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr><th scope="row">Status</th><td><span class="badge">ACTIVE</span></td></tr>
|
<tr><th scope="row">Status</th><td><span class="badge">ACTIVE</span></td></tr>
|
||||||
<tr><th scope="row">Entity</th><td>Levkin</td></tr>
|
<tr><th scope="row">Entity</th><td>Levkin Inc.</td></tr>
|
||||||
<tr><th scope="row">Domain</th><td>levkin.ca</td></tr>
|
<tr><th scope="row">Domain</th><td>levkin.ca</td></tr>
|
||||||
<tr><th scope="row">Updated</th><td><time datetime="2026-05-20">2026-05-20</time></td></tr>
|
<tr><th scope="row">Updated</th><td><time datetime="2026-05-20">2026-05-20</time></td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
@ -227,13 +227,14 @@
|
|||||||
<span class="contact-path">/consult</span>
|
<span class="contact-path">/consult</span>
|
||||||
<span class="desc">15-minute discovery call<span class="sr-only"> (opens cal.levkin.ca)</span></span>
|
<span class="desc">15-minute discovery call<span class="sr-only"> (opens cal.levkin.ca)</span></span>
|
||||||
</a>
|
</a>
|
||||||
<a class="contact-card" href="mailto:hello@levkine.ca?subject=Project%20enquiry">
|
<a class="contact-card" href="mailto:ilia@levkine.ca?subject=Project%20enquiry">
|
||||||
<span class="method post" aria-hidden="true">POST</span>
|
<span class="method post" aria-hidden="true">POST</span>
|
||||||
<span class="contact-path">/email</span>
|
<span class="contact-path">/email</span>
|
||||||
<span class="desc">hello@levkine.ca</span>
|
<span class="desc">ilia@levkine.ca</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<p class="copyright">© Levkin · Canadian software development</p>
|
<p class="copyright">© Levkin · Canadian software development</p>
|
||||||
|
<a href="#main" class="back-top">↑ Back to top</a>
|
||||||
</section>
|
</section>
|
||||||
</article>
|
</article>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
243
spec/spec.css
243
spec/spec.css
@ -1,66 +1,102 @@
|
|||||||
/* --- themes --- */
|
/* --- themes (semantic tokens — no blue-on-blue panels) --- */
|
||||||
:root,
|
:root,
|
||||||
[data-theme="light"] {
|
[data-theme="light"] {
|
||||||
color-scheme: light;
|
color-scheme: light;
|
||||||
--paper: #f4f1ec;
|
--paper: #f6f3ee;
|
||||||
--surface: #ffffff;
|
--surface: #fffefb;
|
||||||
--ink: #1a1a18;
|
--panel: #ebe8e2;
|
||||||
--muted: #4a4844;
|
--ink: #121211;
|
||||||
--rule: #c8c2b8;
|
--muted: #3d3b37;
|
||||||
--accent: #1e4a72;
|
--rule: #c5bfb4;
|
||||||
--accent-hover: #163a5c;
|
--link: #0b4a75;
|
||||||
--accent-bg: #e4ecf4;
|
--link-hover: #083558;
|
||||||
--link: #1e4a72;
|
--path: #0b4a75;
|
||||||
--link-hover: #163a5c;
|
--nav-active: #e4e0d8;
|
||||||
|
--nav-active-border: #0b4a75;
|
||||||
|
--code-bg: #e8e5df;
|
||||||
|
--code-fg: #1a2e28;
|
||||||
|
--table-head: #e4e0d8;
|
||||||
--badge-ok-bg: #c8e6ce;
|
--badge-ok-bg: #c8e6ce;
|
||||||
--badge-ok-fg: #14532d;
|
--badge-ok-fg: #0f3d1a;
|
||||||
--badge-post-bg: #f5dcc8;
|
--badge-ok-border: #6b9e74;
|
||||||
--badge-post-fg: #6b3410;
|
--badge-post-bg: #f0e0d0;
|
||||||
--focus: #1e4a72;
|
--badge-post-fg: #5a2e0a;
|
||||||
--skip-bg: #1a1a18;
|
--badge-post-border: #c49a6a;
|
||||||
--skip-fg: #f4f1ec;
|
--focus: #0b4a75;
|
||||||
|
--skip-bg: #121211;
|
||||||
|
--skip-fg: #f6f3ee;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="dim"] {
|
[data-theme="dim"] {
|
||||||
color-scheme: light;
|
color-scheme: light;
|
||||||
--paper: #ddd8cf;
|
--paper: #c9c3b8;
|
||||||
--surface: #ece8e0;
|
--surface: #e3ded4;
|
||||||
--ink: #1f1d1a;
|
--panel: #d6d0c6;
|
||||||
--muted: #45423c;
|
--ink: #12110f;
|
||||||
--rule: #b5aea2;
|
--muted: #363430;
|
||||||
--accent: #2a5078;
|
--rule: #a39d92;
|
||||||
--accent-hover: #1e3d5c;
|
--link: #094264;
|
||||||
--accent-bg: #d4dde8;
|
--link-hover: #062f48;
|
||||||
--link: #2a5078;
|
--path: #094264;
|
||||||
--link-hover: #1e3d5c;
|
--nav-active: #bab4a8;
|
||||||
--badge-ok-bg: #b8d4be;
|
--nav-active-border: #5c584f;
|
||||||
--badge-ok-fg: #14532d;
|
--code-bg: #d8d2c8;
|
||||||
--badge-post-bg: #e8cdb8;
|
--code-fg: #1a2822;
|
||||||
--badge-post-fg: #5c3010;
|
--table-head: #d0cac0;
|
||||||
--focus: #2a5078;
|
--badge-ok-bg: #a8c8ae;
|
||||||
--skip-bg: #1f1d1a;
|
--badge-ok-fg: #0a3318;
|
||||||
--skip-fg: #ece8e0;
|
--badge-ok-border: #4a7a54;
|
||||||
|
--badge-post-bg: #dcc8b4;
|
||||||
|
--badge-post-fg: #4a2808;
|
||||||
|
--badge-post-border: #a07850;
|
||||||
|
--focus: #094264;
|
||||||
|
--skip-bg: #12110f;
|
||||||
|
--skip-fg: #e3ded4;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="dark"] {
|
[data-theme="dark"] {
|
||||||
color-scheme: dark;
|
color-scheme: dark;
|
||||||
--paper: #141412;
|
--paper: #10100f;
|
||||||
--surface: #1e1e1c;
|
--surface: #1a1a18;
|
||||||
--ink: #ece9e4;
|
--panel: #242422;
|
||||||
--muted: #b8b4ac;
|
--ink: #eeece8;
|
||||||
|
--muted: #c4c0b8;
|
||||||
--rule: #3a3834;
|
--rule: #3a3834;
|
||||||
--accent: #7eb8e8;
|
--link: #7ec8f4;
|
||||||
--accent-hover: #a8d4f8;
|
--link-hover: #a8e0ff;
|
||||||
--accent-bg: #243040;
|
--path: #9ed4f8;
|
||||||
--link: #8ec8f0;
|
--nav-active: #2a2a28;
|
||||||
--link-hover: #b8e0ff;
|
--nav-active-border: #7ec8f4;
|
||||||
--badge-ok-bg: #1e4a2e;
|
--code-bg: #1e1e1c;
|
||||||
--badge-ok-fg: #a8e8b8;
|
--code-fg: #c0d8cc;
|
||||||
--badge-post-bg: #4a3020;
|
--table-head: #242422;
|
||||||
--badge-post-fg: #f0d0b0;
|
--badge-ok-bg: #1a3d28;
|
||||||
--focus: #8ec8f0;
|
--badge-ok-fg: #b8e8c4;
|
||||||
--skip-bg: #ece9e4;
|
--badge-ok-border: #4a8a5c;
|
||||||
--skip-fg: #141412;
|
--badge-post-bg: #3d2a1c;
|
||||||
|
--badge-post-fg: #f0d8c0;
|
||||||
|
--badge-post-border: #8a6a48;
|
||||||
|
--focus: #7ec8f4;
|
||||||
|
--skip-bg: #eeece8;
|
||||||
|
--skip-fg: #10100f;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-contrast: more) {
|
||||||
|
:root,
|
||||||
|
[data-theme="light"],
|
||||||
|
[data-theme="dim"] {
|
||||||
|
--muted: #1a1816;
|
||||||
|
--rule: #5a5650;
|
||||||
|
--badge-ok-border: #0f3d1a;
|
||||||
|
--badge-post-border: #5a2e0a;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] {
|
||||||
|
--muted: #eeece8;
|
||||||
|
--rule: #8a8680;
|
||||||
|
--badge-ok-border: #b8e8c4;
|
||||||
|
--badge-post-border: #f0d8c0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
@ -137,6 +173,7 @@ input:focus-visible,
|
|||||||
|
|
||||||
a {
|
a {
|
||||||
color: var(--link);
|
color: var(--link);
|
||||||
|
text-decoration: underline;
|
||||||
text-decoration-thickness: 1px;
|
text-decoration-thickness: 1px;
|
||||||
text-underline-offset: 0.15em;
|
text-underline-offset: 0.15em;
|
||||||
}
|
}
|
||||||
@ -145,6 +182,19 @@ a:hover {
|
|||||||
color: var(--link-hover);
|
color: var(--link-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.rfc p a,
|
||||||
|
.spec-table a,
|
||||||
|
.endpoint a {
|
||||||
|
text-decoration-thickness: 1.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc nav a,
|
||||||
|
.contact-card,
|
||||||
|
.back,
|
||||||
|
.theme-option span {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 800px) {
|
@media (max-width: 800px) {
|
||||||
body { grid-template-columns: 1fr; }
|
body { grid-template-columns: 1fr; }
|
||||||
.toc {
|
.toc {
|
||||||
@ -180,7 +230,7 @@ a:hover {
|
|||||||
line-height: 2.75rem;
|
line-height: 2.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.back:hover { color: var(--accent); }
|
.back:hover { color: var(--link); }
|
||||||
|
|
||||||
.toc-title {
|
.toc-title {
|
||||||
font-size: 0.65rem;
|
font-size: 0.65rem;
|
||||||
@ -207,12 +257,14 @@ a:hover {
|
|||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toc nav a:hover { color: var(--accent); }
|
.toc nav a:hover { color: var(--link); }
|
||||||
|
|
||||||
.toc nav a.is-active {
|
.toc nav a.is-active {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--accent);
|
color: var(--ink);
|
||||||
background: var(--accent-bg);
|
background: var(--nav-active);
|
||||||
|
padding: 0.35rem 0.5rem;
|
||||||
|
box-shadow: inset 0 0 0 1px var(--rule);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* preferences */
|
/* preferences */
|
||||||
@ -275,10 +327,10 @@ a:hover {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.theme-option input:checked + span {
|
.theme-option input:checked + span {
|
||||||
border-color: var(--accent);
|
border-color: var(--nav-active-border);
|
||||||
background: var(--accent-bg);
|
background: var(--nav-active);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--accent);
|
color: var(--ink);
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme-option input:focus-visible + span {
|
.theme-option input:focus-visible + span {
|
||||||
@ -318,8 +370,8 @@ a:hover {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.font-btn:hover:not(:disabled) {
|
.font-btn:hover:not(:disabled) {
|
||||||
background: var(--accent-bg);
|
background: var(--nav-active);
|
||||||
border-color: var(--accent);
|
border-color: var(--nav-active-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.font-btn:disabled {
|
.font-btn:disabled {
|
||||||
@ -395,12 +447,14 @@ a:hover {
|
|||||||
padding: 0.15rem 0.45rem;
|
padding: 0.15rem 0.45rem;
|
||||||
background: var(--badge-ok-bg);
|
background: var(--badge-ok-bg);
|
||||||
color: var(--badge-ok-fg);
|
color: var(--badge-ok-fg);
|
||||||
|
border: 1px solid var(--badge-ok-border);
|
||||||
letter-spacing: 0.04em;
|
letter-spacing: 0.04em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge.muted {
|
.badge.muted {
|
||||||
background: var(--rule);
|
background: var(--panel);
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
|
border-color: var(--rule);
|
||||||
}
|
}
|
||||||
|
|
||||||
section {
|
section {
|
||||||
@ -422,7 +476,7 @@ section h2 {
|
|||||||
section p { margin-bottom: 0.75rem; color: var(--ink); }
|
section p { margin-bottom: 0.75rem; color: var(--ink); }
|
||||||
|
|
||||||
.code-block {
|
.code-block {
|
||||||
background: var(--accent-bg);
|
background: var(--code-bg);
|
||||||
border: 1px solid var(--rule);
|
border: 1px solid var(--rule);
|
||||||
padding: 1rem 1.25rem;
|
padding: 1rem 1.25rem;
|
||||||
margin: 1rem 0;
|
margin: 1rem 0;
|
||||||
@ -433,7 +487,7 @@ section p { margin-bottom: 0.75rem; color: var(--ink); }
|
|||||||
font-family: var(--mono);
|
font-family: var(--mono);
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
color: var(--accent);
|
color: var(--code-fg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.endpoint {
|
.endpoint {
|
||||||
@ -448,7 +502,7 @@ section p { margin-bottom: 0.75rem; color: var(--ink); }
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.5rem 0.75rem;
|
gap: 0.5rem 0.75rem;
|
||||||
padding: 0.75rem 1rem;
|
padding: 0.75rem 1rem;
|
||||||
background: var(--accent-bg);
|
background: var(--panel);
|
||||||
border-bottom: 1px solid var(--rule);
|
border-bottom: 1px solid var(--rule);
|
||||||
font-family: var(--mono);
|
font-family: var(--mono);
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
@ -458,11 +512,12 @@ section p { margin-bottom: 0.75rem; color: var(--ink); }
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--badge-ok-fg);
|
color: var(--badge-ok-fg);
|
||||||
background: var(--badge-ok-bg);
|
background: var(--badge-ok-bg);
|
||||||
|
border: 1px solid var(--badge-ok-border);
|
||||||
padding: 0.15rem 0.4rem;
|
padding: 0.15rem 0.4rem;
|
||||||
font-size: 0.7rem;
|
font-size: 0.7rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.path { color: var(--accent); font-weight: 500; }
|
.path { color: var(--path); font-weight: 500; }
|
||||||
|
|
||||||
.ext { margin-left: auto; font-size: 0.75rem; }
|
.ext { margin-left: auto; font-size: 0.75rem; }
|
||||||
.ext a { color: var(--muted); }
|
.ext a { color: var(--muted); }
|
||||||
@ -504,14 +559,14 @@ section p { margin-bottom: 0.75rem; color: var(--ink); }
|
|||||||
.spec-table th {
|
.spec-table th {
|
||||||
font-family: var(--mono);
|
font-family: var(--mono);
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
background: var(--accent-bg);
|
background: var(--table-head);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.spec-table code {
|
.spec-table code {
|
||||||
font-family: var(--mono);
|
font-family: var(--mono);
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
color: var(--accent);
|
color: var(--code-fg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.spec-table a { color: var(--link); }
|
.spec-table a { color: var(--link); }
|
||||||
@ -543,18 +598,32 @@ section p { margin-bottom: 0.75rem; color: var(--ink); }
|
|||||||
|
|
||||||
.contact-card:hover,
|
.contact-card:hover,
|
||||||
.contact-card:focus-visible {
|
.contact-card:focus-visible {
|
||||||
background: var(--accent-bg);
|
background: var(--panel);
|
||||||
}
|
}
|
||||||
|
|
||||||
.method.post {
|
.method.post {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--badge-post-fg);
|
color: var(--badge-post-fg);
|
||||||
background: var(--badge-post-bg);
|
background: var(--badge-post-bg);
|
||||||
|
border: 1px solid var(--badge-post-border);
|
||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
padding: 0.1rem 0.35rem;
|
padding: 0.1rem 0.35rem;
|
||||||
font-size: 0.65rem;
|
font-size: 0.65rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.back-top {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
font-family: var(--mono);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--muted);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-top:hover {
|
||||||
|
color: var(--link);
|
||||||
|
}
|
||||||
|
|
||||||
.contact-card .desc {
|
.contact-card .desc {
|
||||||
font-family: var(--serif);
|
font-family: var(--serif);
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
@ -566,3 +635,43 @@ section p { margin-bottom: 0.75rem; color: var(--ink); }
|
|||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
.skip-link,
|
||||||
|
.prefs,
|
||||||
|
.back {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
display: block;
|
||||||
|
background: #fff;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc {
|
||||||
|
position: static;
|
||||||
|
height: auto;
|
||||||
|
border: none;
|
||||||
|
padding: 0 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc nav a.is-active {
|
||||||
|
font-weight: 700;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #000;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rfc {
|
||||||
|
max-width: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
break-inside: avoid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
15
spec/spec.js
15
spec/spec.js
@ -19,9 +19,22 @@ function fontIndexFromScale(scale) {
|
|||||||
return idx === -1 ? DEFAULT_FONT_INDEX : idx;
|
return idx === -1 ? DEFAULT_FONT_INDEX : idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const THEME_COLORS = {
|
||||||
|
light: '#f6f3ee',
|
||||||
|
dim: '#c9c3b8',
|
||||||
|
dark: '#10100f',
|
||||||
|
};
|
||||||
|
|
||||||
function applyTheme(theme) {
|
function applyTheme(theme) {
|
||||||
document.documentElement.dataset.theme = theme;
|
document.documentElement.dataset.theme = theme;
|
||||||
document.documentElement.style.colorScheme = theme === 'dark' ? 'dark' : theme === 'dim' ? 'light' : 'light';
|
document.documentElement.style.colorScheme = theme === 'dark' ? 'dark' : 'light';
|
||||||
|
let meta = document.querySelector('meta[name="theme-color"]');
|
||||||
|
if (!meta) {
|
||||||
|
meta = document.createElement('meta');
|
||||||
|
meta.name = 'theme-color';
|
||||||
|
document.head.appendChild(meta);
|
||||||
|
}
|
||||||
|
meta.content = THEME_COLORS[theme] || THEME_COLORS.light;
|
||||||
const input = document.querySelector(`input[name="theme"][value="${theme}"]`);
|
const input = document.querySelector(`input[name="theme"][value="${theme}"]`);
|
||||||
if (input) input.checked = true;
|
if (input) input.checked = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -60,14 +60,14 @@ body { font-family: var(--sans); background: #2a2824; color: #1a1814; }
|
|||||||
padding: var(--stack-nav) 1rem 0;
|
padding: var(--stack-nav) 1rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Whole folder sticks; tab staggered via folder top; bodies align to reveal */
|
||||||
.folder {
|
.folder {
|
||||||
position: relative;
|
position: sticky;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tabs stick in staggered rail — always above folder bodies */
|
|
||||||
.tab {
|
.tab {
|
||||||
position: sticky;
|
|
||||||
display: block;
|
display: block;
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
max-width: calc(100% - 1rem);
|
max-width: calc(100% - 1rem);
|
||||||
@ -83,31 +83,47 @@ body { font-family: var(--sans); background: #2a2824; color: #1a1814; }
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
box-shadow: 0 -2px 8px rgba(0,0,0,0.12);
|
box-shadow: 0 -2px 8px rgba(0,0,0,0.12);
|
||||||
transition: filter 0.15s;
|
transition: filter 0.15s;
|
||||||
z-index: 60;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab:hover { filter: brightness(1.06); }
|
.tab:hover { filter: brightness(1.06); }
|
||||||
|
|
||||||
/* Bodies share one stick line; active layer z-index set in JS */
|
|
||||||
.body {
|
.body {
|
||||||
position: sticky;
|
|
||||||
top: var(--stack-reveal);
|
|
||||||
background: #e8e2d4;
|
background: #e8e2d4;
|
||||||
border: 1px solid #c4b8a8;
|
border: 1px solid #c4b8a8;
|
||||||
|
border-top: none;
|
||||||
border-radius: 0 10px 10px 10px;
|
border-radius: 0 10px 10px 10px;
|
||||||
padding: 1.25rem 1.4rem 2rem;
|
padding: 1.25rem 1.4rem 2rem;
|
||||||
min-height: var(--stack-body-h);
|
min-height: var(--stack-body-h);
|
||||||
box-shadow: 0 10px 32px rgba(0,0,0,0.25);
|
box-shadow: 0 10px 32px rgba(0,0,0,0.25);
|
||||||
z-index: 10;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.f0 .tab { top: calc(var(--stack-stick) + var(--stack-step) * 0); margin-left: calc(var(--tab-offset) * 0); background: #c9a86c; color: #2a2824; }
|
.f0 { top: calc(var(--stack-stick) + var(--stack-step) * 0); }
|
||||||
.f1 .tab { top: calc(var(--stack-stick) + var(--stack-step) * 1); margin-left: calc(var(--tab-offset) * 1); background: #a8c4d4; color: #1a2830; }
|
.f0 .tab { margin-left: calc(var(--tab-offset) * 0); background: #c9a86c; color: #2a2824; }
|
||||||
.f2 .tab { top: calc(var(--stack-stick) + var(--stack-step) * 2); margin-left: calc(var(--tab-offset) * 2); background: #b8d4a8; color: #1a2818; }
|
.f0 .body { margin-top: calc(var(--stack-reveal) - var(--stack-stick) - var(--stack-tab-h)); }
|
||||||
.f3 .tab { top: calc(var(--stack-stick) + var(--stack-step) * 3); margin-left: calc(var(--tab-offset) * 3); background: #d4b8c4; color: #2a1820; }
|
|
||||||
.f4 .tab { top: calc(var(--stack-stick) + var(--stack-step) * 4); margin-left: calc(var(--tab-offset) * 4); background: #d4c8a8; color: #2a2418; }
|
.f1 { top: calc(var(--stack-stick) + var(--stack-step) * 1); }
|
||||||
.f5 .tab { top: calc(var(--stack-stick) + var(--stack-step) * 5); margin-left: calc(var(--tab-offset) * 5); background: #c4c4c4; color: #2a2a2a; }
|
.f1 .tab { margin-left: calc(var(--tab-offset) * 1); background: #a8c4d4; color: #1a2830; }
|
||||||
.f6 .tab { top: calc(var(--stack-stick) + var(--stack-step) * 6); margin-left: calc(var(--tab-offset) * 6); background: #2a4a6b; color: #e8e2d4; }
|
.f1 .body { margin-top: calc(var(--stack-reveal) - var(--stack-stick) - var(--stack-step) * 1 - var(--stack-tab-h)); }
|
||||||
|
|
||||||
|
.f2 { top: calc(var(--stack-stick) + var(--stack-step) * 2); }
|
||||||
|
.f2 .tab { margin-left: calc(var(--tab-offset) * 2); background: #b8d4a8; color: #1a2818; }
|
||||||
|
.f2 .body { margin-top: calc(var(--stack-reveal) - var(--stack-stick) - var(--stack-step) * 2 - var(--stack-tab-h)); }
|
||||||
|
|
||||||
|
.f3 { top: calc(var(--stack-stick) + var(--stack-step) * 3); }
|
||||||
|
.f3 .tab { margin-left: calc(var(--tab-offset) * 3); background: #d4b8c4; color: #2a1820; }
|
||||||
|
.f3 .body { margin-top: calc(var(--stack-reveal) - var(--stack-stick) - var(--stack-step) * 3 - var(--stack-tab-h)); }
|
||||||
|
|
||||||
|
.f4 { top: calc(var(--stack-stick) + var(--stack-step) * 4); }
|
||||||
|
.f4 .tab { margin-left: calc(var(--tab-offset) * 4); background: #d4c8a8; color: #2a2418; }
|
||||||
|
.f4 .body { margin-top: calc(var(--stack-reveal) - var(--stack-stick) - var(--stack-step) * 4 - var(--stack-tab-h)); }
|
||||||
|
|
||||||
|
.f5 { top: calc(var(--stack-stick) + var(--stack-step) * 5); }
|
||||||
|
.f5 .tab { margin-left: calc(var(--tab-offset) * 5); background: #c4c4c4; color: #2a2a2a; }
|
||||||
|
.f5 .body { margin-top: calc(var(--stack-reveal) - var(--stack-stick) - var(--stack-step) * 5 - var(--stack-tab-h)); }
|
||||||
|
|
||||||
|
.f6 { top: calc(var(--stack-stick) + var(--stack-step) * 6); }
|
||||||
|
.f6 .tab { margin-left: calc(var(--tab-offset) * 6); background: #2a4a6b; color: #e8e2d4; }
|
||||||
|
.f6 .body { margin-top: calc(var(--stack-reveal) - var(--stack-stick) - var(--stack-step) * 6 - var(--stack-tab-h)); }
|
||||||
|
|
||||||
.body h1 { font-size: 1.65rem; margin-bottom: 0.35rem; }
|
.body h1 { font-size: 1.65rem; margin-bottom: 0.35rem; }
|
||||||
.body h2 { font-size: 1.25rem; margin-bottom: 0.4rem; }
|
.body h2 { font-size: 1.25rem; margin-bottom: 0.4rem; }
|
||||||
|
|||||||
@ -37,17 +37,16 @@ body {
|
|||||||
.mount { padding: 0 0.25rem; }
|
.mount { padding: 0 0.25rem; }
|
||||||
|
|
||||||
.unit {
|
.unit {
|
||||||
position: relative;
|
position: sticky;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
border: 1px solid #2a3448;
|
border: 1px solid #2a3448;
|
||||||
background: #161a22;
|
background: #161a22;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
box-shadow: 0 6px 0 #0a0c10, 0 12px 24px rgba(0,0,0,0.4);
|
box-shadow: 0 6px 0 #0a0c10, 0 12px 24px rgba(0,0,0,0.4);
|
||||||
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
.unit-head {
|
.unit-head {
|
||||||
position: sticky;
|
|
||||||
z-index: 60;
|
|
||||||
display: flex; align-items: center; gap: 0.5rem;
|
display: flex; align-items: center; gap: 0.5rem;
|
||||||
padding: 0.4rem 0.65rem;
|
padding: 0.4rem 0.65rem;
|
||||||
background: #1a2030;
|
background: #1a2030;
|
||||||
@ -72,26 +71,36 @@ body {
|
|||||||
.unit-head a.ext { color: #60a5fa; text-decoration: none; font-size: 0.58rem; }
|
.unit-head a.ext { color: #60a5fa; text-decoration: none; font-size: 0.58rem; }
|
||||||
|
|
||||||
.unit-body {
|
.unit-body {
|
||||||
position: sticky;
|
|
||||||
top: var(--stack-reveal);
|
|
||||||
padding: 0.6rem 0.7rem 2rem;
|
padding: 0.6rem 0.7rem 2rem;
|
||||||
min-height: var(--stack-body-h);
|
min-height: var(--stack-body-h);
|
||||||
z-index: 10;
|
|
||||||
background: #161a22;
|
background: #161a22;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.u0 { top: calc(var(--stack-stick) + var(--stack-step) * 0); }
|
||||||
|
.u0 .unit-body { margin-top: calc(var(--stack-reveal) - var(--stack-stick) - var(--stack-tab-h)); }
|
||||||
|
|
||||||
|
.u1 { top: calc(var(--stack-stick) + var(--stack-step) * 1); }
|
||||||
|
.u1 .unit-body { margin-top: calc(var(--stack-reveal) - var(--stack-stick) - var(--stack-step) * 1 - var(--stack-tab-h)); }
|
||||||
|
|
||||||
|
.u2 { top: calc(var(--stack-stick) + var(--stack-step) * 2); }
|
||||||
|
.u2 .unit-body { margin-top: calc(var(--stack-reveal) - var(--stack-stick) - var(--stack-step) * 2 - var(--stack-tab-h)); }
|
||||||
|
|
||||||
|
.u3 { top: calc(var(--stack-stick) + var(--stack-step) * 3); }
|
||||||
|
.u3 .unit-body { margin-top: calc(var(--stack-reveal) - var(--stack-stick) - var(--stack-step) * 3 - var(--stack-tab-h)); }
|
||||||
|
|
||||||
|
.u4 { top: calc(var(--stack-stick) + var(--stack-step) * 4); }
|
||||||
|
.u4 .unit-body { margin-top: calc(var(--stack-reveal) - var(--stack-stick) - var(--stack-step) * 4 - var(--stack-tab-h)); }
|
||||||
|
|
||||||
|
.u5 { top: calc(var(--stack-stick) + var(--stack-step) * 5); }
|
||||||
|
.u5 .unit-body { margin-top: calc(var(--stack-reveal) - var(--stack-stick) - var(--stack-step) * 5 - var(--stack-tab-h)); }
|
||||||
|
|
||||||
|
.u6 { top: calc(var(--stack-stick) + var(--stack-step) * 6); }
|
||||||
|
.u6 .unit-body { margin-top: calc(var(--stack-reveal) - var(--stack-stick) - var(--stack-step) * 6 - var(--stack-tab-h)); }
|
||||||
|
|
||||||
.unit-body strong { color: #e5e7eb; display: block; margin-bottom: 0.2rem; }
|
.unit-body strong { color: #e5e7eb; display: block; margin-bottom: 0.2rem; }
|
||||||
.unit-body p { color: #6b7280; font-size: 0.7rem; }
|
.unit-body p { color: #6b7280; font-size: 0.7rem; }
|
||||||
.unit-body a { color: #60a5fa; text-decoration: none; }
|
.unit-body a { color: #60a5fa; text-decoration: none; }
|
||||||
|
|
||||||
.u0 .unit-head { top: calc(var(--stack-stick) + var(--stack-step) * 0); }
|
|
||||||
.u1 .unit-head { top: calc(var(--stack-stick) + var(--stack-step) * 1); }
|
|
||||||
.u2 .unit-head { top: calc(var(--stack-stick) + var(--stack-step) * 2); }
|
|
||||||
.u3 .unit-head { top: calc(var(--stack-stick) + var(--stack-step) * 3); }
|
|
||||||
.u4 .unit-head { top: calc(var(--stack-stick) + var(--stack-step) * 4); }
|
|
||||||
.u5 .unit-head { top: calc(var(--stack-stick) + var(--stack-step) * 5); }
|
|
||||||
.u6 .unit-head { top: calc(var(--stack-stick) + var(--stack-step) * 6); }
|
|
||||||
|
|
||||||
.foot {
|
.foot {
|
||||||
display: flex; justify-content: space-between;
|
display: flex; justify-content: space-between;
|
||||||
width: min(600px, 100%); margin: 0 auto;
|
width: min(600px, 100%); margin: 0 auto;
|
||||||
|
|||||||
@ -27,27 +27,12 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.frame {
|
.frame {
|
||||||
position: relative;
|
position: sticky;
|
||||||
margin: 0 0.5rem;
|
margin: 0 0.5rem;
|
||||||
border-left: 3px solid #3a3a44;
|
border-left: 3px solid #3a3a44;
|
||||||
background: #141418;
|
background: #141418;
|
||||||
box-shadow: 0 8px 0 #0a0a0c, 0 14px 28px rgba(0,0,0,0.45);
|
box-shadow: 0 8px 0 #0a0a0c, 0 14px 28px rgba(0,0,0,0.45);
|
||||||
}
|
|
||||||
|
|
||||||
.frame-line {
|
|
||||||
position: sticky;
|
|
||||||
z-index: 60;
|
|
||||||
padding: 0.65rem 0 0.35rem 1rem;
|
|
||||||
background: #141418;
|
|
||||||
}
|
|
||||||
|
|
||||||
.frame-body {
|
|
||||||
position: sticky;
|
|
||||||
top: var(--stack-reveal);
|
|
||||||
padding: 0 0 2rem 1rem;
|
|
||||||
min-height: var(--stack-body-h);
|
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
background: #141418;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.frame-line {
|
.frame-line {
|
||||||
@ -59,25 +44,43 @@ body {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
padding: 0.65rem 0 0.35rem 1rem;
|
||||||
|
background: #141418;
|
||||||
}
|
}
|
||||||
|
|
||||||
.frame-line:hover { color: #9fdf9f; text-decoration: underline; }
|
.frame-line:hover { color: #9fdf9f; text-decoration: underline; }
|
||||||
|
|
||||||
|
.frame-body {
|
||||||
|
padding: 0 0 2rem 1rem;
|
||||||
|
min-height: var(--stack-body-h);
|
||||||
|
background: #141418;
|
||||||
|
}
|
||||||
|
|
||||||
|
.f0 { top: calc(var(--stack-stick) + var(--stack-step) * 0); border-color: #c4a574; }
|
||||||
|
.f0 .frame-body { margin-top: calc(var(--stack-reveal) - var(--stack-stick) - var(--stack-tab-h)); }
|
||||||
|
|
||||||
|
.f1 { top: calc(var(--stack-stick) + var(--stack-step) * 1); }
|
||||||
|
.f1 .frame-body { margin-top: calc(var(--stack-reveal) - var(--stack-stick) - var(--stack-step) * 1 - var(--stack-tab-h)); }
|
||||||
|
|
||||||
|
.f2 { top: calc(var(--stack-stick) + var(--stack-step) * 2); }
|
||||||
|
.f2 .frame-body { margin-top: calc(var(--stack-reveal) - var(--stack-stick) - var(--stack-step) * 2 - var(--stack-tab-h)); }
|
||||||
|
|
||||||
|
.f3 { top: calc(var(--stack-stick) + var(--stack-step) * 3); }
|
||||||
|
.f3 .frame-body { margin-top: calc(var(--stack-reveal) - var(--stack-stick) - var(--stack-step) * 3 - var(--stack-tab-h)); }
|
||||||
|
|
||||||
|
.f4 { top: calc(var(--stack-stick) + var(--stack-step) * 4); border-color: #6b8b9b; }
|
||||||
|
.f4 .frame-body { margin-top: calc(var(--stack-reveal) - var(--stack-stick) - var(--stack-step) * 4 - var(--stack-tab-h)); }
|
||||||
|
|
||||||
|
.f5 { top: calc(var(--stack-stick) + var(--stack-step) * 5); }
|
||||||
|
.f5 .frame-body { margin-top: calc(var(--stack-reveal) - var(--stack-stick) - var(--stack-step) * 5 - var(--stack-tab-h)); }
|
||||||
|
|
||||||
|
.f6 { top: calc(var(--stack-stick) + var(--stack-step) * 6); border-color: #7eb87a; }
|
||||||
|
.f6 .frame-body { margin-top: calc(var(--stack-reveal) - var(--stack-stick) - var(--stack-step) * 6 - var(--stack-tab-h)); }
|
||||||
|
|
||||||
.frame-body strong { color: #e8e6e3; font-weight: 500; display: block; margin-bottom: 0.2rem; }
|
.frame-body strong { color: #e8e6e3; font-weight: 500; display: block; margin-bottom: 0.2rem; }
|
||||||
.frame-body p { color: #6b6966; font-size: 0.74rem; }
|
.frame-body p { color: #6b6966; font-size: 0.74rem; }
|
||||||
.frame-body a { color: #8b9cb3; text-decoration: none; }
|
.frame-body a { color: #8b9cb3; text-decoration: none; }
|
||||||
|
|
||||||
.f0 .frame-line { top: calc(var(--stack-stick) + var(--stack-step) * 0); }
|
|
||||||
.f0 { border-color: #c4a574; }
|
|
||||||
.f1 .frame-line { top: calc(var(--stack-stick) + var(--stack-step) * 1); }
|
|
||||||
.f2 .frame-line { top: calc(var(--stack-stick) + var(--stack-step) * 2); }
|
|
||||||
.f3 .frame-line { top: calc(var(--stack-stick) + var(--stack-step) * 3); }
|
|
||||||
.f4 .frame-line { top: calc(var(--stack-stick) + var(--stack-step) * 4); }
|
|
||||||
.f4 { border-color: #6b8b9b; }
|
|
||||||
.f5 .frame-line { top: calc(var(--stack-stick) + var(--stack-step) * 5); }
|
|
||||||
.f6 .frame-line { top: calc(var(--stack-stick) + var(--stack-step) * 6); }
|
|
||||||
.f6 { border-color: #7eb87a; }
|
|
||||||
|
|
||||||
.foot {
|
.foot {
|
||||||
display: flex; justify-content: space-between;
|
display: flex; justify-content: space-between;
|
||||||
width: min(600px, 100%); margin: 0 auto;
|
width: min(600px, 100%); margin: 0 auto;
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
:root {
|
:root {
|
||||||
--mono: 'IBM Plex Mono', ui-monospace, monospace;
|
--mono: 'IBM Plex Mono', ui-monospace, monospace;
|
||||||
--sans: 'Instrument Sans', system-ui, sans-serif;
|
--sans: 'Instrument Sans', system-ui, sans-serif;
|
||||||
|
--card-tab-h: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
@ -35,26 +36,17 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.layer {
|
.layer {
|
||||||
position: relative;
|
position: sticky;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
border-radius: 8px 8px 6px 6px;
|
border-radius: 8px 8px 6px 6px;
|
||||||
border: 1px solid rgba(255,255,255,0.08);
|
border: 1px solid rgba(255,255,255,0.08);
|
||||||
box-shadow: 0 12px 36px rgba(0,0,0,0.5);
|
box-shadow: 0 12px 36px rgba(0,0,0,0.5);
|
||||||
}
|
|
||||||
|
|
||||||
.layer-inner {
|
|
||||||
position: sticky;
|
|
||||||
top: var(--stack-reveal);
|
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
.layer-tab {
|
.layer-tab {
|
||||||
position: sticky;
|
|
||||||
z-index: 55;
|
|
||||||
left: 10px;
|
|
||||||
right: 10px;
|
|
||||||
height: 6px;
|
height: 6px;
|
||||||
margin-bottom: -6px;
|
margin: 0 10px -6px;
|
||||||
border-radius: 5px 5px 0 0;
|
border-radius: 5px 5px 0 0;
|
||||||
background: inherit;
|
background: inherit;
|
||||||
filter: brightness(1.12);
|
filter: brightness(1.12);
|
||||||
@ -63,26 +55,32 @@ body {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.layer-0 { background: #1c1c20; }
|
|
||||||
.layer-0 .layer-tab { top: calc(var(--stack-stick) + var(--stack-step) * 0); }
|
|
||||||
.layer-1 { background: #24242c; }
|
|
||||||
.layer-1 .layer-tab { top: calc(var(--stack-stick) + var(--stack-step) * 1); }
|
|
||||||
.layer-2 { background: #2c2c36; }
|
|
||||||
.layer-2 .layer-tab { top: calc(var(--stack-stick) + var(--stack-step) * 2); }
|
|
||||||
.layer-3 { background: #343440; }
|
|
||||||
.layer-3 .layer-tab { top: calc(var(--stack-stick) + var(--stack-step) * 3); }
|
|
||||||
.layer-4 { background: #3c3c4a; }
|
|
||||||
.layer-4 .layer-tab { top: calc(var(--stack-stick) + var(--stack-step) * 4); }
|
|
||||||
.layer-5 { background: #444454; }
|
|
||||||
.layer-5 .layer-tab { top: calc(var(--stack-stick) + var(--stack-step) * 5); }
|
|
||||||
.layer-6 { background: #4c4c5e; }
|
|
||||||
.layer-6 .layer-tab { top: calc(var(--stack-stick) + var(--stack-step) * 6); }
|
|
||||||
|
|
||||||
.layer-inner {
|
.layer-inner {
|
||||||
padding: 1.2rem 1.35rem 2rem;
|
padding: 1.2rem 1.35rem 2rem;
|
||||||
min-height: var(--stack-body-h);
|
min-height: var(--stack-body-h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.layer-0 { background: #1c1c20; top: calc(var(--stack-stick) + var(--stack-step) * 0); }
|
||||||
|
.layer-0 .layer-inner { margin-top: calc(var(--stack-reveal) - var(--stack-stick) - var(--card-tab-h)); }
|
||||||
|
|
||||||
|
.layer-1 { background: #24242c; top: calc(var(--stack-stick) + var(--stack-step) * 1); }
|
||||||
|
.layer-1 .layer-inner { margin-top: calc(var(--stack-reveal) - var(--stack-stick) - var(--stack-step) * 1 - var(--card-tab-h)); }
|
||||||
|
|
||||||
|
.layer-2 { background: #2c2c36; top: calc(var(--stack-stick) + var(--stack-step) * 2); }
|
||||||
|
.layer-2 .layer-inner { margin-top: calc(var(--stack-reveal) - var(--stack-stick) - var(--stack-step) * 2 - var(--card-tab-h)); }
|
||||||
|
|
||||||
|
.layer-3 { background: #343440; top: calc(var(--stack-stick) + var(--stack-step) * 3); }
|
||||||
|
.layer-3 .layer-inner { margin-top: calc(var(--stack-reveal) - var(--stack-stick) - var(--stack-step) * 3 - var(--card-tab-h)); }
|
||||||
|
|
||||||
|
.layer-4 { background: #3c3c4a; top: calc(var(--stack-stick) + var(--stack-step) * 4); }
|
||||||
|
.layer-4 .layer-inner { margin-top: calc(var(--stack-reveal) - var(--stack-stick) - var(--stack-step) * 4 - var(--card-tab-h)); }
|
||||||
|
|
||||||
|
.layer-5 { background: #444454; top: calc(var(--stack-stick) + var(--stack-step) * 5); }
|
||||||
|
.layer-5 .layer-inner { margin-top: calc(var(--stack-reveal) - var(--stack-stick) - var(--stack-step) * 5 - var(--card-tab-h)); }
|
||||||
|
|
||||||
|
.layer-6 { background: #4c4c5e; top: calc(var(--stack-stick) + var(--stack-step) * 6); }
|
||||||
|
.layer-6 .layer-inner { margin-top: calc(var(--stack-reveal) - var(--stack-stick) - var(--stack-step) * 6 - var(--card-tab-h)); }
|
||||||
|
|
||||||
.layer-head {
|
.layer-head {
|
||||||
display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem 0.65rem;
|
display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem 0.65rem;
|
||||||
margin-bottom: 0.65rem; padding-bottom: 0.5rem;
|
margin-bottom: 0.65rem; padding-bottom: 0.5rem;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user