refactor(docs): rebuild the site around reading, not launching

The generated site had the shape of a product launch page: a near-black navy
ground with mint/coral radial glows, a gradient-clipped headline, a glowing
pill badge, nine emoji feature cards and scroll-reveal animations. None of it
told a reader anything about the language.

It is now typographic and light-first — a warm paper ground, a serif display
face, one ink-blue accent used only where it means something, and rules
instead of floating cards. Colour is reserved for code. Dark mode is the same
design with the ground inverted, defined once as tokens under a single
prefers-color-scheme block.

Structurally:

- base.css holds the tokens and shared chrome; site.css and docs.css hold what
  is specific to the landing page and the reference pages. They ship as linked
  files rather than being inlined into all 900+ pages, which takes the site
  from 16 MB to 5 MB and means a design change no longer needs a regenerate to
  be seen.
- api.css was dead — the generator never referenced it, rendering the API index
  with item.css — and is gone. docs.css replaces item.css and covers all four
  reference page kinds.
- Grids draw their separators as cell borders instead of bleeding a ruled
  background through gaps, so a final row with fewer cards than columns stops
  cleanly instead of leaving a grey hole. The feature card count is not a
  multiple of the column count at any breakpoint.
- Inline code loses its tinted chip; in a language reference, a box behind
  every keyword turns a paragraph into confetti.
- Fonts are the platform's own, so the site makes no webfont request.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-05 01:47:36 +03:00
parent 583783449a
commit 2f3833a295
12 changed files with 1046 additions and 571 deletions

View file

