[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-3067":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":15,"subscribersCount":15,"size":15,"stars1d":16,"stars7d":17,"stars30d":18,"stars90d":15,"forks30d":15,"starsTrendScore":19,"compositeScore":20,"rankGlobal":10,"rankLanguage":10,"license":21,"archived":22,"fork":22,"defaultBranch":23,"hasWiki":24,"hasPages":24,"topics":25,"createdAt":10,"pushedAt":10,"updatedAt":26,"readmeContent":27,"aiSummary":28,"trendingCount":15,"starSnapshotCount":15,"syncStatus":29,"lastSyncTime":30,"discoverSource":31},3067,"metal-fx","Jakubantalik\u002Fmetal-fx","Jakubantalik","Animated WebGL liquid-metal effect for React buttons and UI components","https:\u002F\u002Fmetal.jakubantalik.com",null,"TypeScript",236,15,1,0,7,10,58,21,68.41,"MIT License",false,"main",true,[],"2026-06-12 04:00:16","# metal-fx\n\nAnimated WebGL \"liquid metal\" effect for React. Wrap a button, chip, or icon and it gets a real-time metal ring with optional proximity reflection on neighbouring elements.\n\n[Live demo](https:\u002F\u002Fmetal.jakubantalik.com) · [Repository](https:\u002F\u002Fgithub.com\u002FJakubantalik\u002Fmetal-fx) · [Report an issue](https:\u002F\u002Fgithub.com\u002FJakubantalik\u002Fmetal-fx\u002Fissues)\n\n## Install\n\n```bash\nnpm install metal-fx\n```\n\n## Quick start\n\n```tsx\nimport { MetalFx } from 'metal-fx';\n\nfunction App() {\n  return (\n    \u003CMetalFx variant=\"button\">\n      \u003Cbutton className=\"upgrade-pill\">Upgrade to Pro\u003C\u002Fbutton>\n    \u003C\u002FMetalFx>\n  );\n}\n```\n\nThe component wraps a single child host element, measures it, and paints an animated metal ring on top. The child stays fully interactive — overlays sit above it with `pointer-events: none`.\n\n## Variants\n\n```tsx\n\u003CMetalFx variant=\"button\">  {\u002F* Pill silhouette, 1 px ring, scale 1.6 *\u002F}\n  \u003Cbutton>Upgrade to Pro\u003C\u002Fbutton>\n\u003C\u002FMetalFx>\n\n\u003CMetalFx variant=\"circle\">  {\u002F* Compact circle, 2 px ring, scale 1.3 *\u002F}\n  \u003Cbutton>↑\u003C\u002Fbutton>\n\u003C\u002FMetalFx>\n```\n\n## Presets\n\nThree bundled palettes, each with a tuned dark and light mode block:\n\n```tsx\n\u003CMetalFx preset=\"chromatic\" \u002F>  {\u002F* Iridescent rainbow (default) *\u002F}\n\u003CMetalFx preset=\"silver\" \u002F>     {\u002F* Cool steel *\u002F}\n\u003CMetalFx preset=\"gold\" \u002F>       {\u002F* Warm gold *\u002F}\n```\n\n## Theme\n\n```tsx\n\u003CMetalFx theme=\"auto\" \u002F>    {\u002F* Follows prefers-color-scheme (default) *\u002F}\n\u003CMetalFx theme=\"dark\" \u002F>    {\u002F* Pin to dark backgrounds *\u002F}\n\u003CMetalFx theme=\"light\" \u002F>   {\u002F* Pin to light backgrounds *\u002F}\n```\n\n`auto` reads the OS \u002F browser theme on mount and subscribes to live changes via `matchMedia('(prefers-color-scheme: dark)')`, so the metal frame switches over instantly when the user toggles their system theme. SSR-safe — the initial render falls back to `dark` and rehydrates to the resolved theme on the client.\n\nIf your app has its own theme toggle that doesn't follow the OS, drive `theme` from your app state instead:\n\n```tsx\nconst appTheme = useAppTheme(); \u002F\u002F 'dark' | 'light'\n\u003CMetalFx theme={appTheme}>...\u003C\u002FMetalFx>\n```\n\n## Strength\n\n```tsx\n\u003CMetalFx strength={0.7}>  {\u002F* 70% effect intensity *\u002F}\n  \u003Cbutton>Upgrade to Pro\u003C\u002Fbutton>\n\u003C\u002FMetalFx>\n```\n\n`strength` runs from `0` (invisible) to `1` (full, default). It scales the canvas and glow opacity without changing the underlying shader animation.\n\n## Paused\n\n```tsx\n\u003CMetalFx paused>\n  \u003Cbutton>Upgrade to Pro\u003C\u002Fbutton>\n\u003C\u002FMetalFx>\n```\n\nFreezes the shader on its current frame. The metal silhouette stays visible.\n\n## Proximity reflection (dark mode only)\n\nPass refs to neighbouring elements and they receive a soft, mirrored reflection of the metal ring:\n\n```tsx\nconst sendRef = useRef\u003CHTMLButtonElement>(null);\nconst chipRef = useRef\u003CHTMLButtonElement>(null);\n\n\u003C>\n  \u003Cbutton ref={chipRef}>Tools\u003C\u002Fbutton>\n  \u003CMetalFx variant=\"circle\" reflectionTargets={[chipRef]}>\n    \u003Cbutton ref={sendRef} aria-label=\"Send\">↑\u003C\u002Fbutton>\n  \u003C\u002FMetalFx>\n\u003C\u002F>\n```\n\nReflections are skipped automatically when the resolved theme is `light` — no DOM scanning, no per-frame work in light mode.\n\n## Performance\n\n- One shared WebGL context is reused across every mounted `\u003CMetalFx>` on the page. The shader is compiled once.\n- A single `requestAnimationFrame` loop drives every instance. Per-frame work for one mount: a `gl.drawArrays` plus N×`drawImage` copies (one per visible instance).\n- `IntersectionObserver` pauses per-instance copies when the host scrolls offscreen. When every instance is offscreen the GL render is skipped too.\n- `ResizeObserver` callbacks are debounced through RAF.\n- The GL context, program, and buffer are released when the last `\u003CMetalFx>` unmounts.\n\n## Server-side rendering\n\nThe component renders a transparent placeholder during SSR and only mounts the WebGL pipeline after hydration on the client. No flash of broken effect, no SSR errors.\n\n## Sizing\n\n`MetalFx` does not force any dimensions onto the wrapped child — the wrapper sizes itself to whatever the child renders. Style your child the way you normally would (intrinsic content, CSS class, or inline style):\n\n```tsx\n\u002F\u002F Pattern 1 (recommended): size the child.\n\u003CMetalFx variant=\"circle\">\n  \u003Cbutton style={{ width: 36, height: 36 }} aria-label=\"Send\">↑\u003C\u002Fbutton>\n\u003C\u002FMetalFx>\n\n\u003CMetalFx>\n  \u003Cbutton className=\"rounded-full px-6 h-10\">Upgrade to Pro\u003C\u002Fbutton>\n\u003C\u002FMetalFx>\n```\n\nIf you want a metal frame larger than the child (e.g. padding around an icon), size the wrapper instead and explicitly stretch the child to fill:\n\n```tsx\n\u002F\u002F Pattern 2: size the wrapper, child fills.\n\u003CMetalFx style={{ width: 36, height: 36 }} variant=\"circle\">\n  \u003Cbutton style={{ width: '100%', height: '100%' }} aria-label=\"Send\">↑\u003C\u002Fbutton>\n\u003C\u002FMetalFx>\n```\n\nBoth patterns work; pick whichever fits your layout. The wrapper is `display: inline-flex` so it lays out inline like a button.\n\n## Custom border radius\n\nBy default `MetalFx` reads the computed `border-radius` of the wrapped child each resize. Pass an explicit override when needed:\n\n```tsx\n\u003CMetalFx borderRadius={20}>\n  \u003Cbutton>Upgrade to Pro\u003C\u002Fbutton>\n\u003C\u002FMetalFx>\n```\n\n## License\n\nMIT &copy; [Jakub Antalik](https:\u002F\u002Fgithub.com\u002FJakubantalik). See [LICENSE](.\u002FLICENSE).\n","metal-fx 是一个为 React 按钮和UI组件提供动画 WebGL 流动金属效果的库。它通过包裹按钮、标签或图标等元素，在其周围生成实时的金属环，支持邻近元素反射等高级视觉效果。项目采用 TypeScript 编写，具备多种预设样式（如彩虹色、银色、金色）和主题模式（自动跟随系统主题或固定为深色\u002F浅色），并且允许调整特效强度和暂停动画。适合需要增强用户体验、追求独特视觉风格的应用场景，尤其是在希望给用户界面添加未来感或科技感的设计中使用。",2,"2026-06-11 02:52:19","CREATED_QUERY"]