@boyernick/standard-ui-react 0.4.2-canary.59 → 0.4.2-canary.61

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.
@@ -0,0 +1,138 @@
1
+ export const VERTEX_SHADER = `
2
+ attribute vec2 a_position;
3
+ varying vec2 v_uv;
4
+ void main() {
5
+ v_uv = a_position * 0.5 + 0.5;
6
+ gl_Position = vec4(a_position, 0.0, 1.0);
7
+ }`;
8
+
9
+ /**
10
+ * One pane, drawn into its own output rectangle. Coordinates are CSS pixels
11
+ * with y down: `u_origin` is the output's top-left in scene space, and the
12
+ * pane is centred on `u_center` with half-size `u_half`.
13
+ */
14
+ export const FRAGMENT_SHADER = `
15
+ precision highp float;
16
+ varying vec2 v_uv;
17
+
18
+ uniform sampler2D u_scene;
19
+ uniform vec2 u_sceneSize;
20
+ uniform vec2 u_origin;
21
+ uniform vec2 u_outSize;
22
+ uniform vec2 u_center;
23
+ uniform vec2 u_half;
24
+ uniform float u_dpr;
25
+
26
+ uniform float u_radius;
27
+ uniform float u_power;
28
+ uniform float u_bezel;
29
+ uniform float u_bend;
30
+ uniform float u_dispersionRed;
31
+ uniform float u_dispersionBlue;
32
+ uniform float u_rim;
33
+ uniform float u_rimWidth;
34
+ uniform float u_specular;
35
+ uniform float u_frost;
36
+ uniform float u_grain;
37
+ uniform float u_dim;
38
+ uniform float u_fadeStart;
39
+ uniform float u_fadeEnd;
40
+ uniform float u_shadow;
41
+ uniform float u_hover;
42
+ uniform float u_press;
43
+
44
+ // A rounded rectangle whose corners are superellipses. Power 2 is a circular
45
+ // arc; around 4 is the continuous "squircle" corner.
46
+ float sdPane(vec2 p) {
47
+ float r = min(u_radius, min(u_half.x, u_half.y));
48
+ vec2 q = abs(p) - u_half + r;
49
+ vec2 m = max(q, 0.0);
50
+ float corner = u_power == 2.0
51
+ ? length(m)
52
+ : pow(pow(m.x, u_power) + pow(m.y, u_power), 1.0 / u_power);
53
+ return corner + min(max(q.x, q.y), 0.0) - r;
54
+ }
55
+
56
+ vec2 outwardNormal(vec2 p) {
57
+ float e = 0.5;
58
+ vec2 g = vec2(
59
+ sdPane(p + vec2(e, 0.0)) - sdPane(p - vec2(e, 0.0)),
60
+ sdPane(p + vec2(0.0, e)) - sdPane(p - vec2(0.0, e))
61
+ );
62
+ float l = length(g);
63
+ return l > 1e-5 ? g / l : vec2(0.0);
64
+ }
65
+
66
+ vec3 ground(vec2 scenePx) {
67
+ vec2 uv = clamp(scenePx / u_sceneSize, vec2(0.0), vec2(1.0));
68
+ return texture2D(u_scene, uv).rgb;
69
+ }
70
+
71
+ // Frost: 16 taps on a golden-angle spiral out to about two sigma.
72
+ vec3 frosted(vec2 scenePx) {
73
+ if (u_frost < 0.05) return ground(scenePx);
74
+ vec3 sum = vec3(0.0);
75
+ float weights = 0.0;
76
+ for (int i = 0; i < 16; i++) {
77
+ float fi = float(i);
78
+ float r = sqrt((fi + 0.5) / 16.0) * 2.0 * u_frost;
79
+ float a = fi * 2.39996323;
80
+ float w = exp(-(r * r) / (2.0 * u_frost * u_frost));
81
+ sum += ground(scenePx + vec2(cos(a), sin(a)) * r) * w;
82
+ weights += w;
83
+ }
84
+ return sum / weights;
85
+ }
86
+
87
+ float hash(vec2 p) {
88
+ return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
89
+ }
90
+
91
+ void main() {
92
+ vec2 scenePx = u_origin + vec2(v_uv.x, 1.0 - v_uv.y) * u_outSize;
93
+ vec2 p = scenePx - u_center;
94
+ float d = sdPane(p);
95
+
96
+ // Outside: only a soft contact shadow, heavier below the pane.
97
+ if (d > 0.0) {
98
+ float below = smoothstep(-0.6, 0.8, p.y / max(u_half.y, 1.0));
99
+ float shadow = u_shadow * (1.0 - 0.35 * u_press) * exp(-(d * d) / 40.0) * below;
100
+ gl_FragColor = vec4(0.0, 0.0, 0.0, shadow);
101
+ return;
102
+ }
103
+
104
+ float inside = -d;
105
+ vec2 n = outwardNormal(p);
106
+
107
+ // The bezel bends the ground outward across its width: the pane shows what
108
+ // lies just past its edge, compressed into the band, as a thick lens does.
109
+ float t = u_bezel > 0.0 ? clamp(1.0 - inside / u_bezel, 0.0, 1.0) : 0.0;
110
+ float profile = t * t * (3.0 - 2.0 * t);
111
+ vec2 offset = n * u_bend * (1.0 - 0.35 * u_press) * profile;
112
+
113
+ vec3 color = vec3(
114
+ frosted(scenePx + offset * u_dispersionRed).r,
115
+ frosted(scenePx + offset).g,
116
+ frosted(scenePx + offset * u_dispersionBlue).b
117
+ );
118
+ color *= u_dim + 0.06 * u_hover;
119
+
120
+ // Rim light, strongest where the edge faces the light (top left), and a
121
+ // sheen across the lit side of the bezel.
122
+ vec2 light = normalize(vec2(-0.55, -1.0));
123
+ float facing = max(dot(n, light), 0.0);
124
+ float rim = 1.0 - smoothstep(0.0, u_rimWidth, inside);
125
+ color += u_rim * (1.0 + 0.4 * u_hover) * rim * (0.55 + 0.45 * facing);
126
+ color += u_specular * 0.35 * pow(t, 3.0) * facing;
127
+
128
+ // Grain, fixed to the device pixel grid so a still pane never needs redrawing.
129
+ color += (hash(floor(scenePx * u_dpr)) - 0.5) * u_grain;
130
+
131
+ // Optional fade down the pane.
132
+ float yFrac = (p.y + u_half.y) / (2.0 * u_half.y);
133
+ float fade = u_fadeEnd > u_fadeStart ? 1.0 - smoothstep(u_fadeStart, u_fadeEnd, yFrac) : 1.0;
134
+ color = mix(ground(scenePx), color, fade);
135
+
136
+ float coverage = clamp(inside * u_dpr + 0.5, 0.0, 1.0);
137
+ gl_FragColor = vec4(clamp(color, 0.0, 1.0), coverage);
138
+ }`;
@@ -0,0 +1,12 @@
1
+ import type { GlassConfig } from "./lens-glass/config";
2
+
3
+ /**
4
+ * Shared material for controls over images and video. Their icons are white
5
+ * (`text-fg-scrim`), so the ground is dimmed further than the default to keep
6
+ * them legible over a bright frame, and frosted a little more.
7
+ */
8
+ export const mediaGlass: GlassConfig = {
9
+ dim: 0.62,
10
+ frost: 4,
11
+ shadow: 0.2,
12
+ }
package/src/modal.tsx CHANGED
@@ -14,7 +14,9 @@ import {
14
14
  type ReactElement,
15
15
  type ReactNode,
16
16
  } from "react"
