Files
openchamber/packages/ui/src/components/icon/Icon.tsx
T
Bohdan Triapitsyn 14357257ae perf(ui): migrate icons to SVG sprite system
Replace @remixicon/react with a shared Icon component that renders
via <use href> references to a single hidden SVG sprite. This reduces
DOM node count by replacing inline SVGs with lightweight references.

- Create Icon component with sprite injection (packages/ui/src/components/icon/)
- Migrate all 164 files from @remixicon/react to Icon component
- Auto-generate sprite data from remixicon bundle (scripts/generate-icon-sprite.mjs)
- Add bun run icons:generate to package.json
- Move @remixicon/react to devDependencies
- Add icon usage instructions to theme-system skill
2026-05-13 13:26:35 +03:00

58 lines
1.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React from "react"
import { cn } from "@/lib/utils"
import { iconSpriteData } from "./sprite"
import type { IconName } from "./icons"
const SPRITE_ID = "openchamber-icon-sprite"
let spriteInjected = false
function ensureSpriteOnce() {
if (spriteInjected) return
if (typeof document === "undefined") return
const body = document.body
if (!body) return
const existing = document.getElementById(SPRITE_ID)
if (existing) {
spriteInjected = true
return
}
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg")
svg.id = SPRITE_ID
svg.setAttribute("aria-hidden", "true")
svg.style.display = "none"
svg.innerHTML = Object.entries(iconSpriteData)
.map(([name, content]) => `<symbol id="oc-${name}" viewBox="0 0 24 24">${content}</symbol>`)
.join("")
body.insertBefore(svg, body.firstChild)
spriteInjected = true
}
export interface IconProps extends React.ComponentPropsWithoutRef<"svg"> {
name: IconName
}
export const Icon = React.memo(({ name, className, ...rest }: IconProps) => {
// Inline sprite injection during render must run before <use> tries
// to resolve the #oc-* reference during the same commit.
if (typeof document !== "undefined") {
ensureSpriteOnce()
}
return (
<svg
className={cn("remixicon", className)}
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
{...rest}
>
<use href={`#oc-${name}`} />
</svg>
)
})
Icon.displayName = "Icon"