@ -8,12 +8,8 @@
<meta property="og:title" content="%(ogt)s">
<meta property="og:description" content="%(ogd)s">
<meta property="og:type" content="website">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
<style>
%(css)s
</style>
<link rel="stylesheet" href="base.css">
<link rel="stylesheet" href="site.css">
</head>
<body>
<header class="nav">
@ -30,7 +26,6 @@
<section class="hero">
<div class="wrap hero-grid">
<div>
<span class="pill"><span class="dot"></span> %(pill)s</span>
<h1>%(title_pre)s <span class="accent">%(title_accent)s</span></h1>
<p class="lead">%(lead)s</p>
<div class="cta-row">
@ -38,23 +33,20 @@
<a class="btn btn-ghost" href="%(scta_h)s">%(scta_l)s</a>
</div>
</div>
<div class="code-card reveal">
<div class="code-top">
<span class="tl r"></span><span class="tl y"></span><span class="tl g"></span>
<span class="code-name">%(hero_name)s</span>
</div>
<div class="code-card">
<div class="code-top"><span class="code-name">%(hero_name)s</span></div>
<pre data-lang="ludic">%(hero_code)s</pre>
</div>
</div>
<div class="wrap" style="margin-top:52px">
<div class="pipeline reveal">%(stages)s</div>
<p style="text-align:center;color:var(--muted);font-size:13.5px;margin:14px 0 0">%(pipenote)s</p>
<div class="wrap">
<div class="pipeline">%(stages)s</div>
<p class="pipe-note">%(pipenote)s</p>
</div>
</section>
<section id="features">
<div class="wrap">
<div class="sec-head reveal">
<div class="sec-head">
<div class="kicker">%(feat_kicker)s</div>
<h2>%(feat_title)s</h2>
<p>%(feat_intro)s</p>
@ -63,24 +55,24 @@
</div>
</section>
<section id="showcase" style="padding-top:24px">
<section id="showcase">
<div class="wrap">
<div class="sec-head reveal">
<div class="sec-head">
<div class="kicker">%(sc_kicker)s</div>
<h2>%(sc_title)s</h2>
<p>%(sc_intro)s</p>
</div>
<div class="tabs reveal" id="tabs"></div>
<div class="tabs" id="tabs" role="tablist"></div>
<div id="panels"></div>
<div class="showcase-note reveal"><span class="b">↳</span><span id="note"></span></div>
<div class="showcase-note"><span class="b">↳</span><span id="note"></span></div>
</div>
</section>
<section style="padding-top:24px">
<section id="philosophy">
<div class="wrap">
<div class="banner reveal">
<div class="banner">
<div>
<div class="kicker" style="color:var(--blue)">%(phil_kicker)s</div>
<div class="kicker">%(phil_kicker)s</div>
<h2>%(phil_title)s</h2>
%(phil_paras)s
</div>
@ -91,40 +83,37 @@
<section id="start">
<div class="wrap">
<div class="sec-head reveal">
<div class="sec-head">
<div class="kicker">%(start_kicker)s</div>
<h2>%(start_title)s</h2>
<p>%(start_intro)s</p>
</div>
<div class="steps">
<div>%(steps)s</div>
<div class="term reveal">
<div class="code-top">
<span class="tl r"></span><span class="tl y"></span><span class="tl g"></span>
<span class="code-name">%(term_name)s</span>
</div>
<div class="term">
<div class="code-top"><span class="code-name">%(term_name)s</span></div>
<pre>%(term)s</pre>
</div>
</div>
</div>
</section>
<section id="editors" style="padding-top:24px">
<section id="editors">
<div class="wrap">
<div class="sec-head reveal">
<div class="sec-head">
<div class="kicker">%(ed_kicker)s</div>
<h2>%(ed_title)s</h2>
<p>%(ed_intro)s</p>
</div>
<div class="editors reveal">%(eds)s</div>
<p style="margin-top:22px;color:var(--muted);font-size:14.5px">%(ed_note)s</p>
<div class="editors">%(eds)s</div>
<p class="ed-note">%(ed_note)s</p>
</div>
</section>
<footer>
<div class="wrap foot-in">
<div>
<div class="brand" style="margin-bottom:8px"><span class="logo">L</span> %(brand)s</div>
<div class="brand" style="margin-bottom:6px"><span class="logo">L</span> %(brand)s</div>
<div class="muted">%(tagline)s</div>
</div>
<nav class="foot-links">%(footlinks)s</nav>
@ -138,30 +127,34 @@ const SAMPLES = %(samples)s;
const tabsEl = document.getElementById("tabs");
const panelsEl = document.getElementById("panels");
const noteEl = document.getElementById("note");
SAMPLES.forEach((s, idx)=>{
SAMPLES.forEach((s, idx) => {
const t = document.createElement("button");
t.className = "tab" + (idx===0 ? " active" : "");
t.className = "tab" + (idx === 0 ? " active" : "");
t.type = "button";
t.setAttribute("role", "tab");
t.setAttribute("aria-selected", idx === 0 ? "true" : "false");
t.textContent = s.label;
t.onclick = ()=>select(idx);
t.onclick = () => select(idx);
tabsEl.appendChild(t);
const card = document.createElement("div");
card.className = "code-card panel-code" + (idx===0 ? " active" : "");
card.className = "code-card panel-code" + (idx === 0 ? " active" : "");
card.innerHTML =
'<div class="code-top"><span class="tl r"></span><span class="tl y"></span><span class="tl g"></span><span class="code-name">'
+ s.name + '</span></div><pre>' + Ludic.highlight(s.code) + '</pre>';
'<div class="code-top"><span class="code-name"></span></div><pre></pre>';
card.querySelector(".code-name").textContent = s.name;
card.querySelector("pre").innerHTML = Ludic.highlight(s.code);
panelsEl.appendChild(card);
});
function select(idx){
[...tabsEl.children].forEach((t,i)=>t.classList.toggle("active", i===idx));
[...panelsEl.children].forEach((p,i)=>p.classList.toggle("active", i===idx));
function select(idx) {
[...tabsEl.children].forEach((t, i) => {
const on = i === idx;
t.classList.toggle("active", on);
t.setAttribute("aria-selected", on ? "true" : "false");
});
[...panelsEl.children].forEach((p, i) => p.classList.toggle("active", i === idx));
noteEl.textContent = SAMPLES[idx].note;
}
noteEl.textContent = SAMPLES[0].note;
Ludic.installCards();
const io = new IntersectionObserver((entries)=>{
entries.forEach(e=>{ if(e.isIntersecting){ e.target.classList.add("in"); io.unobserve(e.target); } });
},{threshold:.12});
document.querySelectorAll(".reveal").forEach(el=>io.observe(el));
</script>
</body>
</html>