@shulya/liquid-glass 0.0.0-stage → 1.0.1

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 Shulya LLC
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 CHANGED
@@ -1,3 +1,339 @@
1
- # Temporary Holding Version
1
+ # `@shulya/liquid-glass`
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![npm version](https://img.shields.io/badge/npm-v1.0.0-007aff?style=flat-square)](https://www.npmjs.com/package/@shulya/liquid-glass)
4
+ [![Live Demo](https://img.shields.io/badge/Live%20Demo-liquid.shulya.tech-ff2d55?style=flat-square)](https://liquid.shulya.tech)
5
+ [![GitHub Repo](https://img.shields.io/badge/GitHub-shulya--tech%2Fliquid--glass-181717?style=flat-square&logo=github)](https://github.com/shulya-tech/liquid-glass)
6
+ [![Zero WebGL](<https://img.shields.io/badge/WebGL-None%20(100%25%20DOM%20%2F%202D%20LUT)-30d158?style=flat-square>)](#why-shulyaliquid-glass)
7
+ [![Frameworks](https://img.shields.io/badge/Frameworks-React%20%7C%20Vue%20%7C%20Svelte%20%7C%20Angular%20%7C%20Solid-af52de?style=flat-square)](#framework-integrations)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-black?style=flat-square)](https://github.com/shulya-tech/liquid-glass/blob/main/LICENSE)
9
+
10
+ **`@shulya/liquid-glass`** is an ultra-lightweight, zero-WebGL optical glass rendering engine for modern web applications. Turn any HTML container, card, navigation bar, modal, or interactive widget into a realistic refractive **Liquid Glass** block with real-time control over **`rim`**, **`ior`**, **`height`**, **`frosted`**, and **`fusion`** parameters.
11
+
12
+ - **Live Interactive Demo:** [https://liquid.shulya.tech](https://liquid.shulya.tech)
13
+ - **NPM Package:** [https://www.npmjs.com/package/@shulya/liquid-glass](https://www.npmjs.com/package/@shulya/liquid-glass)
14
+ - **GitHub Repository:** [https://github.com/shulya-tech/liquid-glass](https://github.com/shulya-tech/liquid-glass)
15
+
16
+ ![Liquid Glass Interactive Playground](./screen.png)
17
+
18
+ ---
19
+
20
+ ## Why `@shulya/liquid-glass`?
21
+
22
+ - **Zero WebGL / Zero GPU Shader Overhead:** Does not spawn WebGL contexts or heavy fragment shaders. Runs on 100% standard browser primitives backed by pre-calibrated empirical lookup tables (`Float32Array` LUTs).
23
+ - **Mobile-First & Cool-Running:** Designed for 120 FPS responsiveness on smartphones and tablets with **0% CPU/GPU load when idle**—no battery drain or thermal throttling.
24
+ - **Toggleable Liquid Coalescence (`fusion`):** Enabled by default (`fusion: true`) for organic surface merging when elements approach one another, and can be disabled (`fusion: false`) on standalone blocks to skip proximity tracking for maximum performance.
25
+ - **Universal Framework Support:** First-class bindings included out of the box for **React**, **Vue 3**, **Svelte**, **Angular**, **SolidJS**, and **Vanilla JS**.
26
+ - **Pre-Calibrated Optical Matrix:** Uses a 256-point empirical optical calibration table (`tables/optical_calibration_matrix.xlsx`) for instant $O(1)$ linear lookup of edge deflection, specular rim luminance, and chromatic shift.
27
+
28
+ ---
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ npm install @shulya/liquid-glass
34
+ # or
35
+ pnpm add @shulya/liquid-glass
36
+ # or
37
+ yarn add @shulya/liquid-glass
38
+ ```
39
+
40
+ ---
41
+
42
+ ## Interactive React Playground (`example/`)
43
+
44
+ > **Try it online right now:** [**https://liquid.shulya.tech**](https://liquid.shulya.tech)
45
+ > **Source code on GitHub:** [**github.com/shulya-tech/liquid-glass/tree/main/example**](https://github.com/shulya-tech/liquid-glass/tree/main/example)
46
+
47
+ ![Interactive React Playground Preview](./screen.png)
48
+
49
+ The repository includes a complete **React + Vite** interactive playground in [`example/`](./example/) ([`example/src/App.jsx`](./example/src/App.jsx)) where you can drag three `<LiquidGlass>` components (a `136×74` capsule slider, a `196×116` rounded card, and a `112×112` circular lens) across a high-contrast optical test stage with a green pill track, diagonal rainbow ribbon, sunset sphere, black/white stripe grating, and centered `"Shulya"` typography.
50
+
51
+ All rendering in `example/` is powered directly by the `@shulya/liquid-glass/react` adapter (`<LiquidGlass>`, `scheduleStageRedraw`, `OPTICAL_DEFAULTS`) and the precomputed empirical lookup tables `_T0`–`_T4`.
52
+
53
+ ### Running the React Playground Locally
54
+
55
+ ```bash
56
+ # 1. Build the library bundles from the root directory
57
+ npm run build
58
+
59
+ # 2. Install example dependencies and start the Vite dev server
60
+ cd example
61
+ npm install
62
+ npm run dev
63
+ ```
64
+
65
+ Or run `npm run example` directly from the repository root, then open **`http://localhost:5173`** in your browser to test:
66
+
67
+ - Dragging any of the 3 `<LiquidGlass>` React components over the typography, stripes, and color gradients.
68
+ - Bringing two or all three shapes close together to observe real-time liquid bridge coalescence (`fusion`).
69
+ - Adjusting **`Bevel Width (Rim)`** (`6–32 px`), **`Refraction (IOR)`** (`1.10–1.70`), **`Thickness (Height)`** (`6–34 px`), **`Frosted Blur`** (`0–30 px`), and toggling **`Liquid Fusion`** on/off in real time via the bottom control shelf.
70
+
71
+ ---
72
+
73
+ ## Optical Parameters & Strict Bounds
74
+
75
+ Every component, hook, directive, and controller accepts the following properties. Out-of-range numeric inputs are automatically clamped to their physical `[min, max]` boundaries via `normalizeOptions()`:
76
+
77
+ | Parameter | Type | Default | Min | Max | Description |
78
+ | :----------------- | :--------------: | :--------: | :----: | :----: | :------------------------------------------------------------------------------------------------------------------------------------------------------------- |
79
+ | **`rim`** | `number` | **`16`** | `4` | `48` | Width of the curved perimeter bevel and specular edge highlight (in `px`). |
80
+ | **`ior`** | `number` | **`1.46`** | `1.05` | `2.20` | Optical Index of Refraction controlling background bending intensity. |
81
+ | **`height`** | `number` | **`18`** | `2` | `50` | Apparent elevation and optical thickness of the glass slab (in `px`). |
82
+ | **`frosted`** | `number` | **`0`** | `0` | `32` | Gaussian surface diffusion radius in `px` (`0` = crystal clear, `>0` = frosted satin glass). |
83
+ | **`fusion`** | `boolean` | **`true`** | — | — | Enables liquid surface coalescence with neighboring glass blocks. Set to **`false`** for isolated static blocks to bypass proximity checks and maximize speed. |
84
+ | **`borderRadius`** | `number \| null` | `null` | `0` | `999` | Optional override for corner radius in `px` (automatically inherits CSS `border-radius` when omitted). |
85
+
86
+ You can also import the frozen constants `OPTICAL_DEFAULTS` and `OPTICAL_LIMITS` from any entry point to bind UI sliders directly to the valid bounds.
87
+
88
+ ---
89
+
90
+ ## Framework Integrations
91
+
92
+ ### 1. React (`@shulya/liquid-glass/react`)
93
+
94
+ Use either the `<LiquidGlass>` component or the `useLiquidGlass` hook on any existing element:
95
+
96
+ ```tsx
97
+ import { LiquidGlass, useLiquidGlass } from "@shulya/liquid-glass/react";
98
+
99
+ export function GlassCard() {
100
+ return (
101
+ <LiquidGlass
102
+ rim={16}
103
+ ior={1.46}
104
+ height={18}
105
+ frosted={0}
106
+ fusion={true}
107
+ className="p-6 rounded-3xl"
108
+ >
109
+ <h2>Liquid Glass Card</h2>
110
+ <p>Refractive optical container with zero WebGL overhead.</p>
111
+ </LiquidGlass>
112
+ );
113
+ }
114
+
115
+ // Or via Hook (e.g. with fusion disabled for a static navbar):
116
+ export function StaticNavbar() {
117
+ const glassRef = useLiquidGlass<HTMLElement>({
118
+ rim: 16,
119
+ ior: 1.46,
120
+ height: 18,
121
+ frosted: 8,
122
+ fusion: false,
123
+ });
124
+
125
+ return (
126
+ <nav ref={glassRef} className="top-nav">
127
+ Navigation
128
+ </nav>
129
+ );
130
+ }
131
+ ```
132
+
133
+ ---
134
+
135
+ ### 2. Vue 3 (`@shulya/liquid-glass/vue`)
136
+
137
+ Use the `<LiquidGlass>` component or the `v-liquid-glass` directive:
138
+
139
+ ```vue
140
+ <script setup>
141
+ import { ref } from "vue";
142
+ import { LiquidGlass, vLiquidGlass } from "@shulya/liquid-glass/vue";
143
+
144
+ const ior = ref(1.46);
145
+ const frosted = ref(0);
146
+ </script>
147
+
148
+ <template>
149
+ <!-- Interactive coalescing glass block (fusion enabled by default) -->
150
+ <LiquidGlass
151
+ :rim="16"
152
+ :ior="ior"
153
+ :height="18"
154
+ :frosted="frosted"
155
+ :fusion="true"
156
+ class="glass-box"
157
+ >
158
+ <h3>Vue Liquid Glass</h3>
159
+ </LiquidGlass>
160
+
161
+ <!-- Standalone static glass block with fusion disabled for extra optimization -->
162
+ <section
163
+ v-liquid-glass="{
164
+ rim: 16,
165
+ ior: 1.46,
166
+ height: 18,
167
+ frosted: 0,
168
+ fusion: false,
169
+ }"
170
+ >
171
+ Optimized isolated glass block
172
+ </section>
173
+ </template>
174
+ ```
175
+
176
+ ---
177
+
178
+ ### 3. Svelte (`@shulya/liquid-glass/svelte`)
179
+
180
+ Use the `use:liquidGlass` action on any HTML element. Reactive updates are handled automatically:
181
+
182
+ ```svelte
183
+ <script>
184
+ import { liquidGlass } from '@shulya/liquid-glass/svelte';
185
+
186
+ let rim = 16;
187
+ let ior = 1.46;
188
+ let height = 18;
189
+ let frosted = 0;
190
+ let fusion = true;
191
+ </script>
192
+
193
+ <div
194
+ class="glass-panel"
195
+ use:liquidGlass={{ rim, ior, height, frosted, fusion }}
196
+ >
197
+ <h3>Svelte Liquid Glass</h3>
198
+ </div>
199
+ ```
200
+
201
+ ---
202
+
203
+ ### 4. Angular (`@shulya/liquid-glass/angular`)
204
+
205
+ Attach `attachAngularLiquidGlass` inside your component or wire `LiquidGlassDirective`:
206
+
207
+ ```ts
208
+ import {
209
+ Component,
210
+ ElementRef,
211
+ ViewChild,
212
+ AfterViewInit,
213
+ OnDestroy,
214
+ } from "@angular/core";
215
+ import {
216
+ attachAngularLiquidGlass,
217
+ LiquidGlassController,
218
+ } from "@shulya/liquid-glass/angular";
219
+
220
+ @Component({
221
+ selector: "app-glass-card",
222
+ standalone: true,
223
+ template: `
224
+ <div #glassBox class="glass-card">
225
+ <h3>Angular Liquid Glass</h3>
226
+ </div>
227
+ `,
228
+ })
229
+ export class GlassCardComponent implements AfterViewInit, OnDestroy {
230
+ @ViewChild("glassBox", { static: true }) glassBox!: ElementRef<HTMLElement>;
231
+ private controller?: LiquidGlassController;
232
+
233
+ ngAfterViewInit() {
234
+ this.controller = attachAngularLiquidGlass(this.glassBox, {
235
+ rim: 16,
236
+ ior: 1.46,
237
+ height: 18,
238
+ frosted: 0,
239
+ fusion: true,
240
+ });
241
+ }
242
+
243
+ disableFusionForStaticMode() {
244
+ this.controller?.update({ fusion: false });
245
+ }
246
+
247
+ ngOnDestroy() {
248
+ this.controller?.destroy();
249
+ }
250
+ }
251
+ ```
252
+
253
+ ---
254
+
255
+ ### 5. SolidJS (`@shulya/liquid-glass/solid`)
256
+
257
+ Use the reactive `use:liquidGlass` directive with signals:
258
+
259
+ ```tsx
260
+ import { createSignal } from "solid-js";
261
+ import { liquidGlass } from "@shulya/liquid-glass/solid";
262
+
263
+ export function SolidGlassDemo() {
264
+ const [rim, setRim] = createSignal(16);
265
+ const [ior, setIor] = createSignal(1.46);
266
+ const [height, setHeight] = createSignal(18);
267
+ const [frosted, setFrosted] = createSignal(0);
268
+ const [fusion, setFusion] = createSignal(true);
269
+
270
+ return (
271
+ <div
272
+ class="glass-card"
273
+ use:liquidGlass={() => ({
274
+ rim: rim(),
275
+ ior: ior(),
276
+ height: height(),
277
+ frosted: frosted(),
278
+ fusion: fusion(),
279
+ })}
280
+ >
281
+ <h3>SolidJS Liquid Glass</h3>
282
+ </div>
283
+ );
284
+ }
285
+ ```
286
+
287
+ ---
288
+
289
+ ### 6. Vanilla JS / TypeScript (`@shulya/liquid-glass`)
290
+
291
+ ```js
292
+ import {
293
+ createLiquidGlass,
294
+ OPTICAL_DEFAULTS,
295
+ OPTICAL_LIMITS,
296
+ } from "@shulya/liquid-glass";
297
+
298
+ const glass = createLiquidGlass("#my-card", {
299
+ rim: 16, // Clamped between [4, 48]
300
+ ior: 1.46, // Clamped between [1.05, 2.20]
301
+ height: 18, // Clamped between [2, 50]
302
+ frosted: 0, // Clamped between [0, 32]
303
+ fusion: true, // Default: true (set false to isolate element)
304
+ });
305
+
306
+ // Dynamically update parameters at any time:
307
+ glass.update({
308
+ rim: 24,
309
+ ior: 1.62,
310
+ height: 28,
311
+ frosted: 8,
312
+ fusion: false,
313
+ });
314
+ ```
315
+
316
+ ---
317
+
318
+ ## Pre-Computed Calibration Dataset
319
+
320
+ To guarantee zero runtime trigonometric or shader overhead across mobile devices, `@shulya/liquid-glass` ships with pre-sampled empirical lookup arrays located in:
321
+
322
+ - `tables/optical_calibration_matrix.xlsx` (Excel workbook for inspection and studio tuning)
323
+ - `tables/optical_calibration_matrix.csv` (CSV mirror)
324
+ - `dist/tables.mjs` (`Float32Array` runtime buffers `_T0` – `_T4`)
325
+
326
+ ---
327
+
328
+ ## Links
329
+
330
+ - **Live Demo:** [https://liquid.shulya.tech](https://liquid.shulya.tech)
331
+ - **NPM Package:** [https://www.npmjs.com/package/@shulya/liquid-glass](https://www.npmjs.com/package/@shulya/liquid-glass)
332
+ - **GitHub Repository:** [https://github.com/shulya-tech/liquid-glass](https://github.com/shulya-tech/liquid-glass)
333
+ - **Issue Tracker:** [https://github.com/shulya-tech/liquid-glass/issues](https://github.com/shulya-tech/liquid-glass/issues)
334
+
335
+ ---
336
+
337
+ ## License
338
+
339
+ MIT © [Shulya LLC](https://github.com/shulya-tech)
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Angular Adapter for Liquid Glass UI
3
+ */
4
+ const { LiquidGlassController, OPTICAL_DEFAULTS, OPTICAL_LIMITS } = require('./core.cjs');
5
+
6
+ /**
7
+ * Framework-agnostic Angular Directive Controller Class
8
+ * Can be used directly with Angular's @Directive({ selector: '[liquidGlass]', standalone: true })
9
+ * or instantiated via ElementRef in any Angular component.
10
+ */
11
+ const LiquidGlassDirective = exports.LiquidGlassDirective = class LiquidGlassDirective {
12
+ /**
13
+ * @param {{ nativeElement: HTMLElement } | HTMLElement} elementRef
14
+ */
15
+ constructor(elementRef) {
16
+ this.el = elementRef && elementRef.nativeElement ? elementRef.nativeElement : elementRef;
17
+ this.rim = OPTICAL_DEFAULTS.rim;
18
+ this.ior = OPTICAL_DEFAULTS.ior;
19
+ this.height = OPTICAL_DEFAULTS.height;
20
+ this.frosted = OPTICAL_DEFAULTS.frosted;
21
+ this.fusion = OPTICAL_DEFAULTS.fusion;
22
+ this.borderRadius = null;
23
+ this.liquidGlass = null;
24
+ this._controller = null;
25
+ }
26
+
27
+ ngOnInit() {
28
+ const opts =
29
+ typeof this.liquidGlass === 'object' && this.liquidGlass !== null
30
+ ? this.liquidGlass
31
+ : {
32
+ rim: this.rim,
33
+ ior: this.ior,
34
+ height: this.height,
35
+ frosted: this.frosted,
36
+ fusion: this.fusion,
37
+ borderRadius: this.borderRadius
38
+ };
39
+ this._controller = new LiquidGlassController(this.el, opts);
40
+ }
41
+
42
+ ngOnChanges() {
43
+ if (this._controller) {
44
+ const opts =
45
+ typeof this.liquidGlass === 'object' && this.liquidGlass !== null
46
+ ? this.liquidGlass
47
+ : {
48
+ rim: this.rim,
49
+ ior: this.ior,
50
+ height: this.height,
51
+ frosted: this.frosted,
52
+ fusion: this.fusion,
53
+ borderRadius: this.borderRadius
54
+ };
55
+ this._controller.update(opts);
56
+ }
57
+ }
58
+
59
+ ngOnDestroy() {
60
+ if (this._controller) {
61
+ this._controller.destroy();
62
+ this._controller = null;
63
+ }
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Helper to attach LiquidGlass directly inside Angular ngAfterViewInit
69
+ */
70
+ exports.attachAngularLiquidGlass = function attachAngularLiquidGlass(elementRef, options = {}) {
71
+ const el = elementRef && elementRef.nativeElement ? elementRef.nativeElement : elementRef;
72
+ return new LiquidGlassController(el, options);
73
+ }
74
+
75
+ exports.LiquidGlassController = LiquidGlassController;
76
+ exports.OPTICAL_DEFAULTS = OPTICAL_DEFAULTS;
77
+ exports.OPTICAL_LIMITS = OPTICAL_LIMITS;
78
+ exports.default = LiquidGlassDirective;
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Angular Adapter for Liquid Glass UI
3
+ */
4
+ import { LiquidGlassController, OPTICAL_DEFAULTS, OPTICAL_LIMITS } from './core.mjs';
5
+
6
+ /**
7
+ * Framework-agnostic Angular Directive Controller Class
8
+ * Can be used directly with Angular's @Directive({ selector: '[liquidGlass]', standalone: true })
9
+ * or instantiated via ElementRef in any Angular component.
10
+ */
11
+ export class LiquidGlassDirective {
12
+ /**
13
+ * @param {{ nativeElement: HTMLElement } | HTMLElement} elementRef
14
+ */
15
+ constructor(elementRef) {
16
+ this.el = elementRef && elementRef.nativeElement ? elementRef.nativeElement : elementRef;
17
+ this.rim = OPTICAL_DEFAULTS.rim;
18
+ this.ior = OPTICAL_DEFAULTS.ior;
19
+ this.height = OPTICAL_DEFAULTS.height;
20
+ this.frosted = OPTICAL_DEFAULTS.frosted;
21
+ this.fusion = OPTICAL_DEFAULTS.fusion;
22
+ this.borderRadius = null;
23
+ this.liquidGlass = null;
24
+ this._controller = null;
25
+ }
26
+
27
+ ngOnInit() {
28
+ const opts =
29
+ typeof this.liquidGlass === 'object' && this.liquidGlass !== null
30
+ ? this.liquidGlass
31
+ : {
32
+ rim: this.rim,
33
+ ior: this.ior,
34
+ height: this.height,
35
+ frosted: this.frosted,
36
+ fusion: this.fusion,
37
+ borderRadius: this.borderRadius
38
+ };
39
+ this._controller = new LiquidGlassController(this.el, opts);
40
+ }
41
+
42
+ ngOnChanges() {
43
+ if (this._controller) {
44
+ const opts =
45
+ typeof this.liquidGlass === 'object' && this.liquidGlass !== null
46
+ ? this.liquidGlass
47
+ : {
48
+ rim: this.rim,
49
+ ior: this.ior,
50
+ height: this.height,
51
+ frosted: this.frosted,
52
+ fusion: this.fusion,
53
+ borderRadius: this.borderRadius
54
+ };
55
+ this._controller.update(opts);
56
+ }
57
+ }
58
+
59
+ ngOnDestroy() {
60
+ if (this._controller) {
61
+ this._controller.destroy();
62
+ this._controller = null;
63
+ }
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Helper to attach LiquidGlass directly inside Angular ngAfterViewInit
69
+ */
70
+ export function attachAngularLiquidGlass(elementRef, options = {}) {
71
+ const el = elementRef && elementRef.nativeElement ? elementRef.nativeElement : elementRef;
72
+ return new LiquidGlassController(el, options);
73
+ }
74
+
75
+ export { LiquidGlassController, OPTICAL_DEFAULTS, OPTICAL_LIMITS };
76
+ export default LiquidGlassDirective;