17
- import { Button, buttonVariants } from "./button"
17
+ import { buttonVariants } from "./button"
18
+ import { GlassButton, GlassRoot } from "./glass"
19
+ import { mediaGlass } from "./lib/media-glass"
18
20
  import {
19
21
  Carousel,
20
22
  CarouselContent,
@@ -51,10 +53,9 @@ import {
51
53
  const TRACKPAD_LABEL =
52
54
  "Swipe, use the arrow keys, or use the controls to navigate"
53
55
 
54
- // Glass takes a glass hover: the lens brightens. The ghost button's own
55
- // hover fill is a page grey and is cleared, or it shows through the body.
56
+ // Match the video controls while preserving the image viewer focus treatment.
56
57
  const modalControlClassName =
57
- "glass glass-dark hover:bg-transparent hover:brightness-150 text-fg-scrim hover:text-fg-scrim outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-offset-1 focus-visible:ring-offset-surface-scrim focus-visible:ring-ring/20"
58
+ "glass-optical glass-optical-media text-fg-scrim hover:text-fg-scrim outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-offset-1 focus-visible:ring-offset-surface-scrim focus-visible:ring-ring/20"
58
59
 
59
60
  export const modalContentVariants = cva(
60
61
  "h-dvh max-h-dvh w-screen max-w-none gap-0 overflow-hidden rounded-none border-0 bg-transparent p-0 shadow-none max-sm:max-w-none",
@@ -249,11 +250,14 @@ const ModalDownload = ({
249
250
  <ModalControlTooltip label="Download image">
250
251
  <a
251
252
  href={src}
253
+ data-slot="glass"
254
+ data-glass-radius="css"
255
+ data-config={JSON.stringify({ ...mediaGlass, button: true })}
252
256
  download={name ?? ""}
253
257
  aria-label="Download image"
254
258
  className={cn(
255
259
  buttonVariants({
256
- variant: "ghost",
260
+ variant: "glass",
257
261
  size: "md",
258
262
  rounded: true,
259
263
  iconOnly: true,
@@ -273,9 +277,9 @@ const ModalDismiss = () => (
273
277
  <ModalControlTooltip label="Close image">
274
278
  <DialogClose
275
279
  render={
276
- <Button
280
+ <GlassButton
277
281
  type="button"
278
- variant="ghost"
282
+ config={mediaGlass}
279
283
  size="md"
280
284
  iconOnly
281
285
  rounded
@@ -322,22 +326,24 @@ export const ModalContent = ({
322
326
  {...props}
323
327
  >
324
328
  <TooltipProvider delay={250}>
325
- <ModalBackground src={src} />
326
- <DialogTitle className="sr-only">{alt}</DialogTitle>
327
- <div
328
- data-slot="modal-stage"
329
- className="relative z-10 flex size-full flex-col items-center justify-center p-4"
330
- >
331
- <ModalImage src={src} alt={alt} imgProps={imgProps} />
332
- {variant === "caption" && (caption || children) ? (
333
- <ModalCaption>{caption ?? children}</ModalCaption>
334
- ) : null}
335
- </div>
336
- <ModalDismiss />
337
- <ModalDownload
338
- src={downloadSrc ?? src}
339
- name={downloadName}
340
- />
329
+ <GlassRoot interactiveLighting defaults={mediaGlass} className="size-full [--glass-ground:var(--color-surface-scrim)]">
330
+ <ModalBackground src={src} />
331
+ <DialogTitle className="sr-only">{alt}</DialogTitle>
332
+ <div
333
+ data-slot="modal-stage"
334
+ className="relative z-10 flex size-full flex-col items-center justify-center p-4"
335
+ >
336
+ <ModalImage src={src} alt={alt} imgProps={imgProps} />
337
+ {variant === "caption" && (caption || children) ? (
338
+ <ModalCaption>{caption ?? children}</ModalCaption>
339
+ ) : null}
340
+ </div>
341
+ <ModalDismiss />
342
+ <ModalDownload
343
+ src={downloadSrc ?? src}
344
+ name={downloadName}
345
+ />
346
+ </GlassRoot>
341
347
  </TooltipProvider>
342
348
  </DialogPopup>
343
349
  </DialogPortal>
@@ -493,99 +499,101 @@ const GalleryContent = ({
493
499
  )}
494
500
  >
495
501
  <TooltipProvider delay={250}>
496
- <ModalBackground src={activeImage.src} />
497
- <DialogTitle className="sr-only">{activeImage.alt}</DialogTitle>
498
- <p className="sr-only" aria-live="polite">
499
- Image {activeIndex + 1} of {images.length}. {TRACKPAD_LABEL}.
500
- </p>
501
- <div className="relative z-10 flex size-full flex-col gap-4 p-4">
502
- <Carousel
503
- setApi={setApi}
504
- opts={{ loop, startIndex: activeIndex, duration: 24, dragFree: false }}
505
- data-slot="modal-stage"
506
- className="relative min-h-0 w-full min-w-0 flex-1 [&_[data-slot=carousel-viewport]]:h-full [&_[data-slot=carousel-viewport]]:min-h-0"
507
- aria-label="Gallery"
508
- >
509
- <CarouselContent className="h-full !ml-0 !pr-0">
510
- {images.map((image, index) => (
511
- <CarouselItem
512
- key={`${image.src}-${index}`}
513
- className="!basis-full flex h-full min-h-0 flex-col items-center justify-center !pl-0"
514
- aria-label={`${index + 1} of ${images.length}`}
515
- >
516
- <div className="flex max-h-full min-h-0 w-full flex-col items-center justify-center">
517
- <div className="flex min-h-0 w-full items-center justify-center overflow-hidden">
518
- <ModalImage
519
- {...image}
520
- fit="contain"
521
- imgProps={{
522
- ...image.imgProps,
523
- className: cn(
524
- "max-w-[min(94vw,72rem)]",
525
- image.imgProps?.className,
526
- ),
527
- }}
528
- />
502
+ <GlassRoot interactiveLighting defaults={mediaGlass} className="size-full [--glass-ground:var(--color-surface-scrim)]">
503
+ <ModalBackground src={activeImage.src} />
504
+ <DialogTitle className="sr-only">{activeImage.alt}</DialogTitle>
505
+ <p className="sr-only" aria-live="polite">
506
+ Image {activeIndex + 1} of {images.length}. {TRACKPAD_LABEL}.
507
+ </p>
508
+ <div className="relative z-10 flex size-full flex-col gap-4 p-4">
509
+ <Carousel
510
+ setApi={setApi}
511
+ opts={{ loop, startIndex: activeIndex, duration: 24, dragFree: false }}
512
+ data-slot="modal-stage"
513
+ className="relative min-h-0 w-full min-w-0 flex-1 [&_[data-slot=carousel-viewport]]:h-full [&_[data-slot=carousel-viewport]]:min-h-0"
514
+ aria-label="Gallery"
515
+ >
516
+ <CarouselContent className="h-full !ml-0 !pr-0">
517
+ {images.map((image, index) => (
518
+ <CarouselItem
519
+ key={`${image.src}-${index}`}
520
+ className="!basis-full flex h-full min-h-0 flex-col items-center justify-center !pl-0"
521
+ aria-label={`${index + 1} of ${images.length}`}
522
+ >
523
+ <div className="flex max-h-full min-h-0 w-full flex-col items-center justify-center">
524
+ <div className="flex min-h-0 w-full items-center justify-center overflow-hidden">
525
+ <ModalImage
526
+ {...image}
527
+ fit="contain"
528
+ imgProps={{
529
+ ...image.imgProps,
530
+ className: cn(
531
+ "max-w-[min(94vw,72rem)]",
532
+ image.imgProps?.className,
533
+ ),
534
+ }}
535
+ />
536
+ </div>
537
+ {variant === "caption" && image.caption ? (
538
+ <ModalCaption>{image.caption}</ModalCaption>
539
+ ) : null}
529
540
  </div>
530
- {variant === "caption" && image.caption ? (
531
- <ModalCaption>{image.caption}</ModalCaption>
532
- ) : null}
533
- </div>
534
- </CarouselItem>
535
- ))}
536
- </CarouselContent>
537
- </Carousel>
538
- <GalleryFilmstrip
539
- images={images}
540
- activeIndex={activeIndex}
541
- onSelect={handleSelect}
541
+ </CarouselItem>
542
+ ))}
543
+ </CarouselContent>
544
+ </Carousel>
545
+ <GalleryFilmstrip
546
+ images={images}
547
+ activeIndex={activeIndex}
548
+ onSelect={handleSelect}
549
+ />
550
+ </div>
551
+ <ModalDismiss />
552
+ <ModalDownload
553
+ src={activeImage.downloadSrc ?? activeImage.src}
554
+ name={activeImage.downloadName}
542
555
  />
543
- </div>
544
- <ModalDismiss />
545
- <ModalDownload
546
- src={activeImage.downloadSrc ?? activeImage.src}
547
- name={activeImage.downloadName}
548
- />
549
- {images.length > 1 ? (
550
- <>
551
- <ModalControlTooltip label="Previous image" side="right">
552
- <Button
553
- type="button"
554
- variant="ghost"
555
- size="lg"
556
- iconOnly
557
- rounded
558
- aria-label="Previous image"
559
- disabled={!loop && !api?.canScrollPrev()}
560
- onClick={() => api?.scrollPrev()}
561
- className={cn(
562
- modalControlClassName,
563
- "absolute top-1/2 left-4 z-20 -translate-y-1/2 active:!-translate-y-1/2",
564
- )}
565
- >
566
- <IconChevronRightSmall className="rotate-180" aria-hidden />
567
- </Button>
568
- </ModalControlTooltip>
569
- <ModalControlTooltip label="Next image" side="left">
570
- <Button
571
- type="button"
572
- variant="ghost"
573
- size="lg"
574
- iconOnly
575
- rounded
576
- aria-label="Next image"
577
- disabled={!loop && !api?.canScrollNext()}
578
- onClick={() => api?.scrollNext()}
579
- className={cn(
580
- modalControlClassName,
581
- "absolute top-1/2 right-4 z-20 -translate-y-1/2 active:!-translate-y-1/2",
582
- )}
583
- >
584
- <IconChevronRightSmall aria-hidden />
585
- </Button>
586
- </ModalControlTooltip>
587
- </>
588
- ) : null}
556
+ {images.length > 1 ? (
557
+ <>
558
+ <ModalControlTooltip label="Previous image" side="right">
559
+ <GlassButton
560
+ type="button"
561
+ config={mediaGlass}
562
+ size="lg"
563
+ iconOnly
564
+ rounded
565
+ aria-label="Previous image"
566
+ disabled={!loop && !api?.canScrollPrev()}
567
+ onClick={() => api?.scrollPrev()}
568
+ className={cn(
569
+ modalControlClassName,
570
+ "absolute top-1/2 left-4 z-20 -translate-y-1/2 active:!-translate-y-1/2",
571
+ )}
572
+ >
573
+ <IconChevronRightSmall className="rotate-180" aria-hidden />
574
+ </GlassButton>
575
+ </ModalControlTooltip>
576
+ <ModalControlTooltip label="Next image" side="left">
577
+ <GlassButton
578
+ type="button"
579
+ config={mediaGlass}
580
+ size="lg"
581
+ iconOnly
582
+ rounded
583
+ aria-label="Next image"
584
+ disabled={!loop && !api?.canScrollNext()}
585
+ onClick={() => api?.scrollNext()}
586
+ className={cn(
587
+ modalControlClassName,
588
+ "absolute top-1/2 right-4 z-20 -translate-y-1/2 active:!-translate-y-1/2",
589
+ )}
590
+ >
591
+ <IconChevronRightSmall aria-hidden />
592
+ </GlassButton>
593
+ </ModalControlTooltip>
594
+ </>
595
+ ) : null}
596
+ </GlassRoot>
589
597
  </TooltipProvider>
590
598
  </DialogPopup>
591
599
  </DialogPortal>
@@ -18,6 +18,7 @@ import { IconVolumeFull } from "@central-icons-react/round-filled-radius-2-strok
18
18
  import { IconVolumeOff } from "@central-icons-react/round-filled-radius-2-stroke-2/IconVolumeOff"
19
19
  import { cn } from "./lib/cn"
20
20
  import { Glass, GlassButton, GlassRoot } from "./glass"
21
+ import { mediaGlass } from "./lib/media-glass"
21
22
 
22
23
  /**
23
24
  * Picture-in-Picture support is a static browser capability, not React state.
@@ -36,19 +37,7 @@ const formatTime = (seconds: number) => {
36
37
  }
37
38
 
38
39
  // All panes are direct children of the video root, sharing one WebGL context.
39
- const videoGlass = {
40
- blurAmount: 0.08,
41
- refraction: 0.12,
42
- zRadius: 3,
43
- chromAberration: 0,
44
- brightness: -0.3,
45
- edgeHighlight: 0.045,
46
- specular: 0.02,
47
- fresnel: 1,
48
- shadowOpacity: 0.065,
49
- shadowSpread: 12,
50
- shadowOffsetY: 3,
51
- }
40
+
52
41
 
53
42
  export type VideoPlayerProps = ComponentProps<"div"> & {
54
43
  src: string
@@ -284,7 +273,7 @@ export const VideoPlayer = ({
284
273
  return (
285
274
  <GlassRoot
286
275
  interactiveLighting
287
- defaults={videoGlass}
276
+ defaults={mediaGlass}
288
277
  ref={playerRef}
289
278
  data-slot="video-player"
290
279
  data-playing={playing || undefined}
@@ -350,7 +339,7 @@ export const VideoPlayer = ({
350
339
  type="button"
351
340
  rounded
352
341
  iconOnly
353
- config={videoGlass}
342
+ config={mediaGlass}
354
343
  className={cn(controlVisibility, "left-2 group-data-[fullscreen]:left-5", (failed || loading) && "invisible")}
355
344
  aria-label={ended ? "Replay" : playing ? "Pause" : "Play"}
356
345
  onClick={handleTogglePlay}
@@ -365,7 +354,7 @@ export const VideoPlayer = ({
365
354
  type="button"
366
355
  rounded
367
356
  iconOnly
368
- config={videoGlass}
357
+ config={mediaGlass}
369
358
  className={cn(controlVisibility, "left-13 group-data-[fullscreen]:left-16", (failed || loading) && "invisible")}
370
359
  aria-label={muted ? "Unmute" : "Mute"}
371
360
  onClick={handleToggleMute}
@@ -473,7 +462,7 @@ export const VideoPlayer = ({
473
462
  type="button"
474
463
  rounded
475
464
  iconOnly
476
- config={videoGlass}
465
+ config={mediaGlass}
477
466
  className={cn(controlVisibility, "right-13 group-data-[fullscreen]:right-16", (!pipSupported || failed || loading) && "invisible")}
478
467
  aria-label={
479
468
  pipActive
@@ -494,7 +483,7 @@ export const VideoPlayer = ({
494
483
  type="button"
495
484
  rounded
496
485
  iconOnly
497
- config={videoGlass}
486
+ config={mediaGlass}
498
487
  className={cn(controlVisibility, "right-2 group-data-[fullscreen]:right-5", (failed || loading) && "invisible")}
499
488
  aria-label={fullscreen ? "Exit fullscreen" : "Fullscreen"}
500
489
  aria-pressed={fullscreen}
@@ -1,24 +0,0 @@
1
- # LiquidGlass
2
-
3
- Vendored from `@ybouane/liquidglass@1.0.3`.
4
-
5
- Source: https://github.com/ybouane/liquidglass
6
- License: MIT (as declared by the upstream package).
7
-
8
- `index.js` is the package's published bundled WebGL renderer. StandardUI
9
- vendors the build because version 1.0.3 publishes a `postinstall`
10
- command whose `patch-package` executable is not a runtime dependency.
11
-
12
- Local patch: `LiquidGlass.init()` destroys a partially initialized instance
13
- before rethrowing a startup error, releasing its canvas, listeners and WebGL
14
- resources.
15
-
16
- StandardUI's `data-glass-radius="css"` panes derive the shader radius from the
17
- computed CSS radius. Disabled buttons suppress shader hover/press feedback.
18
-
19
- Responsive images are decoded from `currentSrc` without `srcset` before canvas
20
- sampling. This keeps object-fit crop coordinates in resource pixels instead of
21
- mixing density-corrected `naturalWidth` with full-resolution canvas coordinates.
22
-
23
- Injected canvases compensate for the host's border so the rendered lens stays
24
- centered on bordered controls and their icons.
@@ -1,45 +0,0 @@
1
- /**
2
- * Type surface for the vendored @ybouane/liquidglass 1.0.3 renderer.
3
- * Source: https://github.com/ybouane/liquidglass
4
- * License: MIT
5
- */
6
-
7
- export interface GlassConfig {
8
- blurAmount: number;
9
- refraction: number;
10
- chromAberration: number;
11
- edgeHighlight: number;
12
- specular: number;
13
- fresnel: number;
14
- distortion: number;
15
- cornerRadius: number;
16
- zRadius: number;
17
- opacity: number;
18
- saturation: number;
19
- tintStrength: number;
20
- brightness: number;
21
- shadowOpacity: number;
22
- shadowSpread: number;
23
- shadowOffsetY: number;
24
- floating: boolean;
25
- button: boolean;
26
- bevelMode: number;
27
- }
28
-
29
- export interface LiquidGlassOptions {
30
- root: HTMLElement;
31
- glassElements?: NodeListOf<HTMLElement> | HTMLElement[];
32
- defaults?: Partial<GlassConfig>;
33
- }
34
-
35
- export declare class LiquidGlass {
36
- static init(options: LiquidGlassOptions): Promise<LiquidGlass>;
37
- readonly root: HTMLElement;
38
- readonly defaults: GlassConfig;
39
- fps: number;
40
- destroy(): void;
41
- markChanged(element?: HTMLElement): void;
42
- }
43
-
44
- export declare const DEFAULTS: GlassConfig;
45
- export declare function invalidateFontEmbedCache(): void;