PageSourceSearch

https://slateworks.ai/js/divider_halo.js?v=979ea9dc

js slateworks.ai collected 2026-10-05 15:05:06 UTC 6,698 bytes, 153 lines download raw bytes

1/**
2 * divider_halo.js — DOM measurement + brand color lerp for the
3 * sky-shader wordmark halo. (File name is historical: it originally
4 * also tracked the .hero-divider bar, removed along with the Coming
5 * Soon badge.)
6 *
7 * Each frame, reads .hero-wordmark's bounding rect (in CSS pixels),
8 * converts to gl_FragCoord-space (bottom-left origin, devicePixelRatio
9 * scaled), and publishes the halo state to window._wordmarkHaloState
10 * for SKY_FS to consume.
11 *
12 * Why this lives in the sky shader instead of CSS:
13 *   The CSS radial-gradient that previously painted the glow had no
14 *   dither — at 8-bit RGB, a smooth gold→transparent radial falloff
15 *   produces visible concentric bands. SKY_FS already applies TPDF
16 *   dither at ±1/255 amplitude on every fragment, so anything composited
17 *   into the sky color before that pass gets free band suppression. The
18 *   CSS ::after rule is gone; this is the single source of truth.
19 *
20 * Color: the existing brand pair (gold #F2A900 / slate #1e3a5f), lerped
21 * by --day-mix via brand_mix.js. We read --day-mix directly from
22 * documentElement rather than re-deriving it from zenithLum, so the
23 * halo color tracks the wordmark color exactly. (If the wordmark
24 * crossfade rule ever changes, we don't have to re-implement it here.)
25 *
26 */
27(function () {
28  'use strict';
29
30  // sRGB-linear of the brand pair (computed once; matches the CSS hex
31  // values). The shader does its own dither in sRGB, so we hand it sRGB
32  // values too — interpolation between similar-saturation warm/cool
33  // pairs in sRGB is acceptable here since the halo strength is low and
34  // the difference vs oklab interp is sub-perceptual at peak intensity
35  // ~0.1.
36  var GOLD_R = 0xF2 / 255;
37  var GOLD_G = 0xA9 / 255;
38  var GOLD_B = 0x00 / 255;
39  var SLATE_R = 0x1e / 255;
40  var SLATE_G = 0x3a / 255;
41  var SLATE_B = 0x5f / 255;
42
43  // Peak intensity. The wordmark is a large area, so a low intensity
44  // keeps the halo ambient rather than washing the surrounding sky into
45  // a gold haze.
46  var WORDMARK_STRENGTH = 0.12;
47
48  // Wordmark footprint multipliers applied to the measured rect.
49  // The half-radius is text_dim × factor; the smoothstep(0, 0.75)
50  // falloff means the halo "bright zone" (where contribution > 50%
51  // of peak) extends to ~37.5% of half-radius. To get the bright zone
52  // to span the entire text width, half_radius needs to be ~text_width
53  // / 0.375 ≈ 2.7 × text_half_width — so factor ~2.7+ widthwise. We
54  // round up to 3.0 to give bleed past the leading-S and trailing-s,
55  // so the halo reads as ambient under the whole word rather than a
56  // spotlight on the middle.
57  // Height is more constrained: the bounding rect is roughly the font
58  // line-box (1.05em), so factor 2.0 gives the halo a soft vertical
59  // bleed equal to about one text height past the top and bottom.
60  var WORDMARK_W_FACTOR = 1.0;
61  var WORDMARK_H_FACTOR = 1.0;
62  // The wordmark bounding rect spans the full line-box including the
63  // tall script-font ascenders on S/l/t/k. The visual mass of the word
64  // sits in the lowercase x-height band below the rect's geometric
65  // center, so we shift the halo center downward by ~15% of rect
66  // height to land on the lowercase midpoint.
67  var WORDMARK_Y_OFFSET_FACTOR = 0.10;
68
69  var wordmark = null;
70  var canvas = null;
71  var root = document.documentElement;
72
73  function ensureWordmark() {
74    if (wordmark && wordmark.isConnected) return wordmark;
75    // First .hero-wordmark is the gold (night) layer in document flow —
76    // that's the one with the real bounding box. The day duplicate is
77    // absolute-positioned and shares the same screen footprint.
78    wordmark = document.querySelector('.hero-wordmark');
79    return wordmark;
80  }
81  function ensureCanvas() {
82    if (canvas && canvas.isConnected) return canvas;
83    canvas = document.querySelector('canvas');
84    return canvas;
85  }
86
87  function buildHaloState(rect, halfWCss, halfHCss, peakStrength, dpr,
88                          heroFade, r, g, b, yOffsetCss) {
89    if (!rect || rect.width < 1 || rect.height < 1) return null;
90    var cssCenterX = rect.left + rect.width * 0.5;
91    var cssCenterY = rect.top + rect.height * 0.5 + (yOffsetCss || 0);
92    return {
93      cx: cssCenterX * dpr,
94      cy: (window.innerHeight - cssCenterY) * dpr,
95      rx: halfWCss * dpr,
96      ry: halfHCss * dpr,
97      strength: peakStrength * heroFade,
98      r: r, g: g, b: b
99    };
100  }
101
102  function tick() {
103    // Derive the effective DPR from the actual canvas dimensions vs the
104    // CSS viewport. stars_gl.js caps DPR at 2 (MAX_DPR) for performance;
105    // using window.devicePixelRatio directly would put us out of sync
106    // with the renderer on 3x phones.
107    var cv = ensureCanvas();
108    var dpr = (cv && window.innerWidth > 0) ? (cv.width / window.innerWidth) : 1;
109
110    // Brand color lerp from --day-mix (set by brand_mix.js). Default 0
111    // (full gold) if the variable isn't set yet.
112    var dayMixStr = root.style.getPropertyValue('--day-mix');
113    var dayMix = dayMixStr ? parseFloat(dayMixStr) : 0;
114    if (!(dayMix >= 0 && dayMix <= 1)) dayMix = 0;
115    var r = GOLD_R + (SLATE_R - GOLD_R) * dayMix;
116    var g = GOLD_G + (SLATE_G - GOLD_G) * dayMix;
117    var b = GOLD_B + (SLATE_B - GOLD_B) * dayMix;
118
119    // Tie halo opacity to the hero-content fade. constellations.js drops
120    // .hero-content's opacity during the constellation showcase; the halos
121    // belong to the hero visually and should disappear with it. The global
122    // is the same value the JS animation writes to the DOM, so the halos
123    // and the rest of the hero crossfade in lockstep.
124    var heroFade = (typeof window._heroOverlayFade === 'number')
125      ? window._heroOverlayFade : 1;
126    // The halo is painted by the sky shader, so CSS hiding the hero
127    // (page-overlay navigation) can't hide it — it would glow through
128    // the translucent content cards. Kill it explicitly while a content
129    // page is open.
130    if (window._pageOverlayOpen) heroFade = 0;
131
132    // Wordmark — footprint scales with the wordmark's measured rect so
133    // the halo grows/shrinks with the responsive font-size clamp.
134    var wEl = ensureWordmark();
135    var wRect = wEl ? wEl.getBoundingClientRect() : null;
136    if (wRect && wRect.width >= 1 && wRect.height >= 1) {
137      window._wordmarkHaloState = buildHaloState(
138        wRect,
139        wRect.width * WORDMARK_W_FACTOR,
140        wRect.height * WORDMARK_H_FACTOR,
141        WORDMARK_STRENGTH,
142        dpr, heroFade, r, g, b,
143        wRect.height * WORDMARK_Y_OFFSET_FACTOR
144      );
145    } else {
146      window._wordmarkHaloState = null;
147    }
148
149    requestAnimationFrame(tick);
150  }
151
152  requestAnimationFrame(tick);
153})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.