mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-11 21:57:14 +03:00
Before/after split demos on skill pages + sidebar reorder
Two changes bundled:
1. Before/after split demos on every skill detail page.
- loadCommandDemos() in sub-pages-data.js: dynamically imports each
module in public/js/demos/commands (the same files the homepage
uses), returning a { skillId: { id, caption, before, after } } map.
Falls back to a warn-and-continue if a demo file can't be loaded
so one bad demo doesn't break the whole generator.
- buildSubPageData becomes async; caller in build-sub-pages.js
awaits it.
- Each skill object gets a .demo field (may be null for /shape).
- renderSkillDemo() produces the .split-comparison markup matching
the homepage: .split-container with .split-before + .split-after
+ .split-divider, plus Before/After labels and the caption. The
block sits between the detail header and the editorial wrapper
so readers see the visual before reading any prose.
- sub-pages.css ports the core .split-* layout from main.css (the
.slop-* and .impeccable-* helpers are homepage-specific and not
copied). Height is 360px to match the docs column.
- render-page.js grows a lightweight inline split-compare init
script (60 lines of vanilla JS) that handles drag and the skewed
clip-path without depending on the homepage's full lerp/ResizeObserver
module. Runs only on pages that actually have .split-container.
2. Sidebar reorder: Tutorials first, then skills.
Walk-throughs are the on-ramp; they belong at the top of the sidebar
where a new visitor will find them. Add <hr class="skills-sidebar-divider">
between the Tutorials group and the first skill category so the two
sections read as distinct.
Verified: /skills/polish, /skills/bolder, /skills/critique all render
the demo block. /skills/shape correctly has none. Sidebar on any /skills
or /tutorials page shows Tutorials first, then a thin mist divider,
then Create / Evaluate / Refine / Simplify / Harden / System skill
categories.
This commit is contained in:
@@ -114,6 +114,62 @@ ${bodyHtml}
|
||||
setTimeout(() => btn.classList.remove('is-copied'), 1500);
|
||||
}).catch(() => {});
|
||||
});
|
||||
|
||||
// Lightweight split-compare interaction for before/after demos.
|
||||
// Drag or hover horizontally over .split-container to sweep the
|
||||
// divider. No lerp, no ResizeObserver — good enough for docs.
|
||||
(function initSplitCompare() {
|
||||
const containers = document.querySelectorAll('.split-container');
|
||||
if (containers.length === 0) return;
|
||||
for (const container of containers) {
|
||||
const splitAfter = container.querySelector('.split-after');
|
||||
const splitDivider = container.querySelector('.split-divider');
|
||||
if (!splitAfter || !splitDivider) continue;
|
||||
|
||||
const skewAngle = 10 * Math.PI / 180;
|
||||
const tanAngle = Math.tan(skewAngle);
|
||||
let skewOffset = 8;
|
||||
const recalcSkew = () => {
|
||||
const r = container.getBoundingClientRect();
|
||||
if (r.width > 0 && r.height > 0) {
|
||||
skewOffset = 50 * r.height * tanAngle / r.width;
|
||||
}
|
||||
};
|
||||
recalcSkew();
|
||||
window.addEventListener('resize', recalcSkew, { passive: true });
|
||||
|
||||
const update = (pct) => {
|
||||
const x = Math.max(-skewOffset, Math.min(100 + skewOffset, pct));
|
||||
splitAfter.style.clipPath = \`polygon(\${x + skewOffset}% 0%, 100% 0%, 100% 100%, \${x - skewOffset}% 100%)\`;
|
||||
splitDivider.style.left = \`\${x}%\`;
|
||||
};
|
||||
update(50);
|
||||
|
||||
let tracking = false;
|
||||
const onMove = (clientX) => {
|
||||
const rect = container.getBoundingClientRect();
|
||||
const pct = ((clientX - rect.left) / rect.width) * 100;
|
||||
update(pct);
|
||||
};
|
||||
container.addEventListener('pointerdown', (e) => {
|
||||
tracking = true;
|
||||
container.setPointerCapture(e.pointerId);
|
||||
onMove(e.clientX);
|
||||
});
|
||||
container.addEventListener('pointermove', (e) => {
|
||||
if (!tracking) return;
|
||||
onMove(e.clientX);
|
||||
});
|
||||
const stop = (e) => {
|
||||
if (!tracking) return;
|
||||
tracking = false;
|
||||
try { container.releasePointerCapture(e.pointerId); } catch {}
|
||||
};
|
||||
container.addEventListener('pointerup', stop);
|
||||
container.addEventListener('pointercancel', stop);
|
||||
container.addEventListener('pointerleave', stop);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user