Die Kombination aus zeilenexakter DOM-Textvermessung mittels document.createTreeWalker und Range.getClientRects() sowie kontinuierlichen zweidimensionalen Signed Distance Fields (2D SDF) löst ein klassisches Dilemma moderner Web-Oberflächen: Sie ersetzt starre, rechteckige CSS-Block-Bounding-Boxes durch die reale, treppenförmige Zeilenkontur des umbrochenen Textes. Auf dieser Basis platziert ein deterministischer 2D-Optimierer im Hero-Bereich unserer Startseite sowohl das interaktive WebGL-Porträt als auch ein physikbasiertes Hintergrund-Punktraster millimetergenau in die frei gewordenen Freiräume – ohne Überlappungen, ohne Layout-Shift (CLS = 0) und mit maximal zehn gebündelten Canvas-2D-Draw-Calls pro Frame.
Die folgende Architektur-Übersicht zeigt den qualitativen und quantitativen Unterschied zwischen klassischer CSS-Block-Geometrie, CSS Shapes und unserer sub-elementaren TreeWalker + 2D-SDF-Pipeline:
| Dimension | Klassisches CSS Grid / Flexbox (getBoundingClientRect) | CSS Shapes (shape-outside) | Sub-Element TreeWalker + 2D-SDF-Pipeline (hackenberg.tech) |
|---|---|---|---|
| Geometrische Auflösung | Grobe Element-Box (max-w-2xl); ignoriert vorzeitige Zeilenumbrüche und verschenkt rechtsseitigen Freiraum. | Fließtext umfließt ein statisches Float-Bild; keine Rückkopplung der Textkontur an Canvas-Hintergründe. | Zeilenexakte Range.getClientRects()-Vermessung jedes Textknotens; präzises 1D-Intervall-Merging (bands[]). |
| Dynamische Objekt-Platzierung | Starre Breakpoints (md:, lg:); führt auf Zwischenbreiten zu Sprüngen oder konservativ geschrumpften Bildern. | Nur für Inline-Floats nutzbar; kein globaler 2D-Kollisions-Solver für unabhängige UI-Layer. | Exakter 2D-Solver prüft konkurrierende Kandidaten (Rechts-Mittig vs. Oben neben <h1>) mit +4px Hysterese. |
| Hintergrund-Partikel & Konturschnitt | Rechteckiger Ausschnitt oder Übermalen von Text (schlechter Kontrast und visuelle Unruhe). | Keine Schnittstelle zu HTML5 Canvas oder WebGL-Partikelsystemen. | Kontinuierliches 2D-SDF (sdRoundRect) mit polynomieller -Verschmelzung (smin) und 32px-smoothstep-Fade. |
| Rendering-Effizienz (Canvas 2D) | Einzelnes ctx.fill() pro Partikel erzeugt hunderte State-Changes pro Frame bei individueller Opazität. | Nicht anwendbar (rein deklaratives Text-Wrapping im Browser-Layout). | 10-Bucket Opacity Quantisierung (alphaBin = 0..9) reduziert hunderte Punkte auf maximal 10 Draw-Calls/Frame. |
| Webfont- & First-Paint-Stabilität | Anfällig für Flash of Unstyled Text (FOUT) und nachträgliche Geometrie-Sprünge beim Font-Swap. | Reflow bei Webfont-Ladevorgang verschiebt Zeilenumbrüche sichtbar. | Synchronisiertes Zwei-Barrieren-Gate (document.fonts.ready portrait:ready) garantiert CLS = 0. |
TippLive-Implementierung im Quellcode:
Der vollständige Geometrie- und Platzierungs-Solver ist insrc/pages/index.astro(Zeilen 1264–1617) implementiert; das kontinuierliche 2D-SDF-Gitter und die Feder-Dämpfer-Physik befinden sich insrc/components/ReactiveDotGrid.astro.
Warum versagen klassische CSS-Block-Boxen bei asymmetrischen Hero-Layouts?
Klassische CSS-Block-Boxen versagen bei asymmetrischen Hero-Layouts, weil das Browser-Box-Modell jedes Block-Element (<h1>, <p>, <div>) als achsenparalleles Rechteck über seine volle Containerbreite (max-width) behandelt, selbst wenn einzelne Textzeilen durch den Zeilenumbruch deutlich früher enden. Dadurch entsteht am rechten Rand von Überschriften und Absätzen ein unsichtbarer „toter Raum” von oft 180 bis 340 Pixeln Breite, der für benachbarte Grafiken oder interaktive Hintergrund-Gitter gesperrt bleibt.
Seit der Formalisierung des optimalen Zeilenumbruchs durch [KP81] und moderner linearer Constraint-Solver für Benutzeroberflächen wie Cassowary [BBS01] gilt im UI-Engineering ein strukturelles Spannungsfeld: Typografie erzeugt im Flattersatz (Ragged Right) eine organische, stufenförmige Außenkontur, während Layout-Container in HTML und CSS streng rechteckige Boxen aufspannen.
Im Hero-Bereich unserer Startseite treffen fünf semantisch unterschiedliche Inhaltsschichten aufeinander:
- Hauptüberschrift (
#hero-heading): Zweizeiliges<h1>mit großem Schriftgrad (text-4xl md:text-6xl, Font Outfit Extrabold), dessen erste Zeile ("Hi, I am") oder zweite Zeile ("Dr. Georg Hackenberg") je nach Viewport-Breite unterschiedlich weit nach rechts ragt. - Untertitel (
#hero-subtitle): Mehrzeilige Rollenbeschreibung mit Bullet-Separatoren. - Biografie-Absatz (
#hero-bio): Fließtext mit eingebetteten<strong>-Hervorhebungen und Inline-Emojis. - Entity-Trust-Pills (
#hero-trust-pills): Flex-Wrap-Zeilen aus sechs abgerundeten Glassmorphism-Badges (FH Upper Austria, TU Munich, RWTH Aachen, Uni Mannheim, CADdrive, Mentawise). - Aktionsleiste (
#hero-actions-row): Primärer Call-to-Action-Button (#hero-contact-btn) neben vier quadratischen Social-Media-Schaltflächen (#hero-social-group).
Verwendet man zur Kollisionsvermeidung die Standardmethode Element.getBoundingClientRect() auf den Block-Elementen <h1> und <p>, meldet der Browser für jeden Absatz konstant die volle CSS-Maximalbreite (max-w-2xl, also ). Auf einem typischen Tablet- oder kompakten Laptop-Viewport ( bis ) bleibt rechts neben dieser breiten Block-Box kaum Platz – obwohl in Wahrheit neben der kürzeren <h1>-Überschrift oder neben den umbrochenen Pill-Reihen hunderte Quadratpixel völlig frei wären.
Um diesen verschenkten Raum zurückzugewinnen, müssen wir die Ebene der CSS-Block-Container verlassen und die tatsächliche optische Belegung jeder einzelnen Textzeile und jedes Inline-Badges vermessen.
Wie extrahiert man zeilenexakte Text-Konturen mit TreeWalker und Range.getClientRects()?
Die zeilenexakte Extraktion einer Text-Kontur gelingt durch die Kombination des DOM-Iterators document.createTreeWalker(el, NodeFilter.SHOW_TEXT) mit der CSSOM-View-Methode Range.getClientRects(). Während der TreeWalker sämtliche Textknoten auch über verschachtelte Inline-Elemente (<span>, <strong>) hinweg traversiert, liefert ein darüber gelegtes Range-Objekt für jeden Zeilenumbruch ein separates, pixelgenaues Client-Rechteck der tatsächlich gerenderten Glyphen.
Würde man lediglich el.getClientRects() auf dem Eltern-Element <p> aufrufen, erhielte man weiterhin nur eine einzige große Block-Box. Erst wenn man auf die Blattknoten des DOM-Baums (Text-Nodes) hinabsteigt und deren Inhalt über document.createRange() markiert, schlüsselt die Layout-Engine des Browsers gemäß W3C CSSOM View Specification [W3C16] jeden mehrzeiligen Textknoten in ein DOMRectList-Array seiner einzelnen Bildschirmzeilen auf.
Schritt 1: Tiefentraversierung aller Textknoten via TreeWalker
Da unsere Überschrift Farbverlaufs-Spans (<span class="heading-gradient-brand">) und der Biografie-Text zahlreiche <strong>-Elemente enthält, reicht eine flache Iteration über childNodes nicht aus. Der im WHATWG DOM Standard [Wha24] spezifizierte TreeWalker mit der Bitmaske NodeFilter.SHOW_TEXT besucht jeden Textknoten in Dokumentreihenfolge ohne Rekursions-Overhead:
const gridRect = heroGrid.getBoundingClientRect();
const rawRects = [];
function collectLineRects(el) {
if (!el) return;
const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
let node;
while ((node = walker.nextNode())) {
if (!node.textContent || !node.textContent.trim()) continue;
const r = document.createRange();
r.selectNodeContents(node);
const clientRects = r.getClientRects();
for (let i = 0; i < clientRects.length; i++) {
const cr = clientRects[i];
if (cr.width < 2 || cr.height < 2) continue;
rawRects.push({
top: cr.top - gridRect.top,
bottom: cr.bottom - gridRect.top,
right: cr.right - gridRect.left
});
}
}
}
Für die Trust-Pills (#hero-trust-pills) und die Aktionsleiste (#hero-actions-row) ergänzen wir rawRects um die exakten Außenkanten jedes sichtbaren Kind-Buttons (child.getBoundingClientRect()). Dadurch sind sämtliche interaktiven Badges und Textfragmente in einem einheitlichen lokalen Koordinatensystem relativ zu #hero-grid erfasst.
Schritt 2: 1D-Intervall-Merging zu horizontalen Konturbändern (bands[])
Weil eine einzelne visuelle Bildschirmzeile aus mehreren DOM-Textknoten bestehen kann (beispielsweise normaler Text gefolgt von einem <strong>-Knoten in derselben Zeile), liefert rawRects zunächst mehrere Teilrechtecke auf gleicher vertikaler Höhe. Wir sortieren rawRects aufsteigend nach top und verschmelzen vertikal überlappende Rechtecke zu disjunkten horizontalen Konturbändern bands[]:
rawRects.sort(function (a, b) { return a.top - b.top; });
const bands = [];
for (let i = 0; i < rawRects.length; i++) {
const r = rawRects[i];
if (bands.length === 0) {
bands.push({ top: r.top, bottom: r.bottom, right: r.right });
continue;
}
const last = bands[bands.length - 1];
const verticalOverlap = Math.min(last.bottom, r.bottom) - Math.max(last.top, r.top);
const minHeight = Math.min(last.bottom - last.top, r.bottom - r.top);
const isSameLine = Math.abs(last.top - r.top) < 10 || verticalOverlap > minHeight * 0.35;
if (isSameLine) {
last.top = Math.min(last.top, r.top);
last.bottom = Math.max(last.bottom, r.bottom);
last.right = Math.max(last.right, r.right);
} else {
bands.push({ top: r.top, bottom: r.bottom, right: r.right });
}
}
Zwei Fragmente gelten genau dann als dieselbe visuelle Zeile (isSameLine), wenn ihre Oberkanten um weniger als voneinander abweichen oder ihre vertikale Schnittmenge mehr als der kleineren Fragmenthöhe beträgt (verticalOverlap > minHeight * 0.35). Dieses Kriterium fängt auch Inline-Emojis (🏙️, 🏰, 🌄) und fetten Text mit minimal abweichender Baseline-Box zuverlässig in einem gemeinsamen Band ein.
Wie berechnet der 2D-Optimierer die kollisionsfreie Porträt-Platzierung?
Der 2D-Platzierungs-Optimierer ermittelt auf Basis der extrahierten Konturbänder bands[] in Echtzeit die größtmögliche, garantiert überlappungsfreie Quadratfläche für das WebGL-Porträt (DepthPortrait.astro), indem er zwei geometrische Kandidatenpositionen (Rechts-Mittig vs. Oben neben <h1>) gegeneinander auswertet. Reicht auf schmalen Smartphones der horizontale Freiraum für keinen der beiden Kandidaten bis zur Mindesthöhe der <h1>-Überschrift aus, blendet der Solver das Porträt sauber aus.
Die Hindernisfunktion maxRightInVerticalRange(yTop, yBottom)
Gegeben sei ein vertikales Zielintervall für das Porträtquadrat der Kantenlänge . Um jede optische Berührung mit den Textzeilen auszuschließen, definieren wir einen Sicherheitsabstand von PORTRAIT_GAP_X = 18 Pixeln horizontal und PORTRAIT_GAP_Y = 6 Pixeln vertikal. Die Funktion maxRightInVerticalRange bestimmt die am weitesten nach rechts ragende Textkante innerhalb dieses vertikalen Korridors:
Daraus folgt unmittelbar die minimal zulässige linke Startkoordinate reqLeft des Porträts im Intervall :
Wettbewerb der zwei Platzierungs-Kandidaten
Der Solver in src/pages/index.astro prüft nun zwei architektonisch sinnvolle Ankerpositionen innerhalb der Hero-Breite (gridWidth) und Hero-Höhe (gridHeight):
- Kandidat A – „Rechts-Mittig” (
sizeRightCenter): Auf Desktop- und breiten Tablet-Viewports soll das Porträt vertikal zentriert rechts neben dem gesamten Textblock schweben. Da eine größere Kantenlänge das vertikale Intervall aufweitet und dadurch potenziell längere Textzeilen in der Mitte einschließt, sucht der Solver absteigend ab das größte Quadrat , das kollisionsfrei in die verbleibende Breite passt:let sizeRightCenter = 0; let reqLeftRightCenter = 0; const maxPossibleRightCenter = Math.min(480, gridHeight); for (let S = maxPossibleRightCenter; S >= 1; S--) { const yTop = (gridHeight - S) * 0.5; const yBottom = (gridHeight + S) * 0.5; const reqLeft = maxRightInVerticalRange(yTop, yBottom) + PORTRAIT_GAP_X; if (reqLeft + S <= gridWidth) { sizeRightCenter = S; reqLeftRightCenter = reqLeft; break; } } - Kandidat B – „Oben neben
<h1>” (sizeTopRight): Auf mittleren Viewports, auf denen der Fließtext bereits fast die gesamte Breite einnimmt, die kurze<h1>-Überschrift (max-w-[21rem]) rechts oben jedoch noch eine Nische frei lässt, kann das Porträt bündig auf Höhe der<h1>([0, h1Height]) sitzen. Seine Kantenlänge ist exakt auf die Höhe der Überschrifth1Height() normiert, soferngridWidth - reqLeft >= h1Heighterfüllt ist.
Auswahlregel, Hysterese und automatische Zentrierung im Restraum
Damit das Porträt niemals kleiner als die Hauptüberschrift wirkt, gilt die harte Untergrenze . Zwischen beiden Kandidaten entscheidet der Solver zugunsten der zentrierten Hauptposition, sobald diese mindestens gleich groß oder innerhalb einer -Rundungstoleranz liegt (prefersCenter = sizeRightCenter + 4 >= sizeTopRight).
Sobald Position und Größe feststehen, wird das Porträt im verbleibenden freien Korridor zwischen reqLeft und (gridWidth) exakt mittig ausgerichtet:
Ergänzend passt die vorangestellte Routine in src/pages/index.astro den primären Kontakt-Button (#hero-contact-btn) dreistufig an den verfügbaren Platz an („Get in Touch” „Contact” Envelope-Icon), sodass die Aktionszeile auf keinem Viewport jemals ungewollt umbricht.
Wie formt ein 2D Signed Distance Field (SDF) das reaktive Punktraster um die Textkontur?
Ein zweidimensionales Signed Distance Field (2D SDF) ordnet jedem Gitterpunkt des Hintergrund-Canvas seinen kürzesten vorzeichenbehafteten euklidischen Abstand zur nächstgelegenen Textzeile und zum äußeren Hero-Rahmen zu. Durch die Verknüpfung abgerundeter Box-Distanzfunktionen (sdRoundRect) über Inigo Quilez’ polynomielles Smooth Minimum (smin) entsteht eine organisch gerundete L-förmige Ausschnittskontur mit weichem 32-Pixel-Opazitätsübergang.

Nachdem syncHeroResponsiveLayout() die Konturbänder bands[] berechnet hat, serialisiert das Skript die ganzzahligen Tripel [Math.round(b.top), Math.round(b.bottom), Math.ceil(b.right)] in das Attribut data-contour der Komponente ReactiveDotGrid.astro und feuert das Event dotgrid:layout.
1. Exakte 2D-Distanzfunktion eines abgerundeten Rechtecks (sdRoundRect)
Klassische SDF-Verfahren wurden in der Echtzeit-Computergrafik durch [Gre07] für randscharfe Vektortexturen etabliert und von [Qui18] für analytische 2D-Primitiven verallgemeinert. Für einen Gitterpunkt und ein achsenparalleles Rechteck mit Mittelpunkt , Halbausdehnung und Eckenradius lautet die exakte vorzeichenbehaftete Distanz:
Dabei gilt:
- : Der Punkt liegt innerhalb des Rechtecks.
- : Der Punkt liegt exakt auf der abgerundeten Konturlinie.
- : Der Punkt liegt außerhalb des Rechtecks, und entspricht dem exakten euklidischen Pixelabstand zur nächstgelegenen Kante oder Ecke.
2. Organische -Stufenverschmelzung mit Quilez’ Polynomial smin
Würde man die Distanz zum gesamten Textblock schlicht als hartes Minimum über alle Zeilenbänder berechnen, entstünden an jedem Zeilenumbruch-Rücksprung scharfe -Knicke im Gradientenfeld. Um diese Treppenstufen wie eine flüssige Membran abzurunden, setzen wir das polynomielle Smooth Minimum (smin) nach [Qui13] ein:
In ReactiveDotGrid.astro bestimmen wir für jeden Punkt des -Gitters die zwei kleinsten Distanzen zu allen um CLEARANCE_X = 16 Pixel und CLEARANCE_Y = 14 Pixel aufgeweiteten Zeilenbändern und verschmelzen sie mit Radius :
let d1 = Infinity;
let d2 = Infinity;
for (let i = 0; i < bands.length; i++) {
const b = bands[i];
const d = sdRoundRect(
x0,
y0,
-500,
b[0] - CLEARANCE_Y,
b[2] + CLEARANCE_X,
b[1] + CLEARANCE_Y,
12
);
if (d < d1) {
d2 = d1;
d1 = d;
} else if (d < d2) {
d2 = d;
}
}
const dText = d2 < Infinity ? smin(d1, d2, 12) : d1;
// Hard Culling innerhalb der Text-Sicherheitszone
if (dText <= 0) continue;
// Konkave Ausrundung (Fillet) zwischen Außenrahmen (-dOuter) und Textkontur (dText)
const distInsideOpen = smin(-dOuter, dText, 22);
Ein zweiter Aufruf smin(-dOuter, dText, 22) verrundet zusätzlich die konkaven Innenecken, an denen die Textkolumne oben und unten auf den abgerundeten Hero-Außenrahmen (dOuter) trifft. Anschließend moduliert eine kubische Hermite-Interpolation (smoothstep(0, 32, dText)) die Punkt-Opazität sanft von (direkt an der Sicherheitsgrenze) auf (ab Abstand).
Wie erreichen wir 60 FPS durch 10-Bucket Opacity Quantisierung und Feder-Dämpfer-Physik?
Um trotz hunderter individuell abgedunkelter und animierter Gitterpunkte konstant 60 FPS bei minimaler CPU-Last zu garantieren, quantisiert ReactiveDotGrid.astro die kontinuierliche SDF-Randopazität beim Gitteraufbau in exakt zehn diskrete Opazitäts-Buckets (alphaBin = 0..9). Während der Render-Schleife werden sämtliche Ruhelage-Punkte eines Buckets in einem einzigen gemeinsamen ctx.beginPath()- und ctx.fill()-Aufruf gezeichnet, was hunderte einzelne Canvas-State-Wechsel auf maximal zehn gebündelte Draw-Calls pro Frame reduziert.
10-Bucket Opacity Batching im HTML5 2D Canvas
Im unmittelbaren Modus (Immediate Mode) der HTML5 Canvas 2D API ist nicht die Berechnung von Math.sin oder ctx.arc der Flaschenhals, sondern das ständige Umschalten von ctx.fillStyle und das Absenden hunderter einzelner ctx.fill()-Befehle an den Grafiktreiber. Da sich die statische Randabschwächung alphaFactor = textAlpha * cornerAlpha jedes Punktes nur bei einem Resize ändert, diskretisieren wir sie bereits in rebuildLattice() in zehn Stufen und sortieren das Punkt-Array aufsteigend nach alphaBin:
const alphaFactor = textAlpha * cornerAlpha;
if (alphaFactor < 0.04) continue;
const alphaBin = Math.min(9, Math.floor(alphaFactor * 10));
// ...
dots.sort((a, b) => a.alphaBin - b.alphaBin);
In der draw()-Schleife iteriert der Renderer über die zehn Buckets , setzt ctx.fillStyle ein einziges Mal pro Bucket und fügt alle zugehörigen Kreise mittels ctx.moveTo(x + r, y) und ctx.arc(x, y, r, 0, Math.PI * 2) demselben Subpfad hinzu:
for (let bin = 0; bin < 10; bin++) {
if (idx >= len || dots[idx].alphaBin > bin) continue;
const binAlpha = baseAlpha * ((bin + 0.5) / 10);
ctx.fillStyle = `rgba(${baseRgb}, ${binAlpha.toFixed(3)})`;
ctx.beginPath();
while (idx < len && dots[idx].alphaBin === bin) {
const dot = dots[idx];
idx++;
if (dot.energy > 0.005) {
energizedDots.push(dot);
continue;
}
const x = dot.x0 + dot.dx;
const y = dot.y0 + dot.dy;
ctx.moveTo(x + r, y);
ctx.arc(x, y, r, 0, Math.PI * 2);
}
ctx.fill();
}
Lediglich die wenigen Punkte im unmittelbaren Maus-Einflussradius (dot.energy > 0.005), deren Farbe dynamisch zwischen dem ruhigen Slate-Ton und unseren Markenfarben (#3b82f6 Brand Blue / #a855f7 Brand Purple) interpoliert wird, werden in energizedDots gesammelt und separat gezeichnet.
Semi-implizite Feder-Dämpfer-ODE und Zwei-Barrieren-First-Paint-Gate
Bewegt sich der Mauszeiger im Abstand über das Gitter, erfahren die Punkte eine radiale Abstoßung mit glattem Sinus-Profil:
Die Rückkehr in die Ruhelage folgt einer semi-impliziten Euler-Integration eines gedämpften harmonischen Oszillators mit Federsteifigkeit und Dämpfungskoeffizient :
Mit einer kritischen Dämpfungsrate von schwingen ausgelenkte Gitterpunkte geschmeidig und ohne nervöses Nachzittern in ihre Gleichgewichtslage zurück.
Damit beim ersten Seitenaufruf weder ein unfertiger Webfont-Umbruch (Flash of Unstyled Text) noch ein verspätetes WebGL-Textur-Laden zu einem sichtbaren Umspringen der Kontur führt, synchronisiert src/pages/index.astro die Freigabe über ein deterministisches Zwei-Barrieren-Gate:
- Barriere 1 (
isFontsReady): Prüft synchron viadocument.fonts.check('800 60px Outfit')oder wartet auf das im W3C CSS Font Loading Standard [W3C14] definierte Promisedocument.fonts.ready(abgesichert durch einen Fallback-Timer). Erst wenn die finalen Outfit-Glyphenmetriken aktiv sind, vermisstsyncHeroResponsiveLayout()die Textzeilen und startet die gestaffelte Radial-Intro-Welle (dotgrid:intro). - Barriere 2 (
isPortraitReady): Lauscht auf das Custom Eventportrait:readydes WebGL-Porträts (mit Sicherheits-Timeout). Erst wennisFontsReady && isPortraitReadyerfüllt ist, schaltet#hero-portrait-cellvonvisibility: hidden; opacity: 0weich auf sichtbar.
Fazit: Fünf Architektur-Prinzipien für geometriebewusste Web-Interfaces
Die Verknüpfung von DOM-Text-Geometrie und analytischen 2D-Distanzfeldern zeigt, dass moderne Web-Frontends nicht an die starren Grenzen rechteckiger CSS-Block-Container gebunden sind:
- Sub-Element-Vermessung statt Block-Box-Verschnitt:
document.createTreeWalker(el, NodeFilter.SHOW_TEXT)in Kombination mitRange.getClientRects()erschließt die wahre Zeilenkontur von Fließtext und Überschriften. - 1D-Intervall-Merging als robuste Geometrie-Brücke: Das Zusammenführen einzelner Textknoten-Rechtecke über einen -Überlappungs-Schwellenwert reduziert hunderte DOM-Fragmente auf wenige kompakte Konturbänder
bands[]. - Deterministische 2D-Constraint-Optimierung statt starrer Breakpoints: Ein expliziter Kandidaten-Solver (Rechts-Mittig vs. Oben neben
<h1>) nutzt jeden freien Pixel über alle Viewport-Breiten hinweg kollisionsfrei aus. - Analytische 2D-SDFs mit
smin-Filleting: Die Kombination aussdRoundRectund Inigo Quilez’ polynomiellersmin-Funktion formt harte Treppenstufen in organisch fließende Freiräume um. - 10-Bucket Opacity Batching & Font-Ready-Gates: Die Quantisierung kontinuierlicher SDF-Opazitäten auf zehn Canvas-Draw-Calls pro Frame sowie das
document.fonts.ready-Synchronisations-Gate verbinden mathematische Präzision mit 60-FPS-Effizienz und CLS = 0.
Referenzen
- [Wha24]WHATWG (2024). DOM Standard — Interface TreeWalker and NodeIterator. WHATWG Living Standard. Online verfügbar unter https://dom.spec.whatwg.org/#interface-treewalker.
- [W3C16]W3C CSS Working Group (2016). CSSOM View Module — Extensions to the Range Interface (getClientRects). W3C Working Draft. Online verfügbar unter https://www.w3.org/TR/cssom-view-1/#extensions-to-the-range-interface.
- [W3C14]W3C CSS Working Group (2014). CSS Font Loading Module Level 3 — The ready Attribute. W3C Working Draft. Online verfügbar unter https://www.w3.org/TR/css-font-loading-3/#font-face-set-ready.
- [KP81]Donald E. Knuth and Michael F. Plass (1981). Breaking Paragraphs into Lines. Software: Practice and Experience, 11(11), S. 1119--1184. DOI: 10.1002/spe.4380111102.
- [Qui13]Inigo Quilez (2013). Smooth Minimum (smin) for Distance Fields. Inigo Quilez Articles. Online verfügbar unter https://iquilezles.org/articles/smin/.
- [Qui18]Inigo Quilez (2018). 2D Distance Functions (Signed Distance Fields). Inigo Quilez Articles. Online verfügbar unter https://iquilezles.org/articles/distfunctions2d/.
- [Gre07]Chris Green (2007). Improved Alpha-Tested Magnification for Vector Textures and Special Effects. In ACM SIGGRAPH 2007 Courses, S. 9--18. DOI: 10.1145/1281500.1281665.
- [BBS01]Greg J. Badros, Alan Borning and Peter J. Stuckey (2001). The Cassowary Linear Arithmetic Constraint Solving Algorithm. ACM Transactions on Computer-Human Interaction, 8(4), S. 267--306. DOI: 10.1145/504704.504705.