glasskit-js 1.0.0 → 1.0.2

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/LiquidGlass.mjs CHANGED
@@ -19,7 +19,7 @@ import LG from "./liquid-glass.js";
19
19
  const GLASS_KEYS = [
20
20
  "mode", "frost", "refraction", "depth", "dispersion", "splay",
21
21
  "lightAngle", "lightIntensity", "curvature", "convexity",
22
- "tint", "tintOpacity", "sheen", "sheenColor", "saturate", "brightness", "radius", "background",
22
+ "tint", "tintOpacity", "sheen", "sheenColor", "saturate", "brightness", "shadow", "radius", "background",
23
23
  ];
24
24
 
25
25
  export default function LiquidGlass({ as: Tag = "div", children, ...props }) {
package/README.md CHANGED
@@ -1,54 +1,28 @@
1
1
  # Glasskit
2
2
 
3
- **Drop-in Apple / Figma "Liquid Glass" for the web — and the generator that ships the code.**
3
+ **Drop-in Apple / Figma "Liquid Glass" for the web.**
4
+
5
+ [![npm version](https://img.shields.io/npm/v/glasskit-js?color=8b5cf6&label=npm)](https://www.npmjs.com/package/glasskit-js)
6
+ [![minzipped size](https://img.shields.io/bundlephobia/minzip/glasskit-js?color=8b5cf6)](https://bundlephobia.com/package/glasskit-js)
7
+ [![zero dependencies](https://img.shields.io/badge/dependencies-0-8b5cf6)](https://www.npmjs.com/package/glasskit-js?activeTab=dependencies)
8
+ [![types included](https://img.shields.io/npm/types/glasskit-js?color=8b5cf6)](./liquid-glass.d.ts)
9
+ [![license MIT](https://img.shields.io/npm/l/glasskit-js?color=8b5cf6)](./LICENSE)
4
10
 
5
11
  The only liquid-glass tool that lets you **switch the rendering engine** (pure CSS · SVG
6
- displacement · cross-browser clone · WebGL), apply it to **any element/shape**, **copy the
7
- code in any framework**, and **measure the real performance cost** before you ship.
12
+ displacement · cross-browser clone · WebGL), apply it to **any element/shape**, and use it
13
+ from **vanilla JS, a web component, or React** with zero dependencies.
8
14
 
9
- > npm: **`glasskit-js`** · global: **`Glasskit`** · web component: **`<glass-kit>`**
15
+ > 🎛️ **[Try the generator →](https://amanblog.github.io/glasskit/)** tune it visually and copy the code in any framework.
10
16
 
17
+ ```bash
18
+ npm i glasskit-js
11
19
  ```
12
- liquid-glass.js ← the engine (zero deps, UMD + <glass-kit> web component)
13
- liquid-glass.d.ts ← TypeScript types
14
- LiquidGlass.mjs ← React/Next.js wrapper (default export, no JSX — works in any bundler)
15
- package.json ← npm package "glasskit-js"
16
- demo.html ← minimal standalone playground
17
- site/ ← the generator website (static, deploy as-is)
18
- index.html · app.js · benchmark.html · liquid-glass.js
19
- bench/ ← automated cross-browser benchmark runner (Playwright)
20
- ```
21
-
22
- ---
23
-
24
- ## The engine
25
-
26
- ### Modes — the switch nobody else gives you
27
20
 
28
- | Mode | Real refraction | Browsers | Refracts | Best for |
29
- |------|:---:|---|---|---|
30
- | `css` | ✗ (blur) | **all** | live backdrop | default product UI, mobile, Safari/FF |
31
- | `svg` | ✓ | **Chromium** | live backdrop | the "wow" surface on Chrome/Edge |
32
- | `svg-clone` | ✓ | **all** | a cloned DOM element | cross-browser refraction over DOM |
33
- | `webgl` | ✓ | **all** | an img/canvas/video | hero over a fixed background/video |
34
- | `auto` | — | — | — | Chromium→`svg`, else `svg-clone` if `background` set, else `css` |
35
-
36
- `svg-clone` is the cross-browser trick: Safari/Firefox don't allow an SVG filter in
37
- `backdrop-filter`, so it **clones the background element and filters the clone** instead.
38
- It refracts DOM, not `<canvas>` pixels — use `webgl` for canvas/video backgrounds.
39
-
40
- ### Params ↔ Figma's Glass panel
21
+ > npm: **`glasskit-js`** · global: **`Glasskit`** · web component: **`<glass-kit>`**
41
22
 
42
- | Figma slider | Option | | Optical extras | Option |
43
- |---|---|---|---|---|
44
- | Frost | `frost` | | curvature (sphere→squircle) | `curvature` |
45
- | Refraction | `refraction` | | convex↔concave | `convexity` |
46
- | Depth | `depth` | | tint | `tint`, `tintOpacity` |
47
- | Dispersion | `dispersion` | | corner radius | `radius` |
48
- | Splay | `splay` | | | |
49
- | Light (angle / %) | `lightAngle` / `lightIntensity` | | | |
23
+ ---
50
24
 
51
- ### Usage
25
+ ## Usage
52
26
 
53
27
  ```html
54
28
  <!-- vanilla / CDN -->
@@ -74,71 +48,45 @@ import Glass from 'glasskit-js/react';
74
48
  style={{ borderRadius: 999, padding: '14px 28px' }}>Get tickets</Glass>
75
49
  ```
76
50
 
77
- ### Shapes
78
- Any **rounded rectangle (incl. pills & circles)** works with zero extra code — a
79
- `ResizeObserver` regenerates the refraction map on resize. Arbitrary outlines (blobs,
80
- SVG paths) keep the blur/tint/highlight via `clip-path`, but refraction edges assume a
81
- rounded box; supply a custom displacement map for true custom outlines.
82
-
83
51
  ---
84
52
 
85
- ## The generator website (`site/`)
86
-
87
- Static — no build step. Tune visually → switch engine → copy code for React, Vue,
88
- vanilla, web component, CSS, or Tailwind. Plus a preset gallery and a benchmark page.
89
-
90
- ```bash
91
- npx serve site # or: python3 -m http.server --directory site
92
- ```
93
-
94
- **Deploy to Cloudflare Pages:** create a Pages project, set the **build command empty**
95
- and the **output/root directory to `site`**. Pure static files. (Vercel: framework
96
- "Other", output dir `site`.) Then set your domain in the `og:`/`canonical` tags and add an
97
- `og.png` (a generator script lives at `bench/og.mjs`).
53
+ ## Modes the switch nobody else gives you
98
54
 
99
- ---
55
+ | Mode | Real refraction | Browsers | Refracts | Best for |
56
+ |------|:---:|---|---|---|
57
+ | `css` | ✗ (blur) | **all** | live backdrop | default product UI, mobile, Safari/FF |
58
+ | `svg` | ✓ | **Chromium** | live backdrop | the "wow" surface on Chrome/Edge |
59
+ | `svg-clone` | ✓ | **all** | a cloned DOM element | cross-browser refraction over DOM |
60
+ | `webgl` | ✓ | **all** | an img/canvas/video | hero over a fixed background/video |
61
+ | `auto` | — | — | — | Chromium→`svg`, else `svg-clone` if `background` set, else `css` |
100
62
 
101
- ## The benchmark
63
+ `svg-clone` is the cross-browser trick: Safari/Firefox don't allow an SVG filter in
64
+ `backdrop-filter`, so it **clones the background element and filters the clone** instead.
65
+ It refracts DOM, not `<canvas>` pixels — use `webgl` for canvas/video backgrounds.
102
66
 
103
- **In-browser** (`site/benchmark.html`): spawns batches of glass elements per engine,
104
- animates them over a live backdrop, and records **avg/min FPS, avg & p95 frame time,
105
- jank %, and JS heap**. Results are saved per browser in `localStorage`, so running it in
106
- Chrome → Safari → Firefox builds a cross-browser comparison. Export JSON anytime.
67
+ ## Params Figma's Glass panel
107
68
 
108
- **Automated** (`bench/run-playwright.mjs`): drives the page across every installed engine.
69
+ | Figma slider | Option | | Optical extras | Option |
70
+ |---|---|---|---|---|
71
+ | Frost | `frost` | | curvature (sphere→squircle) | `curvature` |
72
+ | Refraction | `refraction` | | convex↔concave | `convexity` |
73
+ | Depth | `depth` | | tint | `tint`, `tintOpacity` |
74
+ | Dispersion | `dispersion` | | corner radius | `radius` |
75
+ | Splay | `splay` | | | |
76
+ | Light (angle / %) | `lightAngle` / `lightIntensity` | | | |
109
77
 
110
- ```bash
111
- npm i # installs playwright-core
112
- npm run bench # uses your installed Chrome
113
- node bench/run-playwright.mjs --dur 2.5 --counts 1,5,15,30,60 --modes css,svg,svg-clone,webgl
114
- npx playwright install webkit firefox # to also bench Safari & Firefox engines
115
- ```
116
- Writes `bench/results.json` and `bench/results.csv`.
78
+ ## Shapes
117
79
 
118
- > Note: headless reports vsync-capped 60fps; real differences show under load and on
119
- > retina (`dpr 2`). `svg-clone` is the heaviest (one clone per element); `webgl` is
120
- > capped by the browser's ~8–16 live GL contexts both are surfaced as findings.
80
+ Any **rounded rectangle (incl. pills & circles)** works with zero extra code — a
81
+ `ResizeObserver` regenerates the refraction map on resize. Arbitrary outlines (blobs,
82
+ SVG paths) keep the blur/tint/highlight via `clip-path`, but refraction edges assume a
83
+ rounded box; supply a custom displacement map for true custom outlines.
121
84
 
122
85
  ---
123
86
 
124
- ## Releasing (CI)
125
-
126
- Two GitHub Actions handle shipping:
127
-
128
- - **`.github/workflows/pages.yml`** — deploys `site/` to GitHub Pages on every push to `main` that touches `site/`.
129
- - **`.github/workflows/release.yml`** — publishes to npm when a `v*` tag is pushed, via **npm Trusted Publishing (OIDC)** — no `NPM_TOKEN`, provenance generated automatically.
130
-
131
- **One-time bootstrap** (npm requires the package to exist before a trusted publisher can be configured):
132
-
133
- 1. `npm publish --access public` once from a logged-in machine to create `glasskit-js`.
134
- 2. npmjs.com → the package → **Settings → Trusted Publisher** → GitHub Actions, repo `amanblog/glasskit`, workflow `release.yml`.
135
-
136
- After that, cutting a release is tokenless:
137
-
138
- ```bash
139
- npm version patch # bumps package.json + creates the vX.Y.Z tag & commit
140
- git push --follow-tags # CI verifies tag == version, then publishes with provenance
141
- ```
87
+ Built something with it, or want to hack on the engine, generator, or benchmark?
88
+ See **[CONTRIBUTING.md](./CONTRIBUTING.md)**.
142
89
 
143
90
  ## License
144
- MIT — see [LICENSE](LICENSE).
91
+
92
+ MIT — see [LICENSE](./LICENSE).
package/liquid-glass.d.ts CHANGED
@@ -43,6 +43,8 @@ export interface LiquidGlassOptions {
43
43
  saturate?: number;
44
44
  /** Backdrop brightness (css mode). @default 1.04 */
45
45
  brightness?: number;
46
+ /** Outer drop shadow as any CSS box-shadow value; "none" or "" removes it. The inner light border/bezel are controlled by `lightIntensity`. @default "0 8px 30px rgba(0,0,0,0.18)" */
47
+ shadow?: string;
46
48
  /** Override corner radius (px). null = read element's border-radius. @default null */
47
49
  radius?: number | null;
48
50
  /** Element or selector to refract. Required for `svg-clone` and `webgl`. */
package/liquid-glass.js CHANGED
@@ -42,6 +42,7 @@
42
42
  sheenColor: '255,255,255', // "r,g,b" — recolor the gloss
43
43
  saturate: 1.4,
44
44
  brightness: 1.04,
45
+ shadow: '0 8px 30px rgba(0,0,0,0.18)', // outer drop shadow; 'none'/'' removes it, or pass any CSS box-shadow
45
46
  radius: null, // null = read element border-radius
46
47
  background: null // Element|selector — required for 'svg-clone' & 'webgl'
47
48
  };
@@ -58,6 +59,11 @@
58
59
  function clamp8(v) { return v < 0 ? 0 : v > 255 ? 255 : Math.round(v); }
59
60
  // only ever emit a clean "r,g,b" triplet into inline CSS — blocks url()/expression smuggling
60
61
  function safeRGB(v) { return (typeof v === 'string' && /^\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*$/.test(v)) ? v.replace(/\s+/g, '') : '255,255,255'; }
62
+ // allow only box-shadow-shaped CSS (lengths/colors); reject url()/expression/extra declarations
63
+ function safeShadow(v) {
64
+ if (typeof v !== 'string' || !v || v === 'none') return '';
65
+ return /url\(|expression|javascript:|[;{}<>]/i.test(v) ? '' : v;
66
+ }
61
67
  function num(v, d) { var n = parseFloat(v); return isFinite(n) ? n : d; }
62
68
  function resolveEl(x) { return typeof x === 'string' ? document.querySelector(x) : x; }
63
69
  function readRadius(el) { return parseFloat(getComputedStyle(el).borderRadius) || 0; }
@@ -251,11 +257,13 @@
251
257
  overlay.style.background = sh <= 0 ? 'none' :
252
258
  'linear-gradient(' + (num(o.lightAngle, -45) + 90) + 'deg,rgba(' + sc + ',' + (0.6 * sh).toFixed(3) +
253
259
  ') 0%,rgba(' + sc + ',0) 30%,rgba(' + sc + ',0) 70%,rgba(' + sc + ',' + (0.14 * sh).toFixed(3) + ') 100%)';
254
- overlay.style.boxShadow =
260
+ // inset light border + bezel (scale with lightIntensity) and a separately controllable outer drop shadow
261
+ var insets =
255
262
  'inset 0 0 0 1px rgba(255,255,255,' + (0.30 * li) + '),' +
256
263
  'inset ' + (Math.cos(a) * 2).toFixed(1) + 'px ' + (Math.sin(a) * 2).toFixed(1) + 'px 2px rgba(255,255,255,' + (0.5 * li) + '),' +
257
- 'inset ' + (-Math.cos(a) * 2).toFixed(1) + 'px ' + (-Math.sin(a) * 2).toFixed(1) + 'px 6px rgba(0,0,0,.15),' +
258
- '0 8px 30px rgba(0,0,0,.18)';
264
+ 'inset ' + (-Math.cos(a) * 2).toFixed(1) + 'px ' + (-Math.sin(a) * 2).toFixed(1) + 'px 6px rgba(0,0,0,.15)';
265
+ var drop = safeShadow(o.shadow);
266
+ overlay.style.boxShadow = drop ? insets + ',' + drop : insets;
259
267
  }
260
268
 
261
269
  /* ------------------------------ WebGL ------------------------------ */
@@ -389,7 +397,7 @@
389
397
  /* ===================== <glass-kit> web component ===================== */
390
398
  var ATTRS = ['mode', 'frost', 'refraction', 'depth', 'dispersion', 'splay', 'light-angle',
391
399
  'light-intensity', 'curvature', 'convexity', 'tint', 'tint-opacity', 'sheen', 'sheen-color',
392
- 'radius', 'background'];
400
+ 'shadow', 'radius', 'background'];
393
401
  function camel(s) { return s.replace(/-([a-z])/g, function (_, c) { return c.toUpperCase(); }); }
394
402
  function defineElement() {
395
403
  if (typeof customElements === 'undefined' || customElements.get('glass-kit')) return;
@@ -399,7 +407,7 @@
399
407
  ATTRS.forEach(function (a) {
400
408
  if (!node.hasAttribute(a)) return;
401
409
  var v = node.getAttribute(a), key = camel(a);
402
- opts[key] = (a === 'mode' || a === 'tint' || a === 'sheen-color' || a === 'background') ? v : parseFloat(v);
410
+ opts[key] = (a === 'mode' || a === 'tint' || a === 'sheen-color' || a === 'shadow' || a === 'background') ? v : parseFloat(v);
403
411
  });
404
412
  return opts;
405
413
  }
@@ -416,5 +424,5 @@
416
424
  }
417
425
  if (typeof window !== 'undefined') { if (document.readyState !== 'loading') defineElement(); else document.addEventListener('DOMContentLoaded', defineElement); }
418
426
 
419
- return { apply: apply, defineElement: defineElement, isChromium: isChromium, pickMode: pickMode, DEFAULTS: DEFAULTS, version: '1.0.0' };
427
+ return { apply: apply, defineElement: defineElement, isChromium: isChromium, pickMode: pickMode, DEFAULTS: DEFAULTS, version: '1.0.2' };
420
428
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glasskit-js",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Drop-in Apple/Figma 'Liquid Glass' for any element — switch between pure CSS, SVG displacement (live backdrop), cross-browser clone-mode, or WebGL. Real refraction, chromatic aberration, specular highlight. Zero dependencies. Class + <liquid-glass> web component.",
5
5
  "keywords": [
6
6
  "liquid-glass",