Started May 9 · 09:00·Opus 4.7·Tideline Brand System
LN
Leonel Ngoya09:30

I need to ship the 320 Tideline icons in three formats: a single SVG sprite, individual React components (Tree-shakable), and a fallback iconfont. How would you structure the build?

AxiomOpus 4.709:35

Tool calls

websearch Confirmed svgr 8.x supports the React 19 ref forwarding shape.

Pipeline shape — one source of truth, three outputs. ``` icons/source/*.svg # the 320 hand-authored files build/sprite.svg # <symbol id='...'/> for every icon, gzipped → ~32KB build/react/<Name>.tsx # one component per icon, named export from index.ts build/iconfont/tideline.woff2 # fallback font for legacy email clients ``` For the React components: `svgr` with a Babel pre-pass that swaps every `<svg>` for a tiny wrapper that forwards `ref` and `className`. Keeps the components ~80 bytes each after minification. For the sprite: a single `<symbol>` per icon, lazy-mount once on the first `<Icon name='…'/>` use. The browser caches the sprite forever. For the iconfont: `svgtofont` plus a manual review pass to fix glyph kerning. This is the painful one. Want me to walk through the glyph-mapping table?
LN
Leonel Ngoya10:00

Skip the iconfont mapping — we'll outsource that. Walk me through the sprite-mount strategy instead.

AxiomOpus 4.710:04
Sprite-mount strategy: 1. **First-render check.** The first `<Icon/>` call invokes `useSyncExternalStore` against a single `mounted` ref. If `false`, kick off a `fetch('/tideline-sprite.svg')` and stash the SVG string in `document.body`. 2. **Once-only side effect.** Use a module-level boolean so React Strict Mode's double-invoke doesn't fetch twice. 3. **Icon component.** Renders `<svg><use href={"#tideline-" + name} /></svg>`. The `<symbol>` already carries the viewBox. 4. **SSR.** Skip the mount on the server; rely on the browser fetch. Don't try to inline the sprite into the HTML — the parser stalls on 32KB of inline SVG. 5. **Cache busting.** Hash the sprite URL (`tideline-sprite.a3f9.svg`) and serve with `Cache-Control: public, max-age=31536000, immutable`.

Demo template — no model is invoked. Replies are randomly picked from a small list of fake responses in mock-data/axiom.ts.