Echte Initialen mit initial-letter
Eine echte Initiale, die der Browser selbst auf das Zeilenraster setzt: oben bündig mit der Versalhöhe, unten auf der Grundlinie der dritten Zeile.
Der erste Buchstabe eines Artikels wird über ::first-letter gewählt und mit initial-letter auf eine genaue Zahl von Textzeilen vergrößert. Der Browser setzt die Initiale oben auf die Versalhöhe der ersten Zeile und unten auf die Grundlinie der letzten. initial-letter: 3 ist drei Zeilen hoch und drei tief; initial-letter: 3 1 ist erhaben und sinkt nur eine Zeile ein.
Ein klassisches Buch- und Magazinsignal: Die Initiale markiert den Einstieg in einen langen Text und gibt Editorial-Seiten Rhythmus und Handwerk. Mit initial-letter sitzt sie ohne Nachjustieren auf dem Zeilenraster.
Wann einsetzen
Long-Reads, Insights und Fachbeiträge, Fallstudien, Essays, Journal- und Magazin-Layouts, erzählende Über-uns-Texte.
Wann nicht
Kurze Teaser, Listen, UI-Texte. Absätze, die mit Zitat, Zahl oder Link beginnen. Mehr als eine Initiale pro Seite.
Varianten
- Eingesenkt über 3 bis 4 Zeilen (initial-letter: 3)
- Erhaben (initial-letter: 3 1)
- Serif-Initiale in Grotesk-Text, in Markenfarbe
- Initiale auf farbiger Fläche
- Mit Kapitälchen in der ersten Zeile (::first-line, font-variant-caps)
Umsetzung
article p:first-of-type::first-letter { font-family: var(--serif); color: var(--marke); margin-inline-end: .1em } und in @supports (initial-letter: 3) or (-webkit-initial-letter: 3) { … { -webkit-initial-letter: 3; initial-letter: 3 } }. Ohne Unterstützung bleibt nur der farbige Serif-Buchstabe in normaler Größe. Bewusst kein float-Rückfall, der leicht verrutscht. Für schmale Spalten per Container Query auf 2 reduzieren. Browserstand laut MDN (31.10.2026): Chrome und Edge ab 110 teilweise, Safari nur mit -webkit-Präfix, Firefox nicht.
Robust umsetzen
Telefon. Auf schmalen Spalten auf 2 Zeilen reduzieren (Container oder Media Query), damit neben der Initiale noch Wörter Platz haben.
Ohne Bewegung. Nicht relevant, das Muster bewegt sich nicht.
Barrierefrei. Der Buchstabe bleibt Teil des Wortes im DOM; Screenreader lesen das Wort normal, ein eigenes <span> mit aria-hidden ist nicht nötig. Kontrast der Initialfarbe prüfen.
Code
<!DOCTYPE html>
<!--
Echte Initialen mit initial-letter
Fundus · Typo-Techniken · https://cecodesign.de/lab/fundus/typo-techniken/initial-letter-initialen/
Eigenständige Datei. Bilder liegen hier unter bilder/ – eigene einsetzen.
-->
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Echte Initialen mit initial-letter · Fundus-Demo</title>
<script>document.documentElement.classList.add('js');if(matchMedia('(prefers-reduced-motion: reduce)').matches)document.documentElement.classList.add('reduziert')</script>
<style>
@font-face { font-family: 'Newsreader'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/newsreader:vf@latest/latin-opsz-normal.woff2') format('woff2-variations'); font-weight: 200 800; font-style: normal; font-display: swap; }
@font-face { font-family: 'Newsreader'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/newsreader:vf@latest/latin-opsz-italic.woff2') format('woff2-variations'); font-weight: 200 800; font-style: italic; font-display: swap; }
@font-face { font-family: 'Schibsted Grotesk'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/schibsted-grotesk:vf@latest/latin-wght-normal.woff2') format('woff2-variations'); font-weight: 400 900; font-style: normal; font-display: swap; }
:root {
/* Rezept „Kanzlei & Beratung“: Nachtblau auf warmen Graus, Cyan nur für Links und Fokus */
--grund: #f9f8f8; --flaeche: #efebeb; --linie: #d2cdc7; --marke: #1e1e50; --text: #212529; --meta: #73645a; --akzent: #00bee6;
--kurve: cubic-bezier(0.4, 0, 0.22, 1);
--farbe: 125ms; --klapp: 250ms; --menue: 330ms;
--serif: 'Newsreader', Georgia, serif;
--sans: 'Schibsted Grotesk', system-ui, sans-serif;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html { background: var(--grund); color: var(--text); font: 400 1.0625rem/1.6 var(--sans); -webkit-font-smoothing: antialiased; }
body { min-height: 100svh; padding: clamp(14px, 2.4vw, 32px) clamp(16px, 3.4vw, 52px) 64px; }
.meta { font: 500 .8125rem/1.45 var(--sans); color: var(--meta); }
.ziff { font-variant-numeric: tabular-nums; }
.klein { font: 500 .75rem/1.4 var(--sans); letter-spacing: .08em; text-transform: uppercase; }
a { color: inherit; text-decoration: none; }
:focus-visible { outline: 2px solid var(--akzent); outline-offset: 3px; }
/* ——— Bedienleiste (im Fluss) ——— */
.leiste { max-width: 1240px; margin: 0 auto; display: flex; flex-wrap: wrap; gap: 12px 24px; align-items: center; padding: 8px 0 18px; border-bottom: 1px solid var(--linie); color: var(--meta); }
.gruppe { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.wahl { display: inline-flex; gap: 2px; padding: 3px; border: 1px solid var(--linie); border-radius: 4px; background: #fff; }
.wahl button, .schalter { font: 500 .8125rem/1 var(--sans); color: var(--meta); background: none; border: 0; border-radius: 2px; padding: 8px 11px; cursor: pointer; transition: color var(--farbe) var(--kurve), background-color var(--farbe) var(--kurve); white-space: nowrap; }
.wahl button:hover, .schalter:hover { color: var(--marke); }
.wahl button[aria-pressed="true"] { background: var(--marke); color: var(--grund); }
.schalter { border: 1px solid var(--linie); background: #fff; display: inline-flex; align-items: center; gap: 8px; border-radius: 4px; }
.schalter::before { content: ""; width: 12px; height: 12px; border-radius: 2px; border: 1px solid currentColor; transition: background-color var(--farbe) var(--kurve); }
.schalter[aria-pressed="true"] { color: var(--marke); }
.schalter[aria-pressed="true"]::before { background: var(--marke); border-color: var(--marke); box-shadow: inset 0 0 0 2px #fff; }
.hinweis { flex-basis: 100%; color: var(--marke); text-transform: none; letter-spacing: 0; }
.hinweis[hidden] { display: none; }
/* ——— Kanzlei-Seite ——— */
.seite { max-width: 1240px; margin: 0 auto; }
.kopf { display: flex; align-items: center; gap: 28px; padding: 20px 0; border-bottom: 1px solid var(--linie); }
.marke { display: grid; line-height: 1.15; color: var(--marke); }
.marke b { font: 600 1rem/1.2 var(--sans); letter-spacing: .01em; }
.marke span { font-size: .75rem; color: var(--meta); letter-spacing: .02em; }
.nav { display: flex; gap: 24px; margin-left: auto; font: 500 .9375rem/1 var(--sans); }
.nav a { padding: 6px 0; box-shadow: inset 0 -1px 0 transparent; transition: box-shadow var(--farbe) var(--kurve), color var(--farbe) var(--kurve); }
.nav a:hover { box-shadow: inset 0 -1px 0 var(--marke); }
.nav a[aria-current] { color: var(--marke); box-shadow: inset 0 -2px 0 var(--marke); }
.knopf { display: inline-flex; padding: 11px 16px; border: 1px solid var(--marke); color: var(--marke); font: 500 .875rem/1 var(--sans); border-radius: 2px; transition: background-color var(--farbe) var(--kurve), color var(--farbe) var(--kurve); white-space: nowrap; }
.knopf:hover { background: var(--marke); color: var(--grund); }
.beitrag { display: grid; grid-template-columns: minmax(0, 190px) minmax(0, 680px) minmax(0, 250px); justify-content: space-between; gap: 0 clamp(28px, 4vw, 64px); padding-top: clamp(30px, 4.6vw, 64px); }
.schiene { display: grid; align-content: start; gap: 18px; padding-top: 10px; border-top: 1px solid var(--marke); }
.schiene dt { margin-bottom: 2px; }
.schiene dd { font-size: .9375rem; color: var(--text); }
.rubrik { color: var(--marke); margin-bottom: 18px; }
.rubrik a { box-shadow: inset 0 -1px 0 var(--akzent); }
h1 { font: 400 clamp(2rem, 1.3rem + 2.6vw, 3.5rem)/1.1 var(--serif); font-optical-sizing: auto; letter-spacing: -0.01em; color: var(--marke); text-wrap: balance; max-width: 18ch; }
.autor { display: flex; align-items: center; gap: 12px; margin: 26px 0 clamp(30px, 3.6vw, 44px); padding-bottom: 22px; border-bottom: 1px solid var(--linie); }
.autor i { display: grid; place-items: center; width: 40px; height: 40px; border-radius: 50%; background: var(--flaeche); color: var(--marke); font: 500 .8125rem/1 var(--sans); font-style: normal; letter-spacing: .04em; }
.autor b { display: block; font-weight: 500; color: var(--text); font-size: .9375rem; }
/* Lauftext */
.text { position: relative; container-type: inline-size; }
.text p { max-width: 65ch; text-wrap: pretty; hyphens: auto; }
.text p + p { margin-top: 1.1em; }
.text h2 { font: 400 clamp(1.625rem, 1.2rem + 1.6vw, 2.5rem)/1.15 var(--serif); font-optical-sizing: auto; color: var(--marke); letter-spacing: -0.01em; margin: 1.6em 0 .6em; }
.text blockquote { margin: 1.6em 0; padding-left: 22px; border-left: 2px solid var(--marke); font: italic 400 clamp(1.375rem, 1.1rem + .8vw, 1.75rem)/1.3 var(--serif); color: var(--marke); max-width: 30ch; }
.text blockquote footer { margin-top: 10px; font: 500 .8125rem/1.4 var(--sans); font-style: normal; color: var(--meta); }
/* ——— Das Muster ——— */
.erster::first-letter {
font-family: var(--serif); font-weight: 400; font-style: normal; color: var(--marke);
margin-inline-end: .1em;
}
@supports (initial-letter: 3) or (-webkit-initial-letter: 3) {
[data-art="einsenk"] .erster::first-letter, [data-art="flaeche"] .erster::first-letter { -webkit-initial-letter: 3; initial-letter: 3; }
[data-art="erhaben"] .erster::first-letter { -webkit-initial-letter: 3 1; initial-letter: 3 1; }
[data-art="flaeche"] .erster::first-letter { background: var(--marke); color: var(--grund); padding: .1em .16em .08em; margin-inline-end: .16em; }
/* schmale Spalte: nur zwei Zeilen tief */
@container (max-width: 26rem) {
[data-art="einsenk"] .erster::first-letter, [data-art="flaeche"] .erster::first-letter { -webkit-initial-letter: 2; initial-letter: 2; }
[data-art="erhaben"] .erster::first-letter { -webkit-initial-letter: 2 1; initial-letter: 2 1; }
}
}
/* Rückfall (und Schalter „ohne Unterstützung“): nur Farbe und Serif, normale Größe */
.ohne .erster::first-letter { -webkit-initial-letter: normal !important; initial-letter: normal !important; background: none !important; color: var(--marke) !important; padding: 0 !important; margin-inline-end: 0 !important; }
.kaps .erster::first-line { font-variant-caps: all-small-caps; letter-spacing: .05em; }
.erster { transition: opacity var(--klapp) var(--kurve); }
.wechsel .erster { opacity: 0; transition-duration: var(--farbe); }
/* Zeilenraster */
.raster { position: absolute; inset: 0; pointer-events: none; transition: opacity var(--klapp) var(--kurve); }
.aus .raster { opacity: 0; }
.raster > * { position: absolute; transition: top var(--klapp) var(--kurve), opacity var(--klapp) var(--kurve); }
.gl { height: 0; border-top: 1px solid rgba(30, 30, 80, .12); }
.gl.tief { border-top-color: rgba(0, 190, 230, .9); }
.gl.kap { border-top: 1px dashed rgba(0, 150, 190, .9); }
.nr { font: 600 10px/1 var(--sans); color: #087894; font-variant-numeric: tabular-nums; letter-spacing: .04em; }
.etikett { font: 600 10.5px/1 var(--sans); letter-spacing: .06em; text-transform: uppercase; color: #fff; background: #087894; padding: 4px 6px; border-radius: 2px; white-space: nowrap; }
/* Seitenspalte */
.seitlich { display: grid; align-content: start; gap: 22px; }
.karte { padding: 20px; background: var(--flaeche); }
.karte h3 { font: 500 .9375rem/1.3 var(--sans); color: var(--marke); margin: 6px 0 2px; }
.karte p { font-size: .875rem; color: var(--text); }
.karte a { display: inline-block; margin-top: 10px; font-size: .875rem; color: var(--marke); box-shadow: inset 0 -1px 0 var(--akzent); }
.inhalt { list-style: none; border-top: 1px solid var(--linie); font-size: .875rem; }
.inhalt li { padding: 9px 0; border-bottom: 1px solid var(--linie); color: var(--text); }
.inhalt li:first-child { color: var(--marke); font-weight: 500; }
/* ——— Werte ——— */
.werte { max-width: 1240px; margin: 44px auto 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr)); gap: 4px 44px; padding-top: 18px; border-top: 1px solid var(--linie); font-size: .8125rem; }
.werte div { display: flex; gap: 14px; padding: 5px 0; border-bottom: 1px solid var(--linie); }
.werte dt { min-width: 10.5em; color: var(--meta); }
.werte dd { overflow-wrap: anywhere; color: var(--text); }
/* ——— Vorschau ——— */
.zustand { display: none; }
.vorschau body { overflow: hidden; padding: 24px 40px; }
.vorschau .leiste, .vorschau .werte, .vorschau .schiene, .vorschau .seitlich, .vorschau .nav, .vorschau .autor, .vorschau .rubrik { display: none; }
.vorschau .zustand { display: flex; justify-content: space-between; max-width: 1240px; margin: 0 auto 4px; color: var(--meta); }
.vorschau .zustand b { color: var(--marke); }
.vorschau .beitrag { grid-template-columns: minmax(0, 1fr); padding-top: 34px; }
.vorschau h1 { font-size: 2.6rem; max-width: 24ch; margin-bottom: 30px; }
.vorschau .text { font-size: 1.3rem; line-height: 1.55; }
.vorschau .text p { max-width: 56ch; }
:root.reduziert * { transition-duration: .01ms !important; animation-duration: .01ms !important; }
@media (max-width: 1100px) {
.beitrag { grid-template-columns: minmax(0, 170px) minmax(0, 1fr); }
.seitlich { grid-column: 2; margin-top: 40px; grid-template-columns: 1fr 1fr; }
}
@media (max-width: 760px) {
.beitrag { grid-template-columns: minmax(0, 1fr); }
.schiene { grid-row: 2; grid-template-columns: 1fr 1fr; margin: 0 0 26px; gap: 12px 18px; }
.seitlich { grid-column: 1; grid-template-columns: 1fr; }
.nav { display: none; }
.kopf { justify-content: space-between; }
.leiste { gap: 10px 12px; }
.wahl button { padding: 9px 10px; }
.werte dt { min-width: 8em; }
}
@media (max-width: 440px) {
.marke span { font-size: .6875rem; letter-spacing: 0; white-space: nowrap; }
.kopf .knopf { padding-inline: 12px; }
}
/* 320 px: Zusatz der Marke und Umschalter dürfen umbrechen */
@media (max-width: 380px) {
.marke { min-width: 0; }
.marke span { white-space: normal; }
.kopf { gap: 12px; }
.wahl { flex-wrap: wrap; }
}
@media (hover: none) {
.wahl button:hover, .schalter:hover { color: inherit; }
.wahl button[aria-pressed="true"]:hover { color: var(--grund); }
.nav a:hover { box-shadow: none; }
.knopf:hover { background: none; color: var(--marke); }
}
/* Kontrastmodus: Knöpfe mit Rahmen */
@media (forced-colors: active) { button, [role=button], [role=tab], [role=switch] { border: 1px solid ButtonText !important; } }
</style>
</head>
<body>
<div class="leiste meta" role="toolbar" aria-label="Steuerung der Demo">
<div class="gruppe">
<span>Initiale</span>
<div class="wahl" role="group" aria-label="Art der Initiale">
<button type="button" data-art="einsenk" aria-pressed="true">Eingesenkt · 3</button>
<button type="button" data-art="erhaben" aria-pressed="false">Erhaben · 3 1</button>
<button type="button" data-art="flaeche" aria-pressed="false">Mit Fläche</button>
</div>
</div>
<button type="button" class="schalter" data-kaps aria-pressed="false">Kapitälchen 1. Zeile</button>
<button type="button" class="schalter" data-ohne aria-pressed="false">Ohne Unterstützung</button>
<button type="button" class="schalter" data-raster aria-pressed="true">Zeilenraster</button>
<p class="hinweis" data-hinweis hidden>Dieser Browser kennt initial-letter nicht (etwa Firefox). Zu sehen ist der Rückfall: der erste Buchstabe in Serif und Markenfarbe, ohne Vergrößerung.</p>
</div>
<p class="zustand meta" aria-hidden="true"><span>initial-letter · <b data-zustand>Eingesenkt · 3</b></span><span data-zustand-wert></span></p>
<div class="seite">
<header class="kopf">
<a class="marke" href="#start"><b>Brandes Hagemeister</b><span>Rechtsanwälte · Steuerberater · Lübeck</span></a>
<nav class="nav" aria-label="Beispielnavigation"><a href="#leistungen">Leistungen</a><a href="#branchen">Branchen</a><a href="#koepfe">Köpfe</a><a href="#insights" aria-current="page">Insights</a><a href="#karriere">Karriere</a></nav>
<a class="knopf" href="#erstgespraech">Erstgespräch</a>
</header>
<article class="beitrag" data-beitrag data-art="einsenk">
<dl class="schiene meta">
<div><dt class="klein">Datum</dt><dd><time datetime="2026-10-28">28.10.2026</time></dd></div>
<div><dt class="klein">Lesezeit</dt><dd>6 Minuten</dd></div>
<div><dt class="klein">Rechtsgebiet</dt><dd>Gesellschaftsrecht, Erbrecht</dd></div>
</dl>
<div class="spalte">
<p class="rubrik klein"><a href="#nachfolge">Insight · Unternehmensnachfolge</a></p>
<h1>Erst der Gesellschafts­vertrag, dann das Testament</h1>
<div class="autor"><i aria-hidden="true">WH</i><span><b>Dr. Wiebke Hagemeister</b><span class="meta">Partnerin, Fachanwältin für Erbrecht</span></span></div>
<div class="text" data-text>
<p class="erster" data-erster>Wer ein Unternehmen an die nächste Generation übergeben will, denkt meist zuerst an das Testament. In der Praxis entscheidet aber oft ein anderes Dokument: der Gesellschaftsvertrag. Er legt fest, ob ein Anteil überhaupt vererbt werden kann, an wen und zu welchen Bedingungen. Steht im Testament etwas anderes, setzt sich in aller Regel der Gesellschaftsvertrag durch.</p>
<p>Gerade bei Unternehmen, die über Jahrzehnte gewachsen sind, stammen diese Verträge häufig aus einer Zeit, in der die heutige Familie noch gar nicht absehbar war. Nachfolgeklauseln nennen Personen, die längst ausgeschieden sind, oder schließen Ehepartner pauschal aus. Beides lässt sich ändern, aber nur gemeinsam mit allen Gesellschaftern.</p>
<blockquote>„Das beste Testament hilft wenig, wenn der Gesellschaftsvertrag die Tür vorher schließt.“<footer>Dr. Wiebke Hagemeister</footer></blockquote>
<h2>Drei Fragen vor jedem Entwurf</h2>
<p>Wir beginnen jedes Nachfolgemandat mit einer Bestandsaufnahme: Welche Verträge gibt es, wer ist beteiligt, und welche Abfindung greift, wenn ein Erbe nicht in die Gesellschaft eintreten darf? Erst danach lohnt es sich, über Testament, Schenkung oder Stiftung zu sprechen.</p>
<div class="raster" data-raster-ebene aria-hidden="true"></div>
</div>
</div>
<aside class="seitlich" aria-label="Zum Beitrag">
<div class="karte">
<span class="klein meta">Ansprechpartnerin</span>
<h3>Dr. Wiebke Hagemeister</h3>
<p class="meta">Königstraße 41<br>23552 Lübeck</p>
<a class="ziff" href="#tel">0451 820 41 30</a>
</div>
<ol class="inhalt">
<li>Erst der Gesellschaftsvertrag</li>
<li>Alte Klauseln, neue Familie</li>
<li>Drei Fragen vor jedem Entwurf</li>
<li>Testament, Schenkung, Stiftung</li>
</ol>
</aside>
</article>
</div>
<dl class="werte" data-werte></dl>
<script>
(() => {
const html = document.documentElement;
const reduziert = () => html.classList.contains('reduziert');
const $ = (s, w = document) => w.querySelector(s);
const $$ = (s, w = document) => [...w.querySelectorAll(s)];
const beitrag = $('[data-beitrag]'), text = $('[data-text]'), erster = $('[data-erster]'), ebene = $('[data-raster-ebene]');
const kapsKnopf = $('[data-kaps]'), ohneKnopf = $('[data-ohne]'), rasterKnopf = $('[data-raster]');
const kann = !!(window.CSS && (CSS.supports('initial-letter', '3') || CSS.supports('-webkit-initial-letter', '3')));
if (!kann) $('[data-hinweis]').hidden = false;
const NAMEN = { einsenk: 'Eingesenkt · 3', erhaben: 'Erhaben · 3 1', flaeche: 'Mit Fläche' };
let art = 'einsenk', kaps = false, ohne = false, raster = true;
/* Grundlinie der letzten Zeile (Marke am Absatzende) und Versalhöhe (Sonde 10cap) */
const ende = document.createElement('i'); ende.style.cssText = 'display:inline-block;width:0;height:0;vertical-align:baseline'; erster.append(ende);
/* Grundlinie der ersten Zeile: Marke nach dem ersten Wort (nicht davor, sonst greift ::first-letter nicht) */
const anfang = document.createElement('i'); anfang.style.cssText = 'display:inline-block;width:0;height:0;vertical-align:baseline';
{ const t0 = erster.firstChild, k = t0.textContent.indexOf(' '); t0.splitText(k); erster.insertBefore(anfang, t0.nextSibling); }
const sonde = document.createElement('i'); sonde.style.cssText = 'position:absolute;visibility:hidden;height:0;width:10cap'; erster.append(sonde);
const kannCap = !!(window.CSS && CSS.supports('width', '1cap'));
const schmal = () => text.getBoundingClientRect().width <= 26 * 16;
const zeilenTief = () => ohne || !kann ? 0 : (schmal() ? 2 : 3);
function el(cls, top, left, width) {
const d = document.createElement('div'); d.className = cls;
d.style.top = top + 'px'; d.style.left = left + 'px'; if (width != null) d.style.width = width + 'px';
ebene.append(d); return d;
}
function zeichnen() {
ebene.innerHTML = '';
const t = text.getBoundingClientRect(), p = erster.getBoundingClientRect();
const cs = getComputedStyle(erster);
const lh = parseFloat(cs.lineHeight) || parseFloat(cs.fontSize) * 1.6;
const letzte = ende.getBoundingClientRect().top - t.top;
const erste = anfang.getBoundingClientRect().top - t.top;
const n = Math.round((letzte - erste) / lh) + 1;
const tief = zeilenTief();
const breite = Math.min(p.width, t.width);
for (let i = 0; i < n; i++) {
const y = i === 0 ? erste : letzte - (n - 1 - i) * lh; /* Zeile 1 eigens gemessen, Rest vom Absatzende aus */
const markiert = art === 'erhaben' ? i === 0 && tief > 0 : i < tief;
el('gl' + (markiert ? ' tief' : ''), y, 0, breite);
if (markiert) { const z = el('nr', y - 11, -16); z.textContent = i + 1; }
}
const kap = kannCap ? sonde.getBoundingClientRect().width / 10 : parseFloat(cs.fontSize) * .7;
el('gl kap', erste - kap, 0, breite);
const e = el('etikett', erste - kap - 24, Math.max(0, breite - 190));
e.textContent = ohne || !kann ? 'Rückfall: Serif + Farbe' : (art === 'erhaben' ? `initial-letter: ${tief} 1 · sinkt 1 Zeile` : `initial-letter: ${tief} · ${tief} Zeilen tief`);
e.style.left = Math.max(0, breite - e.offsetWidth) + 'px';
werte(lh, kap);
}
let raf = 0;
function nachfuehren(ms = 300) {
cancelAnimationFrame(raf);
const bis = performance.now() + (reduziert() ? 0 : ms);
const schritt = () => { zeichnen(); if (performance.now() < bis) raf = requestAnimationFrame(schritt); };
schritt();
}
/* Werte */
const werteListe = $('[data-werte]');
function werte(lh, kap) {
const tief = zeilenTief();
const z = [
['Selektor', '.beitrag p:first-of-type::first-letter'],
['initial-letter', ohne || !kann ? 'normal (Rückfall)' : art === 'erhaben' ? `${tief} 1 (unter 26rem Spalte: 2 1)` : `${tief} (unter 26rem Spalte: 2)`],
['Initiale', `Newsreader 400, opsz auto, ${art === 'flaeche' && !ohne ? 'Grund #1E1E50, Schrift #F9F8F8, padding .1em .16em .08em' : 'Farbe #1E1E50'}`],
['Abstand rechts', art === 'flaeche' && !ohne ? 'margin-inline-end: .16em' : 'margin-inline-end: .1em'],
['Lauftext', `Schibsted Grotesk 400, 1.0625rem / 1.6 (${Math.round(lh * 10) / 10} px Zeile), max. 65ch`],
['Erste Zeile', kaps ? 'font-variant-caps: all-small-caps; letter-spacing: .05em' : 'normal'],
['Gemessen', `Versalhöhe Lauftext ${kap.toFixed(1)} px`],
['Unterstützung', kann ? 'initial-letter vorhanden (dieser Browser)' : 'fehlt: @supports-Rückfall aktiv'],
['Wechsel', 'opacity 125 ms aus, 250 ms ein, cubic-bezier(0.4, 0, 0.22, 1)'],
];
werteListe.innerHTML = z.map(([k, v]) => `<div><dt>${k}</dt><dd>${v}</dd></div>`).join('');
$('[data-zustand]').textContent = ohne || !kann ? 'Rückfall ohne Unterstützung' : NAMEN[art] + (kaps ? ' + Kapitälchen' : '');
$('[data-zustand-wert]').textContent = ohne || !kann ? 'initial-letter: normal' : (art === 'erhaben' ? `initial-letter: ${tief} 1` : `initial-letter: ${tief}`);
}
/* Zustände: kurzes Aus- und Einblenden, damit der Sprung nicht hart wirkt */
let wechselUhr = 0;
function wechsle(f) {
if (reduziert()) { f(); nachfuehren(0); return; }
clearTimeout(wechselUhr);
beitrag.classList.add('wechsel');
wechselUhr = setTimeout(() => { f(); beitrag.classList.remove('wechsel'); nachfuehren(320); }, 125);
}
function setzeArt(a) {
$$('button[data-art]').forEach(k => k.setAttribute('aria-pressed', String(k.dataset.art === a)));
wechsle(() => { art = a; beitrag.dataset.art = a; });
}
function setzeKaps(an) { kapsKnopf.setAttribute('aria-pressed', String(an)); wechsle(() => { kaps = an; beitrag.classList.toggle('kaps', an); }); }
function setzeOhne(an) { ohneKnopf.setAttribute('aria-pressed', String(an)); wechsle(() => { ohne = an; beitrag.classList.toggle('ohne', an); }); }
function setzeRaster(an) { raster = an; rasterKnopf.setAttribute('aria-pressed', String(an)); beitrag.classList.toggle('aus', !an); }
$$('button[data-art]').forEach(k => k.addEventListener('click', () => { pause(); setzeArt(k.dataset.art); }));
kapsKnopf.addEventListener('click', () => { pause(); setzeKaps(!kaps); });
ohneKnopf.addEventListener('click', () => { pause(); setzeOhne(!ohne); });
rasterKnopf.addEventListener('click', () => { pause(); setzeRaster(!raster); });
$$('a[href^="#"]').forEach(a => a.addEventListener('click', e => e.preventDefault()));
addEventListener('resize', () => zeichnen());
/* Vorführung */
let uhren = [];
function pause() { uhren.forEach(clearTimeout); uhren = []; }
function neustart() {
pause();
setzeRaster(true); kaps = false; ohne = false; beitrag.classList.remove('kaps', 'ohne');
kapsKnopf.setAttribute('aria-pressed', 'false'); ohneKnopf.setAttribute('aria-pressed', 'false');
setzeArt('einsenk');
}
function vorfuehren() {
pause();
const plan = [
[0, () => { if (ohne) setzeOhne(false); else neustart(); }],
[2200, () => setzeArt('erhaben')],
[4400, () => setzeArt('flaeche')],
[6600, () => { setzeArt('einsenk'); }],
[7400, () => setzeKaps(true)],
[9400, () => { setzeKaps(false); }],
[9800, () => setzeOhne(true)],
];
plan.forEach(([t, f]) => uhren.push(setTimeout(f, t)));
uhren.push(setTimeout(vorfuehren, 11800));
}
function weiter() { vorfuehren(); }
(document.fonts && document.fonts.ready ? document.fonts.ready : Promise.resolve()).then(() => {
Promise.all(['400 40px "Newsreader"', '400 17px "Schibsted Grotesk"'].map(f => document.fonts.load(f).catch(() => {}))).then(() => zeichnen());
});
zeichnen();
if (window.ResizeObserver) new ResizeObserver(() => zeichnen()).observe(text);
})();
</script>
</body>
</html>