11import { createFileRoute } from '@tanstack/react-router'
2- import { useEffect , useState } from 'react'
2+ import { useEffect , useId , useState } from 'react'
33import StarIcon from '~icons/ic/outline-star'
44import NpmIcon from '~icons/file-icons/npm-old'
55import GithubIcon from '~icons/simple-icons/github'
66import * as Config from '~/lib/config'
7+ import * as Logos from '~/lib/logos'
78import * as Sources from '~/lib/sources'
89import type * as Github from '../../worker/sources/github'
910
@@ -317,16 +318,17 @@ const logoOverrides = Object.fromEntries(
317318)
318319
319320/**
320- * Render a brand mark via CSS `mask-image` + `background: currentColor`
321- * when the slug is in the manifest, else a `<span>` plain-text fallback.
322- * No client-side image fallback so SSR HTML is final and there's no
323- * broken-image flicker. Width is computed from the manifest aspect
324- * ratio so the layout box matches the visual size (no CLS).
321+ * Render a brand mark by inlining the mono SVG as live DOM and
322+ * recoloring via `currentColor`. Falls back to a `<span>` plain-text
323+ * mark when the slug isn't in the manifest. No client-side image
324+ * fallback so SSR HTML is final and there's no broken-image flicker.
325325 *
326- * Why mask instead of `<img>` + `filter`: iOS Safari has a long-standing
327- * bug where applying `filter` to an `<img>` containing an SVG forces
328- * rasterization at 1× device pixels, producing blur on retina/mobile.
329- * `mask-image` recolors via the GPU and stays sharp at any DPR.
326+ * Why inline SVG instead of `<img>` + `filter` or `<span>` + `mask-image`:
327+ * the mono SVGs are themselves `<mask>+<rect>` documents (see
328+ * [scripts/lib/svg.ts](scripts/lib/svg.ts) `wrapMask`). iOS Safari blurs
329+ * any path that rasterizes a masked SVG image — both `<img>` + `filter`
330+ * and CSS `mask-image` hit that bug at 3× DPR. Inlining the SVG renders
331+ * it as live vector DOM and stays sharp at any scale.
330332 */
331333function Mark ( {
332334 height,
@@ -339,37 +341,29 @@ function Mark({
339341 name : string
340342 slug : string
341343} ) {
344+ // `useId()` returns IDs like `:r0:` — colons are valid HTML but ugly
345+ // inside SVG `url(#…)` references; strip them for cleaner markup.
346+ const id = useId ( ) . replace ( / : / g, '' )
342347 const ratio = manifest ?. [ slug ]
343- if ( ratio === undefined )
348+ const svg = ratio !== undefined ? Logos . get ( slug , id ) : undefined
349+ if ( ratio === undefined || ! svg )
344350 return < span className = "text-lg font-medium tracking-[-0.01em] text-primary" > { name } </ span >
345351 // Per-slug visual scale lives in config (not the SVG bytes) so we
346- // don't have to reason about server-side viewBox clipping. We
347- // multiply both dimensions so the layout box reflects the visual
348- // size — important for the marquee, where transform-based scaling
349- // would overflow into the overflow-hidden clip.
350- // lowercase to match the case-insensitive override map
352+ // don't have to reason about server-side viewBox clipping. We size
353+ // the wrapper box in CSS — important for the marquee, where
354+ // transform-based scaling would overflow into the overflow-hidden
355+ // clip. lowercase to match the case-insensitive override map.
351356 const scale = logoOverrides [ slug . toLowerCase ( ) ] ?. scale ?? 1
352357 const renderedHeight = height * scale
353358 const renderedWidth = Math . round ( renderedHeight * ratio )
354- const url = `/logos/mono/${ slug } .svg`
355359 return (
356360 < span
357361 aria-hidden = "true"
358- className = "block bg-current transition-colors duration-100"
362+ className = "block transition-colors duration-100 [&>svg]:block [&>svg]:h-full [&>svg]:w-full "
359363 data-mark
364+ dangerouslySetInnerHTML = { { __html : svg } }
360365 role = "img"
361- style = { {
362- height : `${ renderedHeight } px` ,
363- width : `${ renderedWidth } px` ,
364- WebkitMaskImage : `url(${ url } )` ,
365- maskImage : `url(${ url } )` ,
366- WebkitMaskRepeat : 'no-repeat' ,
367- maskRepeat : 'no-repeat' ,
368- WebkitMaskPosition : 'left center' ,
369- maskPosition : 'left center' ,
370- WebkitMaskSize : 'contain' ,
371- maskSize : 'contain' ,
372- } }
366+ style = { { height : `${ renderedHeight } px` , width : `${ renderedWidth } px` } }
373367 title = { name }
374368 />
375369 )
0 commit comments