mirror of
https://github.com/Nutlope/hallmark.git
synced 2026-08-14 12:35:33 +02:00
Phases C+D+E — pull-arrows, interlude, anti-patterns section, chapter rail
Three phases of editorial-spine moves landed together:
PHASE C — Pacing.
· End-of-section pull-arrows. Each major section now closes with a
small italic-serif arrow link pulling the eye to the next
section: "See the verbs →" (after Section 02), "See the named
tells →" (after 03), "Read the rules →" (after the new 04),
"Install in your editor →" (after 05). Hairline rule above each
arrow. Hover nudges the arrow forward, recolours to accent.
Theme-aware: editorial themes get italic serif; brutal /
manifesto / sport / terminal etc. switch to body sans uppercase.
· Mid-scroll typographic interlude between Sections III and IV.
Single declarative phrase set in the active theme's display
face on a contrasting paper band: "The page is the demo."
("The page" in italic accent, rest in roman ink). Full-bleed,
centred, generous padding. Pure type, zero motion. Resets
attention before the anti-patterns section.
PHASE D — Slop, by name.
· Replaced Section 04 (Without/With) with a new Anti-patterns
section. Five named tells, each shown as: a number (mono), the
tell's name in italic display with a strike-through (the slop,
visually struck), a description (italic serif muted), and then
the Hallmark fix in clean ink with an accent label. Side-by-
side typography so the contrast IS the design.
The five tells: purple-gradient hero, Inter-as-display, centred
everything, icon-tile feature card, the AI nav.
· Original Section 04 markup preserved verbatim inside a
<template id="section-04-archive-without-with"> wrapper. The
template element doesn't render but keeps the content in the
file for revertibility. To restore the original section: change
the opening <template> to <section class="section reveal"
style="--i:3" aria-labelledby="s4"> and the closing </template>
to </section>, then delete the new Anti-patterns section. (The
archived content uses the vs--notes / vs__panel--dash etc class
names which contain "--" sequences invalid inside HTML
comments, hence the template wrapper instead.)
PHASE E — Chapter rail.
· New <aside class="chapter-rail"> pinned to the left margin on
viewports ≥80rem (1280px). Lists II Examples · III Skills · IV
Anti-patterns · V Foundations · VI Install. Italic serif Roman
numeral + small-caps mono name. Hidden below 80rem so it never
competes with content on narrower screens.
· IntersectionObserver in main.js tracks the topmost visible
section and adds .is-current to the matching link. Active link
brightens to ink colour with accent numeral; inactive links
fade to rule-2 muted. Smooth (one CSS-token transition).
Net editorial gravity:
Banner → 01/23 indicator + dropdown (clean chrome)
Section 02 → "Different briefs. Different bones." + larger cards
Section 03 → unchanged (already strong)
Mid-scroll → "The page is the demo." interlude
Section 04 → Anti-patterns ("Slop, by name.") replacing W/W
Pull-arrows between every section
Chapter rail on wide screens
Footer → colophon (publisher's signature page)
Files changed: index.html (markup) · components.css (chapter rail
+ anti-patterns + interlude theme overrides) · sections.css
(anti-patterns layout + pull-arrow + interlude) · main.js
(chapter rail observer).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
5d9354f34d
commit
58ee2deade
@@ -452,6 +452,89 @@
|
||||
.banner__theme-indicator .banner__theme { display: none; }
|
||||
}
|
||||
|
||||
/* — Chapter rail — sticky outline on wide viewports ————————
|
||||
Vertical list of section coordinates pinned to the left margin.
|
||||
Highlights the current section as you scroll. Hidden below 80rem
|
||||
(1280px) where there's no margin space for it. */
|
||||
.chapter-rail {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: max(var(--space-sm), calc((100vw - 76rem) / 2 - 7rem));
|
||||
transform: translateY(-50%);
|
||||
z-index: 5;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.chapter-rail__list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: grid;
|
||||
gap: 0.55rem;
|
||||
}
|
||||
|
||||
.chapter-rail__list a {
|
||||
display: grid;
|
||||
grid-template-columns: 1.6em 1fr;
|
||||
align-items: baseline;
|
||||
gap: 0.55rem;
|
||||
text-decoration: none;
|
||||
color: var(--color-rule-2);
|
||||
padding: 0.15rem 0.4rem 0.15rem 0;
|
||||
font-family: var(--font-label);
|
||||
font-size: 0.625rem;
|
||||
letter-spacing: var(--tracking-label);
|
||||
text-transform: uppercase;
|
||||
transition: color var(--dur-micro) var(--ease-out);
|
||||
}
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
.chapter-rail__list a:hover {
|
||||
color: var(--color-ink-2);
|
||||
}
|
||||
}
|
||||
|
||||
.chapter-rail__list a:focus-visible {
|
||||
outline: 2px solid var(--color-focus);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.chapter-rail__list a.is-current {
|
||||
color: var(--color-ink);
|
||||
}
|
||||
|
||||
.chapter-rail__num {
|
||||
font-family: var(--font-serif);
|
||||
font-style: italic;
|
||||
text-transform: none;
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0;
|
||||
color: var(--color-rule-2);
|
||||
text-align: end;
|
||||
transition: color var(--dur-micro) var(--ease-out);
|
||||
}
|
||||
|
||||
.chapter-rail__list a.is-current .chapter-rail__num {
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
.chapter-rail__name {
|
||||
/* The name itself is small mono — only the active section's name
|
||||
gets visible weight; the others fade. */
|
||||
opacity: 0.6;
|
||||
transition: opacity var(--dur-micro) var(--ease-out);
|
||||
}
|
||||
|
||||
.chapter-rail__list a.is-current .chapter-rail__name,
|
||||
.chapter-rail__list a:hover .chapter-rail__name {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@media (max-width: 80rem) {
|
||||
.chapter-rail { display: none; }
|
||||
}
|
||||
|
||||
/* — Section label (01 / FOUNDATIONS) —————————————————— */
|
||||
.section-label {
|
||||
font-family: var(--font-label);
|
||||
|
||||
@@ -1362,6 +1362,282 @@ html[data-theme="almanac"] body {
|
||||
color: var(--color-accent-ink);
|
||||
}
|
||||
|
||||
/* — 04 · Anti-patterns — Slop, by name ——————————————————
|
||||
Five-row editorial list: number · slop name (struck-through,
|
||||
muted) · fix label + body (clean, ink). Side-by-side typography
|
||||
so the contrast IS the design. Hairline rules between rows. */
|
||||
|
||||
.anti__intro {
|
||||
margin: 0 0 var(--space-2xl);
|
||||
font-family: var(--font-serif);
|
||||
font-style: italic;
|
||||
font-size: var(--text-md);
|
||||
line-height: var(--lh-snug);
|
||||
color: var(--color-ink-2);
|
||||
max-width: 60ch;
|
||||
}
|
||||
|
||||
[data-theme="brutal"] .anti__intro,
|
||||
[data-theme="manifesto"] .anti__intro,
|
||||
[data-theme="terminal"] .anti__intro,
|
||||
[data-theme="sport"] .anti__intro,
|
||||
[data-theme="almanac"] .anti__intro,
|
||||
[data-theme="quiet"] .anti__intro,
|
||||
[data-theme="bloom"] .anti__intro,
|
||||
[data-theme="riso"] .anti__intro {
|
||||
font-family: var(--font-body);
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.anti__intro code {
|
||||
font-family: var(--font-mono);
|
||||
font-style: normal;
|
||||
font-size: 0.875em;
|
||||
background: var(--color-paper-2);
|
||||
padding: 0.05em 0.35em;
|
||||
border: var(--rule-hair) solid var(--color-rule);
|
||||
color: var(--color-ink);
|
||||
}
|
||||
|
||||
.anti-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: grid;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.anti-row {
|
||||
display: grid;
|
||||
grid-template-columns: 3.5rem minmax(0, 1.3fr) minmax(0, 1fr);
|
||||
gap: var(--space-xl);
|
||||
padding-block: var(--space-xl);
|
||||
border-block-start: var(--rule-hair) solid var(--color-rule);
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.anti-row:first-child {
|
||||
border-block-start: 0;
|
||||
padding-block-start: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 60rem) {
|
||||
.anti-row {
|
||||
grid-template-columns: 1fr;
|
||||
gap: var(--space-md);
|
||||
}
|
||||
}
|
||||
|
||||
.anti-row__num {
|
||||
margin: 0;
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--text-xs);
|
||||
letter-spacing: var(--tracking-label);
|
||||
text-transform: uppercase;
|
||||
color: var(--color-muted);
|
||||
padding-block-start: 0.45em;
|
||||
font-feature-settings: "tnum" 1;
|
||||
}
|
||||
|
||||
.anti-row__name {
|
||||
margin: 0 0 var(--space-sm) 0;
|
||||
font-family: var(--font-display);
|
||||
font-style: italic;
|
||||
font-weight: var(--display-weight, 500);
|
||||
font-size: clamp(1.25rem, 1.5vw + 0.75rem, 1.75rem);
|
||||
line-height: var(--lh-tight);
|
||||
letter-spacing: var(--tracking-tight);
|
||||
color: var(--color-rule-2);
|
||||
text-decoration: line-through;
|
||||
text-decoration-thickness: 1px;
|
||||
text-decoration-color: var(--color-rule-2);
|
||||
font-optical-sizing: auto;
|
||||
}
|
||||
|
||||
[data-theme="brutal"] .anti-row__name,
|
||||
[data-theme="manifesto"] .anti-row__name,
|
||||
[data-theme="sport"] .anti-row__name {
|
||||
text-transform: uppercase;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
[data-theme="terminal"] .anti-row__name {
|
||||
font-family: var(--font-mono);
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.anti-row__desc {
|
||||
margin: 0;
|
||||
font-family: var(--font-serif);
|
||||
font-style: italic;
|
||||
font-size: 0.9375rem;
|
||||
line-height: var(--lh-relaxed);
|
||||
color: var(--color-muted);
|
||||
max-width: 50ch;
|
||||
}
|
||||
|
||||
[data-theme="brutal"] .anti-row__desc,
|
||||
[data-theme="manifesto"] .anti-row__desc,
|
||||
[data-theme="terminal"] .anti-row__desc,
|
||||
[data-theme="sport"] .anti-row__desc,
|
||||
[data-theme="almanac"] .anti-row__desc,
|
||||
[data-theme="quiet"] .anti-row__desc,
|
||||
[data-theme="bloom"] .anti-row__desc {
|
||||
font-family: var(--font-body);
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.anti-row__fix-label {
|
||||
margin: 0 0 var(--space-2xs) 0;
|
||||
font-family: var(--font-label);
|
||||
font-size: var(--text-xs);
|
||||
letter-spacing: var(--tracking-label);
|
||||
text-transform: uppercase;
|
||||
color: var(--color-accent);
|
||||
font-feature-settings: "tnum";
|
||||
}
|
||||
|
||||
.anti-row__fix-body {
|
||||
margin: 0;
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-base);
|
||||
line-height: var(--lh-relaxed);
|
||||
color: var(--color-ink);
|
||||
max-width: 45ch;
|
||||
}
|
||||
|
||||
/* — Section pull-arrow ————————————————————————————————————
|
||||
Editorial "continued on p. 12" cue at the end of each section.
|
||||
Italic serif label + arrow, biased left or right per section
|
||||
for rhythm. Hover nudges the arrow forward. Pulls the eye into
|
||||
the next section instead of stopping cold. */
|
||||
.section__pull {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
gap: 0.5em;
|
||||
margin-block-start: var(--space-xl);
|
||||
padding-block: var(--space-sm);
|
||||
font-family: var(--font-serif);
|
||||
font-style: italic;
|
||||
font-size: var(--text-md);
|
||||
line-height: 1.2;
|
||||
color: var(--color-ink-2);
|
||||
text-decoration: none;
|
||||
border-block-start: var(--rule-hair) solid var(--color-rule);
|
||||
width: fit-content;
|
||||
transition: color var(--dur-micro) var(--ease-out);
|
||||
}
|
||||
|
||||
.section__pull--right {
|
||||
margin-inline-start: auto;
|
||||
text-align: end;
|
||||
}
|
||||
|
||||
.section__pull-arrow {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
color: var(--color-rule-2);
|
||||
font-feature-settings: "tnum";
|
||||
transition: transform var(--dur-short) var(--ease-out),
|
||||
color var(--dur-micro) var(--ease-out);
|
||||
}
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
.section__pull:hover { color: var(--color-ink); }
|
||||
.section__pull:hover .section__pull-arrow {
|
||||
transform: translateX(4px);
|
||||
color: var(--color-accent);
|
||||
}
|
||||
}
|
||||
|
||||
.section__pull:focus-visible {
|
||||
outline: 2px solid var(--color-focus);
|
||||
outline-offset: 4px;
|
||||
color: var(--color-ink);
|
||||
}
|
||||
|
||||
[data-theme="brutal"] .section__pull,
|
||||
[data-theme="manifesto"] .section__pull,
|
||||
[data-theme="terminal"] .section__pull,
|
||||
[data-theme="sport"] .section__pull,
|
||||
[data-theme="almanac"] .section__pull,
|
||||
[data-theme="quiet"] .section__pull,
|
||||
[data-theme="bloom"] .section__pull {
|
||||
font-family: var(--font-body);
|
||||
font-style: normal;
|
||||
text-transform: uppercase;
|
||||
font-size: var(--text-sm);
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
[data-theme="terminal"] .section__pull-arrow::before {
|
||||
content: ">";
|
||||
margin-inline-end: 0.4em;
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
/* — Interlude — full-bleed typographic moment between sections —
|
||||
One declarative phrase set in the active theme's display face,
|
||||
centered on a contrasting paper band. Punctuates the scroll;
|
||||
resets attention before the next section. Zero motion, pure type. */
|
||||
.interlude {
|
||||
margin-inline: calc(-1 * var(--page-gutter));
|
||||
padding-block: var(--space-3xl);
|
||||
padding-inline: var(--page-gutter);
|
||||
background: var(--color-paper-2);
|
||||
border-block: var(--rule-hair) solid var(--color-rule);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.interlude__phrase {
|
||||
margin: 0;
|
||||
font-family: var(--font-display);
|
||||
font-weight: var(--display-weight, 500);
|
||||
font-style: var(--display-style, normal);
|
||||
font-size: clamp(2.25rem, 6vw + 1rem, 5rem);
|
||||
line-height: var(--lh-tight);
|
||||
letter-spacing: var(--tracking-display);
|
||||
color: var(--color-ink);
|
||||
font-optical-sizing: auto;
|
||||
max-width: 22ch;
|
||||
/* Long-word safety inherited from base.css */
|
||||
}
|
||||
|
||||
.interlude__phrase em {
|
||||
font-style: italic;
|
||||
color: var(--color-accent-ink, var(--color-accent));
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
[data-theme="brutal"] .interlude__phrase,
|
||||
[data-theme="manifesto"] .interlude__phrase,
|
||||
[data-theme="sport"] .interlude__phrase {
|
||||
text-transform: uppercase;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
[data-theme="brutal"] .interlude__phrase em,
|
||||
[data-theme="manifesto"] .interlude__phrase em,
|
||||
[data-theme="sport"] .interlude__phrase em {
|
||||
font-style: normal;
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
[data-theme="terminal"] .interlude__phrase {
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
[data-theme="terminal"] .interlude__phrase em {
|
||||
font-style: normal;
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
@media (max-width: 48rem) {
|
||||
.interlude { padding-block: var(--space-2xl); }
|
||||
}
|
||||
|
||||
/* ──────────────────────────────────────────────────────────────
|
||||
Mobile collapse — section-head one-column on narrow viewports.
|
||||
Many themes override `.section__head`'s `grid-template-columns`
|
||||
|
||||
+137
-3
@@ -139,6 +139,18 @@
|
||||
</aside>
|
||||
</header>
|
||||
|
||||
<!-- CHAPTER RAIL — sticky outline visible on wide viewports only.
|
||||
Highlights current section as you scroll. Hidden below 80rem. -->
|
||||
<aside class="chapter-rail" aria-label="Page outline">
|
||||
<ol class="chapter-rail__list">
|
||||
<li><a href="#examples" data-chapter-link="examples"><span class="chapter-rail__num">II</span><span class="chapter-rail__name">Examples</span></a></li>
|
||||
<li><a href="#skills" data-chapter-link="skills"><span class="chapter-rail__num">III</span><span class="chapter-rail__name">Skills</span></a></li>
|
||||
<li><a href="#anti-patterns" data-chapter-link="anti-patterns"><span class="chapter-rail__num">IV</span><span class="chapter-rail__name">Anti-patterns</span></a></li>
|
||||
<li><a href="#s5-foundations" data-chapter-link="foundations"><span class="chapter-rail__num">V</span><span class="chapter-rail__name">Foundations</span></a></li>
|
||||
<li><a href="#install" data-chapter-link="install"><span class="chapter-rail__num">VI</span><span class="chapter-rail__name">Install</span></a></li>
|
||||
</ol>
|
||||
</aside>
|
||||
|
||||
<main class="page">
|
||||
|
||||
<!-- 01 · HERO SLOT — archetype swaps per theme -->
|
||||
@@ -312,6 +324,11 @@
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<a class="section__pull" href="#skills" aria-label="Continue to Skills section">
|
||||
<span class="section__pull-label">See the verbs</span>
|
||||
<span class="section__pull-arrow" aria-hidden="true">→</span>
|
||||
</a>
|
||||
</section>
|
||||
|
||||
<!-- 03 · SKILLS — five things Hallmark does, one ruleset underneath -->
|
||||
@@ -523,10 +540,116 @@
|
||||
</li>
|
||||
|
||||
</ol>
|
||||
|
||||
<a class="section__pull section__pull--right" href="#anti-patterns" aria-label="Continue to Anti-patterns section">
|
||||
<span class="section__pull-label">See the named tells</span>
|
||||
<span class="section__pull-arrow" aria-hidden="true">→</span>
|
||||
</a>
|
||||
</section>
|
||||
|
||||
<!-- 04 · WITHOUT / WITH — same copy, two design languages, four examples -->
|
||||
<section class="section reveal" style="--i:3" aria-labelledby="s4">
|
||||
<!-- INTERLUDE — full-bleed typographic moment between III and IV.
|
||||
Mid-scroll punctuation: a single declarative phrase set in
|
||||
the active theme's display face, on a contrasting paper band.
|
||||
Reprise of the marketing pitch in oversized type. -->
|
||||
<aside class="interlude reveal" style="--i:3" aria-hidden="true">
|
||||
<p class="interlude__phrase"><em>The page</em> is the demo.</p>
|
||||
</aside>
|
||||
|
||||
<!-- 04 · ANTI-PATTERNS — Slop, by name. Five named AI-UI tells, each
|
||||
with the Hallmark fix beside it. Replaces the original Without/
|
||||
With section (preserved as a <template> below). -->
|
||||
<section class="section reveal" style="--i:3" id="anti-patterns" aria-labelledby="s-anti">
|
||||
<header class="section__head">
|
||||
<p class="section-label">
|
||||
<span class="num">04</span><span class="divider">⁄</span><span>Anti-patterns</span>
|
||||
</p>
|
||||
<h2 class="section__title" id="s-anti">
|
||||
Slop, by name.
|
||||
</h2>
|
||||
</header>
|
||||
|
||||
<p class="anti__intro">
|
||||
Five tells the LLM reaches for by default. The Hallmark fix beside each. <code>hallmark audit</code> flags every one of these on existing code.
|
||||
</p>
|
||||
|
||||
<ol class="anti-list">
|
||||
|
||||
<li class="anti-row">
|
||||
<p class="anti-row__num tnum">01</p>
|
||||
<div class="anti-row__slop">
|
||||
<h3 class="anti-row__name">The purple-gradient hero.</h3>
|
||||
<p class="anti-row__desc">A hero with a background gradient from purple to blue or purple to pink, white centred text. The single most-recognised AI aesthetic.</p>
|
||||
</div>
|
||||
<div class="anti-row__fix">
|
||||
<p class="anti-row__fix-label">Hallmark</p>
|
||||
<p class="anti-row__fix-body">Pick a single anchor hue. One accent. No gradient backgrounds on heroes. If you want warmth, tint the neutrals.</p>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="anti-row">
|
||||
<p class="anti-row__num tnum">02</p>
|
||||
<div class="anti-row__slop">
|
||||
<h3 class="anti-row__name">Inter as display.</h3>
|
||||
<p class="anti-row__desc">Inter (or Roboto, or Open Sans) used as both display and body, no pairing face. A one-font page is a template page.</p>
|
||||
</div>
|
||||
<div class="anti-row__fix">
|
||||
<p class="anti-row__fix-label">Hallmark</p>
|
||||
<p class="anti-row__fix-body">Pair a distinctive display face with a refined body. Two faces minimum, never the same family doing both jobs.</p>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="anti-row">
|
||||
<p class="anti-row__num tnum">03</p>
|
||||
<div class="anti-row__slop">
|
||||
<h3 class="anti-row__name">Centred everything.</h3>
|
||||
<p class="anti-row__desc">Headline centred, body centred, button centred, section after section of centred columns. Symmetry as default.</p>
|
||||
</div>
|
||||
<div class="anti-row__fix">
|
||||
<p class="anti-row__fix-label">Hallmark</p>
|
||||
<p class="anti-row__fix-body">Bias the layout. Wide left margin, narrow right — or the reverse. Breaking symmetry once is enough to register intent.</p>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="anti-row">
|
||||
<p class="anti-row__num tnum">04</p>
|
||||
<div class="anti-row__slop">
|
||||
<h3 class="anti-row__name">The icon-tile feature card.</h3>
|
||||
<p class="anti-row__desc">Rounded rectangle, icon in a coloured square top-left, two-line heading, three-line copy, optional "Learn more →". The universal template.</p>
|
||||
</div>
|
||||
<div class="anti-row__fix">
|
||||
<p class="anti-row__fix-label">Hallmark</p>
|
||||
<p class="anti-row__fix-body">If you need feature cards, let them be asymmetric — vary sizes, vary alignments, pull the icon inline. Or drop the icon and lead with type.</p>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="anti-row">
|
||||
<p class="anti-row__num tnum">05</p>
|
||||
<div class="anti-row__slop">
|
||||
<h3 class="anti-row__name">The AI nav.</h3>
|
||||
<p class="anti-row__desc">Wordmark hard-left, four inline links centred, CTA button hard-right, sticky on scroll, hairline border-bottom. The shape every LLM ships.</p>
|
||||
</div>
|
||||
<div class="anti-row__fix">
|
||||
<p class="anti-row__fix-label">Hallmark</p>
|
||||
<p class="anti-row__fix-body">Pick a nav archetype that matches the page's genre — newspaper masthead, terminal command bar, edge-aligned minimal. The nav should tell you what kind of site you're on.</p>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
</ol>
|
||||
|
||||
<a class="section__pull section__pull--right" href="#s5-foundations" aria-label="Continue to Foundations section">
|
||||
<span class="section__pull-label">Read the rules</span>
|
||||
<span class="section__pull-arrow" aria-hidden="true">→</span>
|
||||
</a>
|
||||
</section>
|
||||
|
||||
<!-- DEPRECATED: original Section 04 (Without / with) — replaced by
|
||||
the Gallery of Shame above. Wrapped in <template> so the markup
|
||||
stays in this file but doesn't render. To revert: change the
|
||||
opening <template> tag to <section class="section reveal"
|
||||
style="--i:3" aria-labelledby="s4"> and the closing </template>
|
||||
to </section>, then remove the Anti-patterns section above. -->
|
||||
<template id="section-04-archive-without-with">
|
||||
<section class="section reveal" style="--i:3" aria-labelledby="s4">
|
||||
<header class="section__head">
|
||||
<p class="section-label">
|
||||
<span class="num">04</span><span class="divider">⁄</span><span>Without / with</span>
|
||||
@@ -695,7 +818,13 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<a class="section__pull" href="#s5-foundations" aria-label="Continue to Foundations section">
|
||||
<span class="section__pull-label">Read the rules</span>
|
||||
<span class="section__pull-arrow" aria-hidden="true">→</span>
|
||||
</a>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<!-- 05 · FOUNDATIONS — vertical list + active panel (CSS-only tabs) -->
|
||||
<section class="section reveal" style="--i:4" aria-labelledby="s5-foundations">
|
||||
@@ -818,6 +947,11 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<a class="section__pull section__pull--right" href="#install" aria-label="Continue to Install section">
|
||||
<span class="section__pull-label">Install in your editor</span>
|
||||
<span class="section__pull-arrow" aria-hidden="true">→</span>
|
||||
</a>
|
||||
</section>
|
||||
|
||||
<!-- 06 · INSTALL — one command, every harness -->
|
||||
|
||||
@@ -1069,6 +1069,51 @@ if (statesBtn && statesReadout) {
|
||||
});
|
||||
}
|
||||
|
||||
/* — Chapter rail — IntersectionObserver to highlight current section
|
||||
The rail is hidden via CSS below 80rem; we still wire the observer
|
||||
regardless so the highlight is correct if the user resizes up. */
|
||||
(() => {
|
||||
const railLinks = document.querySelectorAll(".chapter-rail [data-chapter-link]");
|
||||
if (!railLinks.length || !("IntersectionObserver" in window)) return;
|
||||
|
||||
const sections = Array.from(railLinks).map((a) => {
|
||||
const id = a.getAttribute("href").slice(1);
|
||||
return { link: a, section: document.getElementById(id) };
|
||||
}).filter((x) => x.section);
|
||||
|
||||
const visible = new Set();
|
||||
|
||||
const setCurrent = () => {
|
||||
let activeId = null;
|
||||
let topMost = Infinity;
|
||||
for (const id of visible) {
|
||||
const sec = document.getElementById(id);
|
||||
if (!sec) continue;
|
||||
const top = sec.getBoundingClientRect().top;
|
||||
if (top < topMost) { topMost = top; activeId = id; }
|
||||
}
|
||||
railLinks.forEach((a) => a.classList.remove("is-current"));
|
||||
if (activeId) {
|
||||
const link = document.querySelector(`.chapter-rail a[href="#${activeId}"]`);
|
||||
if (link) link.classList.add("is-current");
|
||||
}
|
||||
};
|
||||
|
||||
const railIO = new IntersectionObserver((entries) => {
|
||||
for (const e of entries) {
|
||||
const id = e.target.id;
|
||||
if (e.isIntersecting) visible.add(id);
|
||||
else visible.delete(id);
|
||||
}
|
||||
setCurrent();
|
||||
}, {
|
||||
rootMargin: "-25% 0px -55% 0px",
|
||||
threshold: 0,
|
||||
});
|
||||
|
||||
sections.forEach((s) => railIO.observe(s.section));
|
||||
})();
|
||||
|
||||
/* — Tab-click scroll-jump fix —————————————————————————————
|
||||
The CSS-only radio tab pattern in Section 04 (Without/With) and
|
||||
Section 05 (Foundations) places the radio inputs at top:0 of their
|
||||
|
||||
Reference in New Issue
Block a user