@tsogtoodev/shingen-metal 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jakub Antalik
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,164 @@
1
+ # shingen-metal
2
+
3
+ Animated WebGL "liquid metal" effect for Vue 3 — a port of [metal-fx](https://github.com/Jakubantalik/metal-fx) (React) by Jakub Antalik. Wrap a button, chip, or icon and it gets a real-time metal ring with optional proximity reflection on neighbouring elements.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install shingen-metal
9
+ ```
10
+
11
+ ## Quick start
12
+
13
+ ```vue
14
+ <script setup>
15
+ import { MetalFx } from 'shingen-metal';
16
+ </script>
17
+
18
+ <template>
19
+ <MetalFx variant="button">
20
+ <button class="upgrade-pill">Upgrade to Pro</button>
21
+ </MetalFx>
22
+ </template>
23
+ ```
24
+
25
+ The component wraps a single slotted 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`.
26
+
27
+ ## Variants
28
+
29
+ ```vue
30
+ <MetalFx variant="button"> <!-- Pill silhouette, 1 px ring, scale 1.6 -->
31
+ <button>Upgrade to Pro</button>
32
+ </MetalFx>
33
+
34
+ <MetalFx variant="circle"> <!-- Compact circle, 2 px ring, scale 1.3 -->
35
+ <button>↑</button>
36
+ </MetalFx>
37
+ ```
38
+
39
+ ## Presets
40
+
41
+ Three bundled palettes, each with a tuned dark and light mode block:
42
+
43
+ ```vue
44
+ <MetalFx preset="chromatic" /> <!-- Iridescent rainbow (default) -->
45
+ <MetalFx preset="silver" /> <!-- Cool steel -->
46
+ <MetalFx preset="gold" /> <!-- Warm gold -->
47
+ ```
48
+
49
+ ## Theme
50
+
51
+ ```vue
52
+ <MetalFx theme="auto" /> <!-- Follows prefers-color-scheme (default) -->
53
+ <MetalFx theme="dark" /> <!-- Pin to dark backgrounds -->
54
+ <MetalFx theme="light" /> <!-- Pin to light backgrounds -->
55
+ ```
56
+
57
+ `auto` reads the OS / 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.
58
+
59
+ If your app has its own theme toggle that doesn't follow the OS, drive `theme` from your app state instead:
60
+
61
+ ```vue
62
+ <MetalFx :theme="appTheme">...</MetalFx>
63
+ ```
64
+
65
+ ## Strength
66
+
67
+ ```vue
68
+ <MetalFx :strength="0.7"> <!-- 70% effect intensity -->
69
+ <button>Upgrade to Pro</button>
70
+ </MetalFx>
71
+ ```
72
+
73
+ `strength` runs from `0` (invisible) to `1` (full, default). It scales the canvas and glow opacity without changing the underlying shader animation.
74
+
75
+ ## Paused
76
+
77
+ ```vue
78
+ <MetalFx paused>
79
+ <button>Upgrade to Pro</button>
80
+ </MetalFx>
81
+ ```
82
+
83
+ Freezes the shader on its current frame. The metal silhouette stays visible.
84
+
85
+ ## Proximity reflection (dark mode only)
86
+
87
+ Pass template refs to neighbouring elements and they receive a soft, mirrored reflection of the metal ring:
88
+
89
+ ```vue
90
+ <script setup>
91
+ import { ref } from 'vue';
92
+ import { MetalFx } from 'shingen-metal';
93
+
94
+ const chipRef = ref(null);
95
+ </script>
96
+
97
+ <template>
98
+ <button ref="chipRef">Tools</button>
99
+ <MetalFx variant="circle" :reflection-targets="[chipRef]">
100
+ <button aria-label="Send">↑</button>
101
+ </MetalFx>
102
+ </template>
103
+ ```
104
+
105
+ Reflections are skipped automatically when the resolved theme is `light` — no DOM scanning, no per-frame work in light mode.
106
+
107
+ ## Performance
108
+
109
+ - One shared WebGL context is reused across every mounted `<MetalFx>` on the page. The shader is compiled once.
110
+ - A single `requestAnimationFrame` loop drives every instance. Per-frame work for one mount: a `gl.drawArrays` plus N×`drawImage` copies (one per visible instance).
111
+ - `IntersectionObserver` pauses per-instance copies when the host scrolls offscreen. When every instance is offscreen the GL render is skipped too.
112
+ - `ResizeObserver` callbacks are debounced through RAF.
113
+ - The GL context, program, and buffer are released when the last `<MetalFx>` unmounts.
114
+
115
+ ## Server-side rendering
116
+
117
+ The 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.
118
+
119
+ ## Sizing
120
+
121
+ `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):
122
+
123
+ ```vue
124
+ <!-- Pattern 1 (recommended): size the child. -->
125
+ <MetalFx variant="circle">
126
+ <button style="width: 36px; height: 36px" aria-label="Send">↑</button>
127
+ </MetalFx>
128
+
129
+ <MetalFx>
130
+ <button class="rounded-full px-6 h-10">Upgrade to Pro</button>
131
+ </MetalFx>
132
+ ```
133
+
134
+ If 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:
135
+
136
+ ```vue
137
+ <!-- Pattern 2: size the wrapper, child fills. -->
138
+ <MetalFx style="width: 36px; height: 36px" variant="circle">
139
+ <button style="width: 100%; height: 100%" aria-label="Send">↑</button>
140
+ </MetalFx>
141
+ ```
142
+
143
+ Both patterns work; pick whichever fits your layout. The wrapper is `display: inline-flex` so it lays out inline like a button.
144
+
145
+ ## Custom border radius
146
+
147
+ By default `MetalFx` reads the computed `border-radius` of the wrapped child each resize. Pass an explicit override when needed:
148
+
149
+ ```vue
150
+ <MetalFx :border-radius="20">
151
+ <button>Upgrade to Pro</button>
152
+ </MetalFx>
153
+ ```
154
+
155
+ ## Demo
156
+
157
+ ```bash
158
+ npm install
159
+ npm run dev
160
+ ```
161
+
162
+ ## License
163
+
164
+ MIT — port of [metal-fx](https://github.com/Jakubantalik/metal-fx) © [Jakub Antalik](https://github.com/Jakubantalik). See [LICENSE](./LICENSE).