@opalui/react 0.0.1 → 0.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.
@@ -1,11 +1,38 @@
1
1
  /* Shared material. Public variables can be set once on a theme container. */
2
2
  :where(.opal-glass, .opal-button, .opal-input, .opal-menu-panel, .opal-switch, .opal-slider, .opal-segmented, .opal-textarea, .opal-select) {
3
3
  --_opal-glass-rim: var(--opal-glass-rim, inset 0 0 0 .5px rgb(255 255 255 / 7%), inset .65px 1px .65px rgb(255 255 255 / var(--opal-material-rim, .28)), inset -.4px -.65px .8px rgb(255 255 255 / calc(var(--opal-material-rim, .28) * .43)));
4
+ --_opal-glass-edge: 0 0 0 .5px rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .16)), -.65px 0 0 rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .48)), .65px 0 0 rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .48));
4
5
  --_opal-glass-shadow: var(--opal-glass-shadow, 0 3px 8px rgb(15 34 47 / 12%));
5
6
  }
6
7
 
7
- :where(.opal-input,.opal-textarea,.opal-select) { --opal-material-frost:12px; --opal-material-rim:0.22; }
8
- :where(.opal-menu-panel) { --opal-material-frost:24px; --opal-material-rim:0.3; }
8
+ /* One light material for resting controls: clear white face, fine contour,
9
+ a bright inner lip and a broad, low-contrast shadow. */
10
+ :where([data-theme="light"]) {
11
+ --opal-control-background: linear-gradient(180deg, rgb(255 255 255 / 40%) 0%, rgb(255 255 255 / 8%) 32%, rgb(255 255 255 / 4%) 65%, rgb(255 255 255 / 28%) 100%), rgb(255 255 255 / 32%);
12
+ --opal-control-active-background: rgb(255 255 255 / 60%);
13
+ --opal-control-rim-opacity: 1;
14
+ --opal-button-background: var(--opal-control-background);
15
+ --opal-button-hover-background: var(--opal-control-active-background);
16
+ --opal-button-color: #171717;
17
+ --opal-input-background: var(--opal-control-background);
18
+ --opal-input-focus-background: var(--opal-control-active-background);
19
+ --opal-input-color: #171717;
20
+ --opal-segmented-background: var(--opal-control-background);
21
+ --opal-glass-shadow: 0 5px 12px -4px rgb(0 0 0 / 4%);
22
+ --opal-glass-highlight:
23
+ inset 0 0 0 1px rgb(255 255 255 / 78%),
24
+ inset 0 1px 0 rgb(255 255 255 / 100%),
25
+ inset 0 -1px 0 rgb(255 255 255 / 90%);
26
+ }
27
+
28
+ /* Resolve the contour on each surface, where its material settings live.
29
+ edge=.5 reproduces the established 8%/24% light contour exactly. */
30
+ :where([data-theme="light"]) :where(.opal-glass, .opal-button, .opal-input, .opal-menu-panel, .opal-switch, .opal-slider, .opal-segmented, .opal-textarea, .opal-select) {
31
+ --_opal-glass-rim: var(--opal-glass-rim, var(--_opal-glass-edge), var(--opal-glass-highlight));
32
+ }
33
+
34
+ :where(.opal-input,.opal-textarea,.opal-select) { --opal-material-frost:12px; --opal-material-rim:0.22; --opal-material-edge:0.5; }
35
+ :where(.opal-menu-panel) { --opal-material-frost:24px; --opal-material-rim:0.3; --opal-material-edge:0.5; }
9
36
 
10
37
 
11
38
  /* The complete CSS material is server-rendered; lensing is optional. */
@@ -89,6 +116,7 @@
89
116
  /* A crisp upper-left reflection gives glass a curved edge without adding
90
117
  another gradient across the face. Theme-specific rims can override it. */
91
118
  .opal-button[data-variant="glass"]::after {
119
+ opacity: var(--opal-control-rim-opacity, .7);
92
120
  box-shadow: var(--opal-button-rim, var(--_opal-glass-rim));
93
121
  }
94
122
  .opal-button[data-variant="glass"]:not(:active) {
@@ -109,7 +137,8 @@
109
137
 
110
138
  /* Filled is opaque in every state and does not sample the backdrop. */
111
139
  .opal-button[data-variant="filled"] {
112
- color: var(--opal-button-text-color, var(--opal-button-color, var(--opal-button-filled-foreground, #fff)));
140
+ /* Glass theme colors must not override contrast selected for a solid fill. */
141
+ color: var(--opal-button-text-color, var(--opal-button-filled-foreground, #fff));
113
142
  }
114
143
  .opal-button[data-variant="filled"]::before {
115
144
  background: var(--opal-button-tint, var(--opal-button-filled-background, #007aff));
@@ -1 +1 @@
1
- export default "/* The complete CSS material is server-rendered; lensing is optional. */\n.opal-button-filter { position: absolute; pointer-events: none; }\n.opal-button {\n position: relative;\n z-index: 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n box-sizing: border-box;\n min-height: 2.875rem;\n padding: 0.75rem 1.125rem;\n border: 0;\n border-radius: var(--opal-button-radius, 999px);\n background: transparent;\n color: var(--opal-button-text-color, var(--opal-button-tint, var(--opal-button-color, #171e22)));\n box-shadow: var(--opal-button-shadow, 0 2px 6px rgb(15 34 47 / 10%));\n font: inherit;\n font-weight: 400;\n line-height: 1.25;\n cursor: pointer;\n touch-action: manipulation;\n -webkit-tap-highlight-color: transparent;\n transform: scale(1);\n transition:\n transform 600ms cubic-bezier(0.22, 0.61, 0.36, 1),\n box-shadow 450ms ease;\n}\n\n.opal-button[data-icon=\"leading\"] { padding-inline-start: 0.875rem; }\n.opal-button[data-icon=\"only\"] {\n width: 2.875rem;\n height: 2.875rem;\n padding: 0;\n flex-shrink: 0;\n}\n.opal-button-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: 0 0 auto;\n width: 1.25em;\n height: 1.25em;\n line-height: 1;\n pointer-events: none;\n}\n.opal-button-icon > svg,\n.opal-button-icon > img { display: block; width: 100%; height: 100%; }\n\n.opal-button::before,\n.opal-button::after {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: inherit;\n pointer-events: none;\n}\n\n.opal-button::before {\n z-index: -1;\n background: var(--opal-button-background, rgb(236 247 255 / 46%));\n -webkit-backdrop-filter: blur(12px) saturate(115%);\n backdrop-filter: var(--opal-material-filter, blur(var(--opal-material-frost, 12px))) saturate(115%);\n transition: background-color 240ms ease;\n}\n\n.opal-button::after {\n /* Diffuse light only along the edge; leave the center uniformly tinted. */\n box-shadow: var(--opal-button-rim,\n inset 0 0 0.6px 0.5px rgb(255 255 255 / 12%),\n inset 0 0.75px 1.3px rgb(255 255 255 / 24%),\n inset 0 -0.75px 1.3px rgb(255 255 255 / 14%));\n opacity: 0.7;\n transition: opacity 450ms ease;\n}\n\n.opal-button:active:not(:disabled)::after { opacity: 1; }\n\n/* A crisp upper-left reflection gives glass a curved edge without adding\n another gradient across the face. Theme-specific rims can override it. */\n.opal-button[data-variant=\"glass\"]::after {\n box-shadow: var(--opal-button-rim, var(--_opal-glass-rim));\n}\n.opal-button[data-variant=\"glass\"]:not(:active) {\n box-shadow: var(--opal-button-shadow, var(--_opal-glass-shadow));\n}\n\n@media (hover: hover) {\n .opal-button:not([data-variant=\"filled\"]):hover:not(:disabled)::before {\n background-color: var(--opal-button-hover-background, rgb(236 247 255 / 56%));\n }\n}\n\n.opal-button:active:not(:disabled) {\n transform: scale(var(--opal-button-press-scale, 1.045));\n transition-duration: 360ms, 450ms;\n box-shadow: var(--opal-button-pressed-shadow, 0 4px 10px rgb(15 34 47 / 14%));\n}\n\n/* Filled is opaque in every state and does not sample the backdrop. */\n.opal-button[data-variant=\"filled\"] {\n color: var(--opal-button-text-color, var(--opal-button-color, var(--opal-button-filled-foreground, #fff)));\n}\n.opal-button[data-variant=\"filled\"]::before {\n background: var(--opal-button-tint, var(--opal-button-filled-background, #007aff));\n box-shadow:\n inset 0 1px 2px rgb(255 255 255 / 24%),\n inset 0 -1px 2px rgb(255 255 255 / 10%);\n -webkit-backdrop-filter: none;\n backdrop-filter: none;\n transition: filter 240ms ease;\n}\n@media (hover: hover) {\n .opal-button[data-variant=\"filled\"]:hover:not(:disabled)::before { filter: brightness(1.08); }\n}\n\n.opal-button:focus-visible {\n outline: 2px solid var(--opal-focus-color, currentColor);\n outline-offset: 4px;\n}\n\n.opal-button:disabled { opacity: 0.4; cursor: not-allowed; }\n\n@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {\n .opal-button:not([data-variant=\"filled\"])::before, .opal-button:not([data-variant=\"filled\"]):hover:not(:disabled)::before {\n background-color: var(--opal-button-fallback-background, #ededed);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .opal-button, .opal-button::before, .opal-button::after { transition: none; }\n .opal-button:active:not(:disabled) { transform: none; }\n}\n\n@media (prefers-reduced-transparency: reduce) {\n .opal-button:not([data-variant=\"filled\"])::before, .opal-button:not([data-variant=\"filled\"]):hover:not(:disabled)::before {\n background: var(--opal-button-fallback-background, #ededed);\n -webkit-backdrop-filter: none;\n backdrop-filter: none;\n }\n}\n\n@media (forced-colors: active) {\n .opal-button[data-variant] { border: 1px solid ButtonText; background: ButtonFace; color: ButtonText; box-shadow: none; }\n .opal-button::before, .opal-button::after { display: none; }\n .opal-button:disabled { color: GrayText; border-color: GrayText; opacity: 1; }\n}\n";
1
+ export default "/* The complete CSS material is server-rendered; lensing is optional. */\n.opal-button-filter { position: absolute; pointer-events: none; }\n.opal-button {\n position: relative;\n z-index: 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n box-sizing: border-box;\n min-height: 2.875rem;\n padding: 0.75rem 1.125rem;\n border: 0;\n border-radius: var(--opal-button-radius, 999px);\n background: transparent;\n color: var(--opal-button-text-color, var(--opal-button-tint, var(--opal-button-color, #171e22)));\n box-shadow: var(--opal-button-shadow, 0 2px 6px rgb(15 34 47 / 10%));\n font: inherit;\n font-weight: 400;\n line-height: 1.25;\n cursor: pointer;\n touch-action: manipulation;\n -webkit-tap-highlight-color: transparent;\n transform: scale(1);\n transition:\n transform 600ms cubic-bezier(0.22, 0.61, 0.36, 1),\n box-shadow 450ms ease;\n}\n\n.opal-button[data-icon=\"leading\"] { padding-inline-start: 0.875rem; }\n.opal-button[data-icon=\"only\"] {\n width: 2.875rem;\n height: 2.875rem;\n padding: 0;\n flex-shrink: 0;\n}\n.opal-button-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: 0 0 auto;\n width: 1.25em;\n height: 1.25em;\n line-height: 1;\n pointer-events: none;\n}\n.opal-button-icon > svg,\n.opal-button-icon > img { display: block; width: 100%; height: 100%; }\n\n.opal-button::before,\n.opal-button::after {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: inherit;\n pointer-events: none;\n}\n\n.opal-button::before {\n z-index: -1;\n background: var(--opal-button-background, rgb(236 247 255 / 46%));\n -webkit-backdrop-filter: blur(12px) saturate(115%);\n backdrop-filter: var(--opal-material-filter, blur(var(--opal-material-frost, 12px))) saturate(115%);\n transition: background-color 240ms ease;\n}\n\n.opal-button::after {\n /* Diffuse light only along the edge; leave the center uniformly tinted. */\n box-shadow: var(--opal-button-rim,\n inset 0 0 0.6px 0.5px rgb(255 255 255 / 12%),\n inset 0 0.75px 1.3px rgb(255 255 255 / 24%),\n inset 0 -0.75px 1.3px rgb(255 255 255 / 14%));\n opacity: 0.7;\n transition: opacity 450ms ease;\n}\n\n.opal-button:active:not(:disabled)::after { opacity: 1; }\n\n/* A crisp upper-left reflection gives glass a curved edge without adding\n another gradient across the face. Theme-specific rims can override it. */\n.opal-button[data-variant=\"glass\"]::after {\n opacity: var(--opal-control-rim-opacity, .7);\n box-shadow: var(--opal-button-rim, var(--_opal-glass-rim));\n}\n.opal-button[data-variant=\"glass\"]:not(:active) {\n box-shadow: var(--opal-button-shadow, var(--_opal-glass-shadow));\n}\n\n@media (hover: hover) {\n .opal-button:not([data-variant=\"filled\"]):hover:not(:disabled)::before {\n background-color: var(--opal-button-hover-background, rgb(236 247 255 / 56%));\n }\n}\n\n.opal-button:active:not(:disabled) {\n transform: scale(var(--opal-button-press-scale, 1.045));\n transition-duration: 360ms, 450ms;\n box-shadow: var(--opal-button-pressed-shadow, 0 4px 10px rgb(15 34 47 / 14%));\n}\n\n/* Filled is opaque in every state and does not sample the backdrop. */\n.opal-button[data-variant=\"filled\"] {\n /* Glass theme colors must not override contrast selected for a solid fill. */\n color: var(--opal-button-text-color, var(--opal-button-filled-foreground, #fff));\n}\n.opal-button[data-variant=\"filled\"]::before {\n background: var(--opal-button-tint, var(--opal-button-filled-background, #007aff));\n box-shadow:\n inset 0 1px 2px rgb(255 255 255 / 24%),\n inset 0 -1px 2px rgb(255 255 255 / 10%);\n -webkit-backdrop-filter: none;\n backdrop-filter: none;\n transition: filter 240ms ease;\n}\n@media (hover: hover) {\n .opal-button[data-variant=\"filled\"]:hover:not(:disabled)::before { filter: brightness(1.08); }\n}\n\n.opal-button:focus-visible {\n outline: 2px solid var(--opal-focus-color, currentColor);\n outline-offset: 4px;\n}\n\n.opal-button:disabled { opacity: 0.4; cursor: not-allowed; }\n\n@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {\n .opal-button:not([data-variant=\"filled\"])::before, .opal-button:not([data-variant=\"filled\"]):hover:not(:disabled)::before {\n background-color: var(--opal-button-fallback-background, #ededed);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .opal-button, .opal-button::before, .opal-button::after { transition: none; }\n .opal-button:active:not(:disabled) { transform: none; }\n}\n\n@media (prefers-reduced-transparency: reduce) {\n .opal-button:not([data-variant=\"filled\"])::before, .opal-button:not([data-variant=\"filled\"]):hover:not(:disabled)::before {\n background: var(--opal-button-fallback-background, #ededed);\n -webkit-backdrop-filter: none;\n backdrop-filter: none;\n }\n}\n\n@media (forced-colors: active) {\n .opal-button[data-variant] { border: 1px solid ButtonText; background: ButtonFace; color: ButtonText; box-shadow: none; }\n .opal-button::before, .opal-button::after { display: none; }\n .opal-button:disabled { color: GrayText; border-color: GrayText; opacity: 1; }\n}\n";
@@ -1,16 +1,43 @@
1
1
  /* Shared material. Public variables can be set once on a theme container. */
2
2
  :where(.opal-glass, .opal-button, .opal-input, .opal-menu-panel, .opal-switch, .opal-slider, .opal-segmented, .opal-textarea, .opal-select) {
3
3
  --_opal-glass-rim: var(--opal-glass-rim, inset 0 0 0 .5px rgb(255 255 255 / 7%), inset .65px 1px .65px rgb(255 255 255 / var(--opal-material-rim, .28)), inset -.4px -.65px .8px rgb(255 255 255 / calc(var(--opal-material-rim, .28) * .43)));
4
+ --_opal-glass-edge: 0 0 0 .5px rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .16)), -.65px 0 0 rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .48)), .65px 0 0 rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .48));
4
5
  --_opal-glass-shadow: var(--opal-glass-shadow, 0 3px 8px rgb(15 34 47 / 12%));
5
6
  }
6
7
 
7
- :where(.opal-input,.opal-textarea,.opal-select) { --opal-material-frost:12px; --opal-material-rim:0.22; }
8
- :where(.opal-menu-panel) { --opal-material-frost:24px; --opal-material-rim:0.3; }
8
+ /* One light material for resting controls: clear white face, fine contour,
9
+ a bright inner lip and a broad, low-contrast shadow. */
10
+ :where([data-theme="light"]) {
11
+ --opal-control-background: linear-gradient(180deg, rgb(255 255 255 / 40%) 0%, rgb(255 255 255 / 8%) 32%, rgb(255 255 255 / 4%) 65%, rgb(255 255 255 / 28%) 100%), rgb(255 255 255 / 32%);
12
+ --opal-control-active-background: rgb(255 255 255 / 60%);
13
+ --opal-control-rim-opacity: 1;
14
+ --opal-button-background: var(--opal-control-background);
15
+ --opal-button-hover-background: var(--opal-control-active-background);
16
+ --opal-button-color: #171717;
17
+ --opal-input-background: var(--opal-control-background);
18
+ --opal-input-focus-background: var(--opal-control-active-background);
19
+ --opal-input-color: #171717;
20
+ --opal-segmented-background: var(--opal-control-background);
21
+ --opal-glass-shadow: 0 5px 12px -4px rgb(0 0 0 / 4%);
22
+ --opal-glass-highlight:
23
+ inset 0 0 0 1px rgb(255 255 255 / 78%),
24
+ inset 0 1px 0 rgb(255 255 255 / 100%),
25
+ inset 0 -1px 0 rgb(255 255 255 / 90%);
26
+ }
27
+
28
+ /* Resolve the contour on each surface, where its material settings live.
29
+ edge=.5 reproduces the established 8%/24% light contour exactly. */
30
+ :where([data-theme="light"]) :where(.opal-glass, .opal-button, .opal-input, .opal-menu-panel, .opal-switch, .opal-slider, .opal-segmented, .opal-textarea, .opal-select) {
31
+ --_opal-glass-rim: var(--opal-glass-rim, var(--_opal-glass-edge), var(--opal-glass-highlight));
32
+ }
33
+
34
+ :where(.opal-input,.opal-textarea,.opal-select) { --opal-material-frost:12px; --opal-material-rim:0.22; --opal-material-edge:0.5; }
35
+ :where(.opal-menu-panel) { --opal-material-frost:24px; --opal-material-rim:0.3; --opal-material-edge:0.5; }
9
36
 
10
37
 
11
38
  .opal-glass { position:relative; isolation:isolate; border-radius:28px; padding:28px; color:var(--opal-glass-color,inherit); }
12
- .opal-glass::before { content:''; position:absolute; inset:0; z-index:-1; border-radius:inherit; pointer-events:none; background:var(--opal-glass-background,rgb(255 255 255 / 22%)); -webkit-backdrop-filter:blur(var(--opal-material-frost,2px)); backdrop-filter:var(--opal-material-filter,blur(var(--opal-material-frost,2px))); box-shadow:var(--_opal-glass-shadow); }
39
+ .opal-glass::before { content:''; position:absolute; inset:0; z-index:-1; border-radius:inherit; pointer-events:none; background:var(--opal-glass-background,rgb(255 255 255 / 22%)); -webkit-backdrop-filter:blur(var(--opal-material-frost,2px)); backdrop-filter:var(--opal-material-filter,blur(var(--opal-material-frost,2px))); box-shadow:var(--_opal-glass-edge),var(--_opal-glass-shadow); }
13
40
  @media(prefers-reduced-transparency:reduce) { .opal-glass::before { background:var(--opal-glass-solid,#f5f5f5); backdrop-filter:none; -webkit-backdrop-filter:none; } .opal-glass { color:var(--opal-glass-solid-color,#202020); } }
14
41
  @media(forced-colors:active) { .opal-glass { border:1px solid CanvasText; color:CanvasText; } .opal-glass::before { background:Canvas; backdrop-filter:none; box-shadow:none; } }
15
42
 
16
- .opal-glass::after { content:''; position:absolute; inset:0; border-radius:inherit; pointer-events:none; box-shadow:var(--opal-glass-rim,inset 0 0 0 .5px rgb(255 255 255 / 30%),inset .65px 1px .65px rgb(255 255 255 / 90%),inset -.4px -.65px .8px rgb(255 255 255 / 45%)); opacity:var(--opal-material-rim,.3); }
43
+ .opal-glass::after { content:''; position:absolute; inset:0; border-radius:inherit; pointer-events:none; box-shadow:var(--opal-glass-rim,var(--opal-glass-highlight,inset 0 0 0 .5px rgb(255 255 255 / 30%),inset .65px 1px .65px rgb(255 255 255 / 90%),inset -.4px -.65px .8px rgb(255 255 255 / 45%))); opacity:var(--opal-material-rim,.3); }
@@ -1 +1 @@
1
- export default ".opal-glass { position:relative; isolation:isolate; border-radius:28px; padding:28px; color:var(--opal-glass-color,inherit); }\n.opal-glass::before { content:''; position:absolute; inset:0; z-index:-1; border-radius:inherit; pointer-events:none; background:var(--opal-glass-background,rgb(255 255 255 / 22%)); -webkit-backdrop-filter:blur(var(--opal-material-frost,2px)); backdrop-filter:var(--opal-material-filter,blur(var(--opal-material-frost,2px))); box-shadow:var(--_opal-glass-shadow); }\n@media(prefers-reduced-transparency:reduce) { .opal-glass::before { background:var(--opal-glass-solid,#f5f5f5); backdrop-filter:none; -webkit-backdrop-filter:none; } .opal-glass { color:var(--opal-glass-solid-color,#202020); } }\n@media(forced-colors:active) { .opal-glass { border:1px solid CanvasText; color:CanvasText; } .opal-glass::before { background:Canvas; backdrop-filter:none; box-shadow:none; } }\n\n.opal-glass::after { content:''; position:absolute; inset:0; border-radius:inherit; pointer-events:none; box-shadow:var(--opal-glass-rim,inset 0 0 0 .5px rgb(255 255 255 / 30%),inset .65px 1px .65px rgb(255 255 255 / 90%),inset -.4px -.65px .8px rgb(255 255 255 / 45%)); opacity:var(--opal-material-rim,.3); }\n";
1
+ export default ".opal-glass { position:relative; isolation:isolate; border-radius:28px; padding:28px; color:var(--opal-glass-color,inherit); }\n.opal-glass::before { content:''; position:absolute; inset:0; z-index:-1; border-radius:inherit; pointer-events:none; background:var(--opal-glass-background,rgb(255 255 255 / 22%)); -webkit-backdrop-filter:blur(var(--opal-material-frost,2px)); backdrop-filter:var(--opal-material-filter,blur(var(--opal-material-frost,2px))); box-shadow:var(--_opal-glass-edge),var(--_opal-glass-shadow); }\n@media(prefers-reduced-transparency:reduce) { .opal-glass::before { background:var(--opal-glass-solid,#f5f5f5); backdrop-filter:none; -webkit-backdrop-filter:none; } .opal-glass { color:var(--opal-glass-solid-color,#202020); } }\n@media(forced-colors:active) { .opal-glass { border:1px solid CanvasText; color:CanvasText; } .opal-glass::before { background:Canvas; backdrop-filter:none; box-shadow:none; } }\n\n.opal-glass::after { content:''; position:absolute; inset:0; border-radius:inherit; pointer-events:none; box-shadow:var(--opal-glass-rim,var(--opal-glass-highlight,inset 0 0 0 .5px rgb(255 255 255 / 30%),inset .65px 1px .65px rgb(255 255 255 / 90%),inset -.4px -.65px .8px rgb(255 255 255 / 45%))); opacity:var(--opal-material-rim,.3); }\n";
@@ -9,10 +9,10 @@ import componentCss from './glass.css.js';
9
9
  const css = `${sharedCss}\n${componentCss}`;
10
10
  const href = stylesheetHref('glass', css);
11
11
  /** A glass surface. Children remain sharp; only the backdrop is refracted. */
12
- export const Glass = forwardRef(function Glass({ bend, dispersion, frost, rim, className, style, children, ...props }, ref) {
12
+ export const Glass = forwardRef(function Glass({ bend, dispersion, frost, rim, edge, className, style, children, ...props }, ref) {
13
13
  const element = useRef(null);
14
14
  useImperativeHandle(ref, () => element.current, []);
15
- const material = resolveMaterial(materials.surface, { bend, dispersion, frost, rim });
15
+ const material = resolveMaterial(materials.surface, { bend, dispersion, frost, rim, edge });
16
16
  const glass = useGlassLens(element, material);
17
17
  return _jsxs(_Fragment, { children: [_jsx("style", { href: href, precedence: "opalui", children: css }), glass.filter, _jsx("div", { ...props, ref: element, className: ['opal-glass', className].filter(Boolean).join(' '), style: { ...glass.surfaceStyle, ...style }, children: children })] });
18
18
  });
@@ -1 +1 @@
1
- {"version":3,"file":"glass.js","sourceRoot":"","sources":["../../../src/components/glass/glass.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,EAAiC,MAAM,OAAO,CAAA;AAC9F,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAC3D,OAAO,EAAE,SAAS,EAAE,eAAe,EAAsB,MAAM,kCAAkC,CAAA;AACjG,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AACzD,OAAO,SAAS,MAAM,gCAAgC,CAAA;AACtD,OAAO,YAAY,MAAM,oBAAoB,CAAA;AAC7C,MAAM,GAAG,GAAG,GAAG,SAAS,KAAK,YAAY,EAAE,CAAA;AAC3C,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;AAEzC,8EAA8E;AAC9E,MAAM,CAAC,MAAM,KAAK,GAAG,UAAU,CAA6B,SAAS,KAAK,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE,GAAG;IACpJ,MAAM,OAAO,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAA;IAC5C,mBAAmB,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAQ,EAAE,EAAE,CAAC,CAAA;IACpD,MAAM,QAAQ,GAAG,eAAe,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAA;IACrF,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;IAC7C,OAAO,8BACL,gBAAO,IAAI,EAAE,IAAI,EAAE,UAAU,EAAC,QAAQ,YAAE,GAAG,GAAS,EACnD,KAAK,CAAC,MAAM,EACb,iBAAS,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,EAAE,GAAG,KAAK,CAAC,YAAY,EAAE,GAAG,KAAK,EAAE,YAAG,QAAQ,GAAO,IACzJ,CAAA;AACL,CAAC,CAAC,CAAA","sourcesContent":["'use client'\n\nimport { forwardRef, useImperativeHandle, useRef, type ComponentPropsWithoutRef } from 'react'\nimport { useGlassLens } from '../../internal/glass/lens.js'\nimport { materials, resolveMaterial, type GlassSettings } from '../../internal/glass/material.js'\nimport { stylesheetHref } from '../../internal/styles.js'\nimport sharedCss from '../../internal/glass/styles.js'\nimport componentCss from './glass.css?inline'\nconst css = `${sharedCss}\\n${componentCss}`\nconst href = stylesheetHref('glass', css)\nexport type GlassProps = ComponentPropsWithoutRef<'div'> & Partial<GlassSettings>\n/** A glass surface. Children remain sharp; only the backdrop is refracted. */\nexport const Glass = forwardRef<HTMLDivElement, GlassProps>(function Glass({ bend, dispersion, frost, rim, className, style, children, ...props }, ref) {\n const element = useRef<HTMLDivElement>(null)\n useImperativeHandle(ref, () => element.current!, [])\n const material = resolveMaterial(materials.surface, { bend, dispersion, frost, rim })\n const glass = useGlassLens(element, material)\n return <>\n <style href={href} precedence=\"opalui\">{css}</style>\n {glass.filter}\n <div {...props} ref={element} className={['opal-glass', className].filter(Boolean).join(' ')} style={{ ...glass.surfaceStyle, ...style }}>{children}</div>\n </>\n})\n"]}
1
+ {"version":3,"file":"glass.js","sourceRoot":"","sources":["../../../src/components/glass/glass.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,EAAiC,MAAM,OAAO,CAAA;AAC9F,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAC3D,OAAO,EAAE,SAAS,EAAE,eAAe,EAAsB,MAAM,kCAAkC,CAAA;AACjG,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AACzD,OAAO,SAAS,MAAM,gCAAgC,CAAA;AACtD,OAAO,YAAY,MAAM,oBAAoB,CAAA;AAC7C,MAAM,GAAG,GAAG,GAAG,SAAS,KAAK,YAAY,EAAE,CAAA;AAC3C,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;AAEzC,8EAA8E;AAC9E,MAAM,CAAC,MAAM,KAAK,GAAG,UAAU,CAA6B,SAAS,KAAK,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE,GAAG;IAC1J,MAAM,OAAO,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAA;IAC5C,mBAAmB,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAQ,EAAE,EAAE,CAAC,CAAA;IACpD,MAAM,QAAQ,GAAG,eAAe,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAA;IAC3F,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;IAC7C,OAAO,8BACL,gBAAO,IAAI,EAAE,IAAI,EAAE,UAAU,EAAC,QAAQ,YAAE,GAAG,GAAS,EACnD,KAAK,CAAC,MAAM,EACb,iBAAS,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,EAAE,GAAG,KAAK,CAAC,YAAY,EAAE,GAAG,KAAK,EAAE,YAAG,QAAQ,GAAO,IACzJ,CAAA;AACL,CAAC,CAAC,CAAA","sourcesContent":["'use client'\n\nimport { forwardRef, useImperativeHandle, useRef, type ComponentPropsWithoutRef } from 'react'\nimport { useGlassLens } from '../../internal/glass/lens.js'\nimport { materials, resolveMaterial, type GlassSettings } from '../../internal/glass/material.js'\nimport { stylesheetHref } from '../../internal/styles.js'\nimport sharedCss from '../../internal/glass/styles.js'\nimport componentCss from './glass.css?inline'\nconst css = `${sharedCss}\\n${componentCss}`\nconst href = stylesheetHref('glass', css)\nexport type GlassProps = ComponentPropsWithoutRef<'div'> & Partial<GlassSettings>\n/** A glass surface. Children remain sharp; only the backdrop is refracted. */\nexport const Glass = forwardRef<HTMLDivElement, GlassProps>(function Glass({ bend, dispersion, frost, rim, edge, className, style, children, ...props }, ref) {\n const element = useRef<HTMLDivElement>(null)\n useImperativeHandle(ref, () => element.current!, [])\n const material = resolveMaterial(materials.surface, { bend, dispersion, frost, rim, edge })\n const glass = useGlassLens(element, material)\n return <>\n <style href={href} precedence=\"opalui\">{css}</style>\n {glass.filter}\n <div {...props} ref={element} className={['opal-glass', className].filter(Boolean).join(' ')} style={{ ...glass.surfaceStyle, ...style }}>{children}</div>\n </>\n})\n"]}
@@ -1,11 +1,38 @@
1
1
  /* Shared material. Public variables can be set once on a theme container. */
2
2
  :where(.opal-glass, .opal-button, .opal-input, .opal-menu-panel, .opal-switch, .opal-slider, .opal-segmented, .opal-textarea, .opal-select) {
3
3
  --_opal-glass-rim: var(--opal-glass-rim, inset 0 0 0 .5px rgb(255 255 255 / 7%), inset .65px 1px .65px rgb(255 255 255 / var(--opal-material-rim, .28)), inset -.4px -.65px .8px rgb(255 255 255 / calc(var(--opal-material-rim, .28) * .43)));
4
+ --_opal-glass-edge: 0 0 0 .5px rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .16)), -.65px 0 0 rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .48)), .65px 0 0 rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .48));
4
5
  --_opal-glass-shadow: var(--opal-glass-shadow, 0 3px 8px rgb(15 34 47 / 12%));
5
6
  }
6
7
 
7
- :where(.opal-input,.opal-textarea,.opal-select) { --opal-material-frost:12px; --opal-material-rim:0.22; }
8
- :where(.opal-menu-panel) { --opal-material-frost:24px; --opal-material-rim:0.3; }
8
+ /* One light material for resting controls: clear white face, fine contour,
9
+ a bright inner lip and a broad, low-contrast shadow. */
10
+ :where([data-theme="light"]) {
11
+ --opal-control-background: linear-gradient(180deg, rgb(255 255 255 / 40%) 0%, rgb(255 255 255 / 8%) 32%, rgb(255 255 255 / 4%) 65%, rgb(255 255 255 / 28%) 100%), rgb(255 255 255 / 32%);
12
+ --opal-control-active-background: rgb(255 255 255 / 60%);
13
+ --opal-control-rim-opacity: 1;
14
+ --opal-button-background: var(--opal-control-background);
15
+ --opal-button-hover-background: var(--opal-control-active-background);
16
+ --opal-button-color: #171717;
17
+ --opal-input-background: var(--opal-control-background);
18
+ --opal-input-focus-background: var(--opal-control-active-background);
19
+ --opal-input-color: #171717;
20
+ --opal-segmented-background: var(--opal-control-background);
21
+ --opal-glass-shadow: 0 5px 12px -4px rgb(0 0 0 / 4%);
22
+ --opal-glass-highlight:
23
+ inset 0 0 0 1px rgb(255 255 255 / 78%),
24
+ inset 0 1px 0 rgb(255 255 255 / 100%),
25
+ inset 0 -1px 0 rgb(255 255 255 / 90%);
26
+ }
27
+
28
+ /* Resolve the contour on each surface, where its material settings live.
29
+ edge=.5 reproduces the established 8%/24% light contour exactly. */
30
+ :where([data-theme="light"]) :where(.opal-glass, .opal-button, .opal-input, .opal-menu-panel, .opal-switch, .opal-slider, .opal-segmented, .opal-textarea, .opal-select) {
31
+ --_opal-glass-rim: var(--opal-glass-rim, var(--_opal-glass-edge), var(--opal-glass-highlight));
32
+ }
33
+
34
+ :where(.opal-input,.opal-textarea,.opal-select) { --opal-material-frost:12px; --opal-material-rim:0.22; --opal-material-edge:0.5; }
35
+ :where(.opal-menu-panel) { --opal-material-frost:24px; --opal-material-rim:0.3; --opal-material-edge:0.5; }
9
36
 
10
37
 
11
38
  .opal-input {
@@ -21,7 +48,7 @@
21
48
  content: ''; position: absolute; inset: 0; border-radius: inherit;
22
49
  pointer-events: none;
23
50
  box-shadow: var(--opal-input-rim, var(--_opal-glass-rim));
24
- opacity: .7;
51
+ opacity: var(--opal-control-rim-opacity, .7);
25
52
  }
26
53
  .opal-input:focus-within {
27
54
  background: var(--opal-input-focus-background, rgb(255 255 255 / 90%));
@@ -1 +1 @@
1
- export default ".opal-input {\n position: relative;\n display: inline-flex; align-items: center; gap: 10px; box-sizing: border-box;\n width: 360px; max-width: 100%; min-height: 44px; padding: 0 15px; border-radius: 999px;\n background: var(--opal-input-background, rgb(255 255 255 / 65%)); color: var(--opal-input-color, #171e22);\n box-shadow: var(--opal-input-shadow, var(--_opal-glass-shadow));\n backdrop-filter: blur(var(--opal-material-frost,12px)); -webkit-backdrop-filter: blur(var(--opal-material-frost,12px));\n transition: background-color 180ms ease, box-shadow 180ms ease;\n}\n.opal-input::after {\n content: ''; position: absolute; inset: 0; border-radius: inherit;\n pointer-events: none;\n box-shadow: var(--opal-input-rim, var(--_opal-glass-rim));\n opacity: .7;\n}\n.opal-input:focus-within {\n background: var(--opal-input-focus-background, rgb(255 255 255 / 90%));\n}\n.opal-input:has([aria-invalid=\"true\"]) { outline: 2px solid #e5484d; outline-offset: 2px; }\n.opal-input-field { width: 100%; min-width: 0; padding: 12px 0; border: 0; outline: 0; background: transparent; color: inherit; font: inherit; line-height: 1.25; }\n.opal-input-field::placeholder { color: inherit; opacity: .5; }\n.opal-input-field::-webkit-search-cancel-button, .opal-input-field::-webkit-search-decoration { -webkit-appearance: none; }\n.opal-input-icon { display: inline-flex; width: 20px; height: 20px; flex: 0 0 20px; opacity: .55; pointer-events: none; }\n.opal-input-icon > svg { width: 100%; height: 100%; }\n.opal-input-clear { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 28px; width: 28px; height: 28px; margin-inline-end: -6px; padding: 4px; border: 0; border-radius: 50%; background: transparent; color: inherit; opacity: .55; cursor: pointer; }\n.opal-input-clear svg { width: 20px; height: 20px; }\n.opal-input-clear:hover { opacity: 1; }\n.opal-input-clear:focus-visible { outline: 2px solid var(--opal-focus-color, #2885ff); outline-offset: 1px; }\n.opal-input[data-disabled] { opacity: .4; cursor: not-allowed; }\n.opal-input-field:disabled { cursor: not-allowed; }\n@media (prefers-reduced-motion: reduce) { .opal-input { transition: none; } }\n@media (prefers-reduced-transparency: reduce) { .opal-input { background: var(--opal-input-solid, #fff); backdrop-filter: none; -webkit-backdrop-filter: none; } }\n@media (forced-colors: active) { .opal-input { border: 1px solid ButtonText; } }\n";
1
+ export default ".opal-input {\n position: relative;\n display: inline-flex; align-items: center; gap: 10px; box-sizing: border-box;\n width: 360px; max-width: 100%; min-height: 44px; padding: 0 15px; border-radius: 999px;\n background: var(--opal-input-background, rgb(255 255 255 / 65%)); color: var(--opal-input-color, #171e22);\n box-shadow: var(--opal-input-shadow, var(--_opal-glass-shadow));\n backdrop-filter: blur(var(--opal-material-frost,12px)); -webkit-backdrop-filter: blur(var(--opal-material-frost,12px));\n transition: background-color 180ms ease, box-shadow 180ms ease;\n}\n.opal-input::after {\n content: ''; position: absolute; inset: 0; border-radius: inherit;\n pointer-events: none;\n box-shadow: var(--opal-input-rim, var(--_opal-glass-rim));\n opacity: var(--opal-control-rim-opacity, .7);\n}\n.opal-input:focus-within {\n background: var(--opal-input-focus-background, rgb(255 255 255 / 90%));\n}\n.opal-input:has([aria-invalid=\"true\"]) { outline: 2px solid #e5484d; outline-offset: 2px; }\n.opal-input-field { width: 100%; min-width: 0; padding: 12px 0; border: 0; outline: 0; background: transparent; color: inherit; font: inherit; line-height: 1.25; }\n.opal-input-field::placeholder { color: inherit; opacity: .5; }\n.opal-input-field::-webkit-search-cancel-button, .opal-input-field::-webkit-search-decoration { -webkit-appearance: none; }\n.opal-input-icon { display: inline-flex; width: 20px; height: 20px; flex: 0 0 20px; opacity: .55; pointer-events: none; }\n.opal-input-icon > svg { width: 100%; height: 100%; }\n.opal-input-clear { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 28px; width: 28px; height: 28px; margin-inline-end: -6px; padding: 4px; border: 0; border-radius: 50%; background: transparent; color: inherit; opacity: .55; cursor: pointer; }\n.opal-input-clear svg { width: 20px; height: 20px; }\n.opal-input-clear:hover { opacity: 1; }\n.opal-input-clear:focus-visible { outline: 2px solid var(--opal-focus-color, #2885ff); outline-offset: 1px; }\n.opal-input[data-disabled] { opacity: .4; cursor: not-allowed; }\n.opal-input-field:disabled { cursor: not-allowed; }\n@media (prefers-reduced-motion: reduce) { .opal-input { transition: none; } }\n@media (prefers-reduced-transparency: reduce) { .opal-input { background: var(--opal-input-solid, #fff); backdrop-filter: none; -webkit-backdrop-filter: none; } }\n@media (forced-colors: active) { .opal-input { border: 1px solid ButtonText; } }\n";
@@ -1,11 +1,38 @@
1
1
  /* Shared material. Public variables can be set once on a theme container. */
2
2
  :where(.opal-glass, .opal-button, .opal-input, .opal-menu-panel, .opal-switch, .opal-slider, .opal-segmented, .opal-textarea, .opal-select) {
3
3
  --_opal-glass-rim: var(--opal-glass-rim, inset 0 0 0 .5px rgb(255 255 255 / 7%), inset .65px 1px .65px rgb(255 255 255 / var(--opal-material-rim, .28)), inset -.4px -.65px .8px rgb(255 255 255 / calc(var(--opal-material-rim, .28) * .43)));
4
+ --_opal-glass-edge: 0 0 0 .5px rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .16)), -.65px 0 0 rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .48)), .65px 0 0 rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .48));
4
5
  --_opal-glass-shadow: var(--opal-glass-shadow, 0 3px 8px rgb(15 34 47 / 12%));
5
6
  }
6
7
 
7
- :where(.opal-input,.opal-textarea,.opal-select) { --opal-material-frost:12px; --opal-material-rim:0.22; }
8
- :where(.opal-menu-panel) { --opal-material-frost:24px; --opal-material-rim:0.3; }
8
+ /* One light material for resting controls: clear white face, fine contour,
9
+ a bright inner lip and a broad, low-contrast shadow. */
10
+ :where([data-theme="light"]) {
11
+ --opal-control-background: linear-gradient(180deg, rgb(255 255 255 / 40%) 0%, rgb(255 255 255 / 8%) 32%, rgb(255 255 255 / 4%) 65%, rgb(255 255 255 / 28%) 100%), rgb(255 255 255 / 32%);
12
+ --opal-control-active-background: rgb(255 255 255 / 60%);
13
+ --opal-control-rim-opacity: 1;
14
+ --opal-button-background: var(--opal-control-background);
15
+ --opal-button-hover-background: var(--opal-control-active-background);
16
+ --opal-button-color: #171717;
17
+ --opal-input-background: var(--opal-control-background);
18
+ --opal-input-focus-background: var(--opal-control-active-background);
19
+ --opal-input-color: #171717;
20
+ --opal-segmented-background: var(--opal-control-background);
21
+ --opal-glass-shadow: 0 5px 12px -4px rgb(0 0 0 / 4%);
22
+ --opal-glass-highlight:
23
+ inset 0 0 0 1px rgb(255 255 255 / 78%),
24
+ inset 0 1px 0 rgb(255 255 255 / 100%),
25
+ inset 0 -1px 0 rgb(255 255 255 / 90%);
26
+ }
27
+
28
+ /* Resolve the contour on each surface, where its material settings live.
29
+ edge=.5 reproduces the established 8%/24% light contour exactly. */
30
+ :where([data-theme="light"]) :where(.opal-glass, .opal-button, .opal-input, .opal-menu-panel, .opal-switch, .opal-slider, .opal-segmented, .opal-textarea, .opal-select) {
31
+ --_opal-glass-rim: var(--opal-glass-rim, var(--_opal-glass-edge), var(--opal-glass-highlight));
32
+ }
33
+
34
+ :where(.opal-input,.opal-textarea,.opal-select) { --opal-material-frost:12px; --opal-material-rim:0.22; --opal-material-edge:0.5; }
35
+ :where(.opal-menu-panel) { --opal-material-frost:24px; --opal-material-rim:0.3; --opal-material-edge:0.5; }
9
36
 
10
37
 
11
38
  .opal-menu { display: inline-flex; vertical-align: middle; }
@@ -1,11 +1,38 @@
1
1
  /* Shared material. Public variables can be set once on a theme container. */
2
2
  :where(.opal-glass, .opal-button, .opal-input, .opal-menu-panel, .opal-switch, .opal-slider, .opal-segmented, .opal-textarea, .opal-select) {
3
3
  --_opal-glass-rim: var(--opal-glass-rim, inset 0 0 0 .5px rgb(255 255 255 / 7%), inset .65px 1px .65px rgb(255 255 255 / var(--opal-material-rim, .28)), inset -.4px -.65px .8px rgb(255 255 255 / calc(var(--opal-material-rim, .28) * .43)));
4
+ --_opal-glass-edge: 0 0 0 .5px rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .16)), -.65px 0 0 rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .48)), .65px 0 0 rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .48));
4
5
  --_opal-glass-shadow: var(--opal-glass-shadow, 0 3px 8px rgb(15 34 47 / 12%));
5
6
  }
6
7
 
7
- :where(.opal-input,.opal-textarea,.opal-select) { --opal-material-frost:12px; --opal-material-rim:0.22; }
8
- :where(.opal-menu-panel) { --opal-material-frost:24px; --opal-material-rim:0.3; }
8
+ /* One light material for resting controls: clear white face, fine contour,
9
+ a bright inner lip and a broad, low-contrast shadow. */
10
+ :where([data-theme="light"]) {
11
+ --opal-control-background: linear-gradient(180deg, rgb(255 255 255 / 40%) 0%, rgb(255 255 255 / 8%) 32%, rgb(255 255 255 / 4%) 65%, rgb(255 255 255 / 28%) 100%), rgb(255 255 255 / 32%);
12
+ --opal-control-active-background: rgb(255 255 255 / 60%);
13
+ --opal-control-rim-opacity: 1;
14
+ --opal-button-background: var(--opal-control-background);
15
+ --opal-button-hover-background: var(--opal-control-active-background);
16
+ --opal-button-color: #171717;
17
+ --opal-input-background: var(--opal-control-background);
18
+ --opal-input-focus-background: var(--opal-control-active-background);
19
+ --opal-input-color: #171717;
20
+ --opal-segmented-background: var(--opal-control-background);
21
+ --opal-glass-shadow: 0 5px 12px -4px rgb(0 0 0 / 4%);
22
+ --opal-glass-highlight:
23
+ inset 0 0 0 1px rgb(255 255 255 / 78%),
24
+ inset 0 1px 0 rgb(255 255 255 / 100%),
25
+ inset 0 -1px 0 rgb(255 255 255 / 90%);
26
+ }
27
+
28
+ /* Resolve the contour on each surface, where its material settings live.
29
+ edge=.5 reproduces the established 8%/24% light contour exactly. */
30
+ :where([data-theme="light"]) :where(.opal-glass, .opal-button, .opal-input, .opal-menu-panel, .opal-switch, .opal-slider, .opal-segmented, .opal-textarea, .opal-select) {
31
+ --_opal-glass-rim: var(--opal-glass-rim, var(--_opal-glass-edge), var(--opal-glass-highlight));
32
+ }
33
+
34
+ :where(.opal-input,.opal-textarea,.opal-select) { --opal-material-frost:12px; --opal-material-rim:0.22; --opal-material-edge:0.5; }
35
+ :where(.opal-menu-panel) { --opal-material-frost:24px; --opal-material-rim:0.3; --opal-material-edge:0.5; }
9
36
 
10
37
 
11
38
  .opal-segmented-filter { position: absolute; pointer-events: none; }
@@ -27,7 +54,7 @@
27
54
  content: ''; position: absolute; inset: 0; border-radius: inherit;
28
55
  pointer-events: none;
29
56
  box-shadow: var(--opal-segmented-rim, var(--_opal-glass-rim));
30
- opacity: .7;
57
+ opacity: var(--opal-control-rim-opacity, .7);
31
58
  }
32
59
  .opal-segmented-indicator {
33
60
  position: absolute;
@@ -1 +1 @@
1
- export default ".opal-segmented-filter { position: absolute; pointer-events: none; }\n.opal-segmented {\n position: relative;\n display: inline-grid;\n grid-template-columns: repeat(var(--opal-segment-count), minmax(0, 1fr));\n box-sizing: border-box;\n padding: 3px;\n border-radius: 999px;\n background: var(--opal-segmented-background, rgb(0 0 0 / 5%));\n color: var(--opal-segmented-color, #202020);\n box-shadow: var(--opal-segmented-shadow, var(--_opal-glass-shadow));\n vertical-align: middle;\n max-width: 100%;\n isolation: isolate;\n}\n.opal-segmented::after {\n content: ''; position: absolute; inset: 0; border-radius: inherit;\n pointer-events: none;\n box-shadow: var(--opal-segmented-rim, var(--_opal-glass-rim));\n opacity: .7;\n}\n.opal-segmented-indicator {\n position: absolute;\n inset-block: 3px;\n inset-inline-start: calc(3px + (100% - 6px) / var(--opal-segment-count) * var(--opal-segment-index));\n width: calc((100% - 6px) / var(--opal-segment-count));\n border-radius: 999px;\n pointer-events: none;\n background: var(--opal-segmented-thumb, #fff);\n box-shadow: 0 1px 3px rgb(0 0 0 / 10%), inset 0 0 0 0.5px rgb(255 255 255 / 18%);\n transition: inset-inline-start 400ms cubic-bezier(0.22, 0.61, 0.36, 1), transform 450ms cubic-bezier(0.22, 0.61, 0.36, 1), background-color 180ms ease, box-shadow 250ms ease;\n}\n.opal-segmented-item {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 0;\n min-height: 36px;\n padding: 7px 24px;\n border: 0;\n border-radius: 999px;\n background: transparent;\n color: inherit;\n font: inherit;\n line-height: 1.25;\n cursor: pointer;\n touch-action: none;\n -webkit-tap-highlight-color: transparent;\n user-select: none;\n -webkit-user-select: none;\n}\n.opal-segmented-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; transition: transform 170ms ease; }\n.opal-segmented-item[aria-checked=\"true\"] { color: var(--opal-segmented-selected-color, inherit); }\n.opal-segmented-item:focus-visible { outline: 2px solid var(--opal-focus-color, #2885ff); outline-offset: 3px; }\n.opal-segmented:is([data-dragging], :has(.opal-segmented-item:active:not(:disabled))) .opal-segmented-indicator {\n transform: translateX(var(--opal-segment-pull, 0px)) scale(calc(1.14 + var(--opal-segment-stretch, 0)), calc(1.45 - var(--opal-segment-stretch, 0) * 0.75));\n background: var(--opal-segmented-lifted, rgb(255 255 255 / 30%));\n z-index: 0;\n -webkit-backdrop-filter: blur(.25px) saturate(110%);\n backdrop-filter: blur(.25px) saturate(110%);\n box-shadow: 0 5px 12px rgb(0 0 0 / 13%), var(--_opal-glass-rim);\n transition-duration: 170ms, 170ms, 140ms, 170ms;\n}\n/* Pointer movement is direct; restoring the normal transition on release\n lets the pill settle onto the nearest enabled segment. */\n.opal-segmented[data-tracking] .opal-segmented-indicator {\n transition-property: transform, background-color, box-shadow;\n transition-duration: 170ms, 140ms, 170ms;\n}\n/* A small overshoot on release gives the resisted pull a soft rebound. */\n.opal-segmented[data-rebounding]:not([data-dragging]) .opal-segmented-indicator {\n transition: inset-inline-start 400ms cubic-bezier(0.22, 0.61, 0.36, 1), transform 600ms cubic-bezier(0.22, 1.5, 0.36, 1), background-color 180ms ease, box-shadow 250ms ease;\n}\n.opal-segmented-item:disabled { opacity: 0.4; cursor: not-allowed; }\n.opal-segmented[data-disabled] { opacity: 0.4; }\n.opal-segmented[data-disabled] .opal-segmented-item { opacity: 1; }\n@media (prefers-reduced-motion: reduce) {\n .opal-segmented .opal-segmented-indicator, .opal-segmented .opal-segmented-label { transition: none !important; transform: none !important; }\n}\n@media (prefers-reduced-transparency: reduce) {\n .opal-segmented { background: var(--opal-segmented-solid-background, #ededed); }\n .opal-segmented .opal-segmented-indicator { z-index: 0; background: var(--opal-segmented-thumb, #fff) !important; backdrop-filter: none !important; -webkit-backdrop-filter: none !important; }\n}\n@media (forced-colors: active) {\n .opal-segmented::after { display: none; }\n .opal-segmented { background: Canvas; color: ButtonText; outline: 1px solid ButtonText; }\n .opal-segmented .opal-segmented-indicator { z-index: 0; background: Highlight !important; box-shadow: none !important; transform: none !important; }\n .opal-segmented-item[aria-checked=\"true\"] { color: HighlightText; }\n .opal-segmented-item:disabled { color: GrayText; }\n}\n\n/* An inert visual layer owns the ink; native buttons retain all semantics. */\n.opal-segmented-optical-labels { position:absolute; inset:-24px; padding:27px; display:grid; grid-template-columns:repeat(var(--opal-segment-count),minmax(0,1fr)); pointer-events:none; visibility:hidden; z-index:1; }\n.opal-segmented-optical-label { display:flex; align-items:center; justify-content:center; min-width:0; white-space:nowrap; }\n.opal-segmented-optical-label[data-selected] { color:var(--opal-segmented-selected-color,inherit); }\n.opal-segmented-optical-label[data-disabled] { opacity:.4; }\n.opal-segmented[data-lensing] .opal-segmented-optical-labels { visibility:visible; }\n.opal-segmented[data-lensing] .opal-segmented-label { opacity:0; }\n";
1
+ export default ".opal-segmented-filter { position: absolute; pointer-events: none; }\n.opal-segmented {\n position: relative;\n display: inline-grid;\n grid-template-columns: repeat(var(--opal-segment-count), minmax(0, 1fr));\n box-sizing: border-box;\n padding: 3px;\n border-radius: 999px;\n background: var(--opal-segmented-background, rgb(0 0 0 / 5%));\n color: var(--opal-segmented-color, #202020);\n box-shadow: var(--opal-segmented-shadow, var(--_opal-glass-shadow));\n vertical-align: middle;\n max-width: 100%;\n isolation: isolate;\n}\n.opal-segmented::after {\n content: ''; position: absolute; inset: 0; border-radius: inherit;\n pointer-events: none;\n box-shadow: var(--opal-segmented-rim, var(--_opal-glass-rim));\n opacity: var(--opal-control-rim-opacity, .7);\n}\n.opal-segmented-indicator {\n position: absolute;\n inset-block: 3px;\n inset-inline-start: calc(3px + (100% - 6px) / var(--opal-segment-count) * var(--opal-segment-index));\n width: calc((100% - 6px) / var(--opal-segment-count));\n border-radius: 999px;\n pointer-events: none;\n background: var(--opal-segmented-thumb, #fff);\n box-shadow: 0 1px 3px rgb(0 0 0 / 10%), inset 0 0 0 0.5px rgb(255 255 255 / 18%);\n transition: inset-inline-start 400ms cubic-bezier(0.22, 0.61, 0.36, 1), transform 450ms cubic-bezier(0.22, 0.61, 0.36, 1), background-color 180ms ease, box-shadow 250ms ease;\n}\n.opal-segmented-item {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 0;\n min-height: 36px;\n padding: 7px 24px;\n border: 0;\n border-radius: 999px;\n background: transparent;\n color: inherit;\n font: inherit;\n line-height: 1.25;\n cursor: pointer;\n touch-action: none;\n -webkit-tap-highlight-color: transparent;\n user-select: none;\n -webkit-user-select: none;\n}\n.opal-segmented-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; transition: transform 170ms ease; }\n.opal-segmented-item[aria-checked=\"true\"] { color: var(--opal-segmented-selected-color, inherit); }\n.opal-segmented-item:focus-visible { outline: 2px solid var(--opal-focus-color, #2885ff); outline-offset: 3px; }\n.opal-segmented:is([data-dragging], :has(.opal-segmented-item:active:not(:disabled))) .opal-segmented-indicator {\n transform: translateX(var(--opal-segment-pull, 0px)) scale(calc(1.14 + var(--opal-segment-stretch, 0)), calc(1.45 - var(--opal-segment-stretch, 0) * 0.75));\n background: var(--opal-segmented-lifted, rgb(255 255 255 / 30%));\n z-index: 0;\n -webkit-backdrop-filter: blur(.25px) saturate(110%);\n backdrop-filter: blur(.25px) saturate(110%);\n box-shadow: 0 5px 12px rgb(0 0 0 / 13%), var(--_opal-glass-rim);\n transition-duration: 170ms, 170ms, 140ms, 170ms;\n}\n/* Pointer movement is direct; restoring the normal transition on release\n lets the pill settle onto the nearest enabled segment. */\n.opal-segmented[data-tracking] .opal-segmented-indicator {\n transition-property: transform, background-color, box-shadow;\n transition-duration: 170ms, 140ms, 170ms;\n}\n/* A small overshoot on release gives the resisted pull a soft rebound. */\n.opal-segmented[data-rebounding]:not([data-dragging]) .opal-segmented-indicator {\n transition: inset-inline-start 400ms cubic-bezier(0.22, 0.61, 0.36, 1), transform 600ms cubic-bezier(0.22, 1.5, 0.36, 1), background-color 180ms ease, box-shadow 250ms ease;\n}\n.opal-segmented-item:disabled { opacity: 0.4; cursor: not-allowed; }\n.opal-segmented[data-disabled] { opacity: 0.4; }\n.opal-segmented[data-disabled] .opal-segmented-item { opacity: 1; }\n@media (prefers-reduced-motion: reduce) {\n .opal-segmented .opal-segmented-indicator, .opal-segmented .opal-segmented-label { transition: none !important; transform: none !important; }\n}\n@media (prefers-reduced-transparency: reduce) {\n .opal-segmented { background: var(--opal-segmented-solid-background, #ededed); }\n .opal-segmented .opal-segmented-indicator { z-index: 0; background: var(--opal-segmented-thumb, #fff) !important; backdrop-filter: none !important; -webkit-backdrop-filter: none !important; }\n}\n@media (forced-colors: active) {\n .opal-segmented::after { display: none; }\n .opal-segmented { background: Canvas; color: ButtonText; outline: 1px solid ButtonText; }\n .opal-segmented .opal-segmented-indicator { z-index: 0; background: Highlight !important; box-shadow: none !important; transform: none !important; }\n .opal-segmented-item[aria-checked=\"true\"] { color: HighlightText; }\n .opal-segmented-item:disabled { color: GrayText; }\n}\n\n/* An inert visual layer owns the ink; native buttons retain all semantics. */\n.opal-segmented-optical-labels { position:absolute; inset:-24px; padding:27px; display:grid; grid-template-columns:repeat(var(--opal-segment-count),minmax(0,1fr)); pointer-events:none; visibility:hidden; z-index:1; }\n.opal-segmented-optical-label { display:flex; align-items:center; justify-content:center; min-width:0; white-space:nowrap; }\n.opal-segmented-optical-label[data-selected] { color:var(--opal-segmented-selected-color,inherit); }\n.opal-segmented-optical-label[data-disabled] { opacity:.4; }\n.opal-segmented[data-lensing] .opal-segmented-optical-labels { visibility:visible; }\n.opal-segmented[data-lensing] .opal-segmented-label { opacity:0; }\n";
@@ -1,11 +1,38 @@
1
1
  /* Shared material. Public variables can be set once on a theme container. */
2
2
  :where(.opal-glass, .opal-button, .opal-input, .opal-menu-panel, .opal-switch, .opal-slider, .opal-segmented, .opal-textarea, .opal-select) {
3
3
  --_opal-glass-rim: var(--opal-glass-rim, inset 0 0 0 .5px rgb(255 255 255 / 7%), inset .65px 1px .65px rgb(255 255 255 / var(--opal-material-rim, .28)), inset -.4px -.65px .8px rgb(255 255 255 / calc(var(--opal-material-rim, .28) * .43)));
4
+ --_opal-glass-edge: 0 0 0 .5px rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .16)), -.65px 0 0 rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .48)), .65px 0 0 rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .48));
4
5
  --_opal-glass-shadow: var(--opal-glass-shadow, 0 3px 8px rgb(15 34 47 / 12%));
5
6
  }
6
7
 
7
- :where(.opal-input,.opal-textarea,.opal-select) { --opal-material-frost:12px; --opal-material-rim:0.22; }
8
- :where(.opal-menu-panel) { --opal-material-frost:24px; --opal-material-rim:0.3; }
8
+ /* One light material for resting controls: clear white face, fine contour,
9
+ a bright inner lip and a broad, low-contrast shadow. */
10
+ :where([data-theme="light"]) {
11
+ --opal-control-background: linear-gradient(180deg, rgb(255 255 255 / 40%) 0%, rgb(255 255 255 / 8%) 32%, rgb(255 255 255 / 4%) 65%, rgb(255 255 255 / 28%) 100%), rgb(255 255 255 / 32%);
12
+ --opal-control-active-background: rgb(255 255 255 / 60%);
13
+ --opal-control-rim-opacity: 1;
14
+ --opal-button-background: var(--opal-control-background);
15
+ --opal-button-hover-background: var(--opal-control-active-background);
16
+ --opal-button-color: #171717;
17
+ --opal-input-background: var(--opal-control-background);
18
+ --opal-input-focus-background: var(--opal-control-active-background);
19
+ --opal-input-color: #171717;
20
+ --opal-segmented-background: var(--opal-control-background);
21
+ --opal-glass-shadow: 0 5px 12px -4px rgb(0 0 0 / 4%);
22
+ --opal-glass-highlight:
23
+ inset 0 0 0 1px rgb(255 255 255 / 78%),
24
+ inset 0 1px 0 rgb(255 255 255 / 100%),
25
+ inset 0 -1px 0 rgb(255 255 255 / 90%);
26
+ }
27
+
28
+ /* Resolve the contour on each surface, where its material settings live.
29
+ edge=.5 reproduces the established 8%/24% light contour exactly. */
30
+ :where([data-theme="light"]) :where(.opal-glass, .opal-button, .opal-input, .opal-menu-panel, .opal-switch, .opal-slider, .opal-segmented, .opal-textarea, .opal-select) {
31
+ --_opal-glass-rim: var(--opal-glass-rim, var(--_opal-glass-edge), var(--opal-glass-highlight));
32
+ }
33
+
34
+ :where(.opal-input,.opal-textarea,.opal-select) { --opal-material-frost:12px; --opal-material-rim:0.22; --opal-material-edge:0.5; }
35
+ :where(.opal-menu-panel) { --opal-material-frost:24px; --opal-material-rim:0.3; --opal-material-edge:0.5; }
9
36
 
10
37
  .opal-menu { display: inline-flex; vertical-align: middle; }
11
38
  .opal-menu-panel {
@@ -93,7 +120,7 @@
93
120
  backdrop-filter: blur(var(--opal-material-frost,12px)); -webkit-backdrop-filter: blur(var(--opal-material-frost,12px));
94
121
  font: inherit; line-height: 1.25; text-align: start; cursor: pointer;
95
122
  }
96
- .opal-select-trigger::after { content: ''; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; box-shadow: var(--opal-input-rim, var(--_opal-glass-rim)); opacity: .7; }
123
+ .opal-select-trigger::after { content: ''; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; box-shadow: var(--opal-input-rim, var(--_opal-glass-rim)); opacity: var(--opal-control-rim-opacity, .7); }
97
124
  .opal-select-value { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
98
125
  .opal-select-value[data-placeholder] { opacity: .5; }
99
126
  .opal-select-chevron, .opal-select-check { width: 18px; height: 18px; flex-shrink: 0; }
@@ -1 +1 @@
1
- export default ".opal-select { position: relative; display: inline-flex; width: 360px; max-width: 100%; }\n.opal-select-native { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }\n.opal-select-trigger {\n position: relative; display: flex; align-items: center; gap: 10px; box-sizing: border-box;\n width: 100%; min-height: 44px; padding: 12px 16px; border: 0; border-radius: 999px;\n background: var(--opal-input-background, rgb(255 255 255 / 65%)); color: var(--opal-input-color, #171e22);\n box-shadow: var(--opal-input-shadow, var(--_opal-glass-shadow));\n backdrop-filter: blur(var(--opal-material-frost,12px)); -webkit-backdrop-filter: blur(var(--opal-material-frost,12px));\n font: inherit; line-height: 1.25; text-align: start; cursor: pointer;\n}\n.opal-select-trigger::after { content: ''; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; box-shadow: var(--opal-input-rim, var(--_opal-glass-rim)); opacity: .7; }\n.opal-select-value { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.opal-select-value[data-placeholder] { opacity: .5; }\n.opal-select-chevron, .opal-select-check { width: 18px; height: 18px; flex-shrink: 0; }\n.opal-select-chevron { opacity: .6; transition: transform 280ms cubic-bezier(.22, 1, .36, 1); }\n.opal-select-trigger[aria-expanded=\"true\"] .opal-select-chevron { transform: rotate(180deg); }\n.opal-select-check { margin-inline-start: auto; }\n.opal-select-trigger:focus-visible { outline: 2px solid var(--opal-focus-color, #2885ff); outline-offset: 3px; }\n.opal-select-trigger[aria-invalid=\"true\"] { outline: 2px solid #e5484d; outline-offset: 2px; }\n.opal-select-trigger:disabled { opacity: .4; cursor: not-allowed; }\n.opal-menu-panel.opal-select-panel {\n max-height: min(320px, calc(100dvh - 16px));\n background: var(--opal-input-background, rgb(255 255 255 / 65%));\n color: var(--opal-input-color, #171e22);\n backdrop-filter: blur(var(--opal-material-frost,12px)); -webkit-backdrop-filter: blur(var(--opal-material-frost,12px));\n opacity: 0;\n transform: translateY(var(--opal-select-enter-y, -6px)) scale(.97);\n pointer-events: none;\n transition: opacity 160ms ease, transform 220ms cubic-bezier(.22, 1, .36, 1), display 220ms allow-discrete, overlay 220ms allow-discrete;\n}\n.opal-menu-panel.opal-select-panel[data-open] {\n opacity: 1;\n transform: translateY(0) scale(1);\n pointer-events: auto;\n transition-duration: 200ms, 360ms, 360ms, 360ms;\n}\n@starting-style {\n .opal-menu-panel.opal-select-panel[data-open] {\n opacity: 0;\n transform: translateY(var(--opal-select-enter-y, -6px)) scale(.97);\n }\n}\n@media (prefers-reduced-motion: reduce) {\n .opal-menu-panel.opal-select-panel, .opal-menu-panel.opal-select-panel[data-open], .opal-select-chevron { transition: none; transform: none; }\n}\n@media (prefers-reduced-transparency: reduce) {\n .opal-select-trigger, .opal-menu-panel.opal-select-panel { background: var(--opal-input-solid, #fff); backdrop-filter: none; -webkit-backdrop-filter: none; }\n}\n@media (forced-colors: active) {\n .opal-select-trigger { border: 1px solid ButtonText; }\n .opal-menu-panel.opal-select-panel { background: Canvas; color: CanvasText; }\n}\n";
1
+ export default ".opal-select { position: relative; display: inline-flex; width: 360px; max-width: 100%; }\n.opal-select-native { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }\n.opal-select-trigger {\n position: relative; display: flex; align-items: center; gap: 10px; box-sizing: border-box;\n width: 100%; min-height: 44px; padding: 12px 16px; border: 0; border-radius: 999px;\n background: var(--opal-input-background, rgb(255 255 255 / 65%)); color: var(--opal-input-color, #171e22);\n box-shadow: var(--opal-input-shadow, var(--_opal-glass-shadow));\n backdrop-filter: blur(var(--opal-material-frost,12px)); -webkit-backdrop-filter: blur(var(--opal-material-frost,12px));\n font: inherit; line-height: 1.25; text-align: start; cursor: pointer;\n}\n.opal-select-trigger::after { content: ''; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; box-shadow: var(--opal-input-rim, var(--_opal-glass-rim)); opacity: var(--opal-control-rim-opacity, .7); }\n.opal-select-value { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.opal-select-value[data-placeholder] { opacity: .5; }\n.opal-select-chevron, .opal-select-check { width: 18px; height: 18px; flex-shrink: 0; }\n.opal-select-chevron { opacity: .6; transition: transform 280ms cubic-bezier(.22, 1, .36, 1); }\n.opal-select-trigger[aria-expanded=\"true\"] .opal-select-chevron { transform: rotate(180deg); }\n.opal-select-check { margin-inline-start: auto; }\n.opal-select-trigger:focus-visible { outline: 2px solid var(--opal-focus-color, #2885ff); outline-offset: 3px; }\n.opal-select-trigger[aria-invalid=\"true\"] { outline: 2px solid #e5484d; outline-offset: 2px; }\n.opal-select-trigger:disabled { opacity: .4; cursor: not-allowed; }\n.opal-menu-panel.opal-select-panel {\n max-height: min(320px, calc(100dvh - 16px));\n background: var(--opal-input-background, rgb(255 255 255 / 65%));\n color: var(--opal-input-color, #171e22);\n backdrop-filter: blur(var(--opal-material-frost,12px)); -webkit-backdrop-filter: blur(var(--opal-material-frost,12px));\n opacity: 0;\n transform: translateY(var(--opal-select-enter-y, -6px)) scale(.97);\n pointer-events: none;\n transition: opacity 160ms ease, transform 220ms cubic-bezier(.22, 1, .36, 1), display 220ms allow-discrete, overlay 220ms allow-discrete;\n}\n.opal-menu-panel.opal-select-panel[data-open] {\n opacity: 1;\n transform: translateY(0) scale(1);\n pointer-events: auto;\n transition-duration: 200ms, 360ms, 360ms, 360ms;\n}\n@starting-style {\n .opal-menu-panel.opal-select-panel[data-open] {\n opacity: 0;\n transform: translateY(var(--opal-select-enter-y, -6px)) scale(.97);\n }\n}\n@media (prefers-reduced-motion: reduce) {\n .opal-menu-panel.opal-select-panel, .opal-menu-panel.opal-select-panel[data-open], .opal-select-chevron { transition: none; transform: none; }\n}\n@media (prefers-reduced-transparency: reduce) {\n .opal-select-trigger, .opal-menu-panel.opal-select-panel { background: var(--opal-input-solid, #fff); backdrop-filter: none; -webkit-backdrop-filter: none; }\n}\n@media (forced-colors: active) {\n .opal-select-trigger { border: 1px solid ButtonText; }\n .opal-menu-panel.opal-select-panel { background: Canvas; color: CanvasText; }\n}\n";
@@ -1,11 +1,38 @@
1
1
  /* Shared material. Public variables can be set once on a theme container. */
2
2
  :where(.opal-glass, .opal-button, .opal-input, .opal-menu-panel, .opal-switch, .opal-slider, .opal-segmented, .opal-textarea, .opal-select) {
3
3
  --_opal-glass-rim: var(--opal-glass-rim, inset 0 0 0 .5px rgb(255 255 255 / 7%), inset .65px 1px .65px rgb(255 255 255 / var(--opal-material-rim, .28)), inset -.4px -.65px .8px rgb(255 255 255 / calc(var(--opal-material-rim, .28) * .43)));
4
+ --_opal-glass-edge: 0 0 0 .5px rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .16)), -.65px 0 0 rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .48)), .65px 0 0 rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .48));
4
5
  --_opal-glass-shadow: var(--opal-glass-shadow, 0 3px 8px rgb(15 34 47 / 12%));
5
6
  }
6
7
 
7
- :where(.opal-input,.opal-textarea,.opal-select) { --opal-material-frost:12px; --opal-material-rim:0.22; }
8
- :where(.opal-menu-panel) { --opal-material-frost:24px; --opal-material-rim:0.3; }
8
+ /* One light material for resting controls: clear white face, fine contour,
9
+ a bright inner lip and a broad, low-contrast shadow. */
10
+ :where([data-theme="light"]) {
11
+ --opal-control-background: linear-gradient(180deg, rgb(255 255 255 / 40%) 0%, rgb(255 255 255 / 8%) 32%, rgb(255 255 255 / 4%) 65%, rgb(255 255 255 / 28%) 100%), rgb(255 255 255 / 32%);
12
+ --opal-control-active-background: rgb(255 255 255 / 60%);
13
+ --opal-control-rim-opacity: 1;
14
+ --opal-button-background: var(--opal-control-background);
15
+ --opal-button-hover-background: var(--opal-control-active-background);
16
+ --opal-button-color: #171717;
17
+ --opal-input-background: var(--opal-control-background);
18
+ --opal-input-focus-background: var(--opal-control-active-background);
19
+ --opal-input-color: #171717;
20
+ --opal-segmented-background: var(--opal-control-background);
21
+ --opal-glass-shadow: 0 5px 12px -4px rgb(0 0 0 / 4%);
22
+ --opal-glass-highlight:
23
+ inset 0 0 0 1px rgb(255 255 255 / 78%),
24
+ inset 0 1px 0 rgb(255 255 255 / 100%),
25
+ inset 0 -1px 0 rgb(255 255 255 / 90%);
26
+ }
27
+
28
+ /* Resolve the contour on each surface, where its material settings live.
29
+ edge=.5 reproduces the established 8%/24% light contour exactly. */
30
+ :where([data-theme="light"]) :where(.opal-glass, .opal-button, .opal-input, .opal-menu-panel, .opal-switch, .opal-slider, .opal-segmented, .opal-textarea, .opal-select) {
31
+ --_opal-glass-rim: var(--opal-glass-rim, var(--_opal-glass-edge), var(--opal-glass-highlight));
32
+ }
33
+
34
+ :where(.opal-input,.opal-textarea,.opal-select) { --opal-material-frost:12px; --opal-material-rim:0.22; --opal-material-edge:0.5; }
35
+ :where(.opal-menu-panel) { --opal-material-frost:24px; --opal-material-rim:0.3; --opal-material-edge:0.5; }
9
36
 
10
37
 
11
38
  .opal-slider-track, .opal-slider-thumb {
@@ -1,11 +1,38 @@
1
1
  /* Shared material. Public variables can be set once on a theme container. */
2
2
  :where(.opal-glass, .opal-button, .opal-input, .opal-menu-panel, .opal-switch, .opal-slider, .opal-segmented, .opal-textarea, .opal-select) {
3
3
  --_opal-glass-rim: var(--opal-glass-rim, inset 0 0 0 .5px rgb(255 255 255 / 7%), inset .65px 1px .65px rgb(255 255 255 / var(--opal-material-rim, .28)), inset -.4px -.65px .8px rgb(255 255 255 / calc(var(--opal-material-rim, .28) * .43)));
4
+ --_opal-glass-edge: 0 0 0 .5px rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .16)), -.65px 0 0 rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .48)), .65px 0 0 rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .48));
4
5
  --_opal-glass-shadow: var(--opal-glass-shadow, 0 3px 8px rgb(15 34 47 / 12%));
5
6
  }
6
7
 
7
- :where(.opal-input,.opal-textarea,.opal-select) { --opal-material-frost:12px; --opal-material-rim:0.22; }
8
- :where(.opal-menu-panel) { --opal-material-frost:24px; --opal-material-rim:0.3; }
8
+ /* One light material for resting controls: clear white face, fine contour,
9
+ a bright inner lip and a broad, low-contrast shadow. */
10
+ :where([data-theme="light"]) {
11
+ --opal-control-background: linear-gradient(180deg, rgb(255 255 255 / 40%) 0%, rgb(255 255 255 / 8%) 32%, rgb(255 255 255 / 4%) 65%, rgb(255 255 255 / 28%) 100%), rgb(255 255 255 / 32%);
12
+ --opal-control-active-background: rgb(255 255 255 / 60%);
13
+ --opal-control-rim-opacity: 1;
14
+ --opal-button-background: var(--opal-control-background);
15
+ --opal-button-hover-background: var(--opal-control-active-background);
16
+ --opal-button-color: #171717;
17
+ --opal-input-background: var(--opal-control-background);
18
+ --opal-input-focus-background: var(--opal-control-active-background);
19
+ --opal-input-color: #171717;
20
+ --opal-segmented-background: var(--opal-control-background);
21
+ --opal-glass-shadow: 0 5px 12px -4px rgb(0 0 0 / 4%);
22
+ --opal-glass-highlight:
23
+ inset 0 0 0 1px rgb(255 255 255 / 78%),
24
+ inset 0 1px 0 rgb(255 255 255 / 100%),
25
+ inset 0 -1px 0 rgb(255 255 255 / 90%);
26
+ }
27
+
28
+ /* Resolve the contour on each surface, where its material settings live.
29
+ edge=.5 reproduces the established 8%/24% light contour exactly. */
30
+ :where([data-theme="light"]) :where(.opal-glass, .opal-button, .opal-input, .opal-menu-panel, .opal-switch, .opal-slider, .opal-segmented, .opal-textarea, .opal-select) {
31
+ --_opal-glass-rim: var(--opal-glass-rim, var(--_opal-glass-edge), var(--opal-glass-highlight));
32
+ }
33
+
34
+ :where(.opal-input,.opal-textarea,.opal-select) { --opal-material-frost:12px; --opal-material-rim:0.22; --opal-material-edge:0.5; }
35
+ :where(.opal-menu-panel) { --opal-material-frost:24px; --opal-material-rim:0.3; --opal-material-edge:0.5; }
9
36
 
10
37
 
11
38
  .opal-switch {
@@ -1,11 +1,38 @@
1
1
  /* Shared material. Public variables can be set once on a theme container. */
2
2
  :where(.opal-glass, .opal-button, .opal-input, .opal-menu-panel, .opal-switch, .opal-slider, .opal-segmented, .opal-textarea, .opal-select) {
3
3
  --_opal-glass-rim: var(--opal-glass-rim, inset 0 0 0 .5px rgb(255 255 255 / 7%), inset .65px 1px .65px rgb(255 255 255 / var(--opal-material-rim, .28)), inset -.4px -.65px .8px rgb(255 255 255 / calc(var(--opal-material-rim, .28) * .43)));
4
+ --_opal-glass-edge: 0 0 0 .5px rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .16)), -.65px 0 0 rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .48)), .65px 0 0 rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .48));
4
5
  --_opal-glass-shadow: var(--opal-glass-shadow, 0 3px 8px rgb(15 34 47 / 12%));
5
6
  }
6
7
 
7
- :where(.opal-input,.opal-textarea,.opal-select) { --opal-material-frost:12px; --opal-material-rim:0.22; }
8
- :where(.opal-menu-panel) { --opal-material-frost:24px; --opal-material-rim:0.3; }
8
+ /* One light material for resting controls: clear white face, fine contour,
9
+ a bright inner lip and a broad, low-contrast shadow. */
10
+ :where([data-theme="light"]) {
11
+ --opal-control-background: linear-gradient(180deg, rgb(255 255 255 / 40%) 0%, rgb(255 255 255 / 8%) 32%, rgb(255 255 255 / 4%) 65%, rgb(255 255 255 / 28%) 100%), rgb(255 255 255 / 32%);
12
+ --opal-control-active-background: rgb(255 255 255 / 60%);
13
+ --opal-control-rim-opacity: 1;
14
+ --opal-button-background: var(--opal-control-background);
15
+ --opal-button-hover-background: var(--opal-control-active-background);
16
+ --opal-button-color: #171717;
17
+ --opal-input-background: var(--opal-control-background);
18
+ --opal-input-focus-background: var(--opal-control-active-background);
19
+ --opal-input-color: #171717;
20
+ --opal-segmented-background: var(--opal-control-background);
21
+ --opal-glass-shadow: 0 5px 12px -4px rgb(0 0 0 / 4%);
22
+ --opal-glass-highlight:
23
+ inset 0 0 0 1px rgb(255 255 255 / 78%),
24
+ inset 0 1px 0 rgb(255 255 255 / 100%),
25
+ inset 0 -1px 0 rgb(255 255 255 / 90%);
26
+ }
27
+
28
+ /* Resolve the contour on each surface, where its material settings live.
29
+ edge=.5 reproduces the established 8%/24% light contour exactly. */
30
+ :where([data-theme="light"]) :where(.opal-glass, .opal-button, .opal-input, .opal-menu-panel, .opal-switch, .opal-slider, .opal-segmented, .opal-textarea, .opal-select) {
31
+ --_opal-glass-rim: var(--opal-glass-rim, var(--_opal-glass-edge), var(--opal-glass-highlight));
32
+ }
33
+
34
+ :where(.opal-input,.opal-textarea,.opal-select) { --opal-material-frost:12px; --opal-material-rim:0.22; --opal-material-edge:0.5; }
35
+ :where(.opal-menu-panel) { --opal-material-frost:24px; --opal-material-rim:0.3; --opal-material-edge:0.5; }
9
36
 
10
37
 
11
38
  .opal-textarea {
@@ -16,7 +43,7 @@
16
43
  backdrop-filter: blur(var(--opal-material-frost,12px)); -webkit-backdrop-filter: blur(var(--opal-material-frost,12px));
17
44
  transition: background-color 180ms ease;
18
45
  }
19
- .opal-textarea::after { content: ''; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; box-shadow: var(--opal-input-rim, var(--_opal-glass-rim)); opacity: .7; }
46
+ .opal-textarea::after { content: ''; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; box-shadow: var(--opal-input-rim, var(--_opal-glass-rim)); opacity: var(--opal-control-rim-opacity, .7); }
20
47
  .opal-textarea:focus-within { background: var(--opal-input-focus-background, rgb(255 255 255 / 90%)); }
21
48
  /* Inset the native resize grip from the outer rounded glass corner. */
22
49
  .opal-textarea-field { display: block; box-sizing: border-box; width: 100%; min-width: 0; min-height: 88px; resize: vertical; padding: 8px 10px; border: 0; border-radius: 14px; outline: none; background: transparent; color: inherit; font: inherit; line-height: 1.5; }
@@ -1 +1 @@
1
- export default ".opal-textarea {\n position: relative; display: inline-flex; box-sizing: border-box;\n width: 360px; max-width: 100%; padding: 6px; border-radius: 20px;\n background: var(--opal-input-background, rgb(255 255 255 / 65%)); color: var(--opal-input-color, #171e22);\n box-shadow: var(--opal-input-shadow, var(--_opal-glass-shadow));\n backdrop-filter: blur(var(--opal-material-frost,12px)); -webkit-backdrop-filter: blur(var(--opal-material-frost,12px));\n transition: background-color 180ms ease;\n}\n.opal-textarea::after { content: ''; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; box-shadow: var(--opal-input-rim, var(--_opal-glass-rim)); opacity: .7; }\n.opal-textarea:focus-within { background: var(--opal-input-focus-background, rgb(255 255 255 / 90%)); }\n/* Inset the native resize grip from the outer rounded glass corner. */\n.opal-textarea-field { display: block; box-sizing: border-box; width: 100%; min-width: 0; min-height: 88px; resize: vertical; padding: 8px 10px; border: 0; border-radius: 14px; outline: none; background: transparent; color: inherit; font: inherit; line-height: 1.5; }\n.opal-textarea-field::placeholder { color: inherit; opacity: .5; }\n.opal-textarea:has([aria-invalid=\"true\"]) { outline: 2px solid #e5484d; outline-offset: 2px; }\n.opal-textarea[data-disabled] { opacity: .4; }\n.opal-textarea-field:disabled { cursor: not-allowed; resize: none; }\n@media (prefers-reduced-motion: reduce) { .opal-textarea { transition: none; } }\n@media (prefers-reduced-transparency: reduce) { .opal-textarea { background: var(--opal-input-solid, #fff); backdrop-filter: none; -webkit-backdrop-filter: none; } }\n@media (forced-colors: active) { .opal-textarea { border: 1px solid ButtonText; } .opal-textarea:focus-within { outline: 2px solid Highlight; } }\n";
1
+ export default ".opal-textarea {\n position: relative; display: inline-flex; box-sizing: border-box;\n width: 360px; max-width: 100%; padding: 6px; border-radius: 20px;\n background: var(--opal-input-background, rgb(255 255 255 / 65%)); color: var(--opal-input-color, #171e22);\n box-shadow: var(--opal-input-shadow, var(--_opal-glass-shadow));\n backdrop-filter: blur(var(--opal-material-frost,12px)); -webkit-backdrop-filter: blur(var(--opal-material-frost,12px));\n transition: background-color 180ms ease;\n}\n.opal-textarea::after { content: ''; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; box-shadow: var(--opal-input-rim, var(--_opal-glass-rim)); opacity: var(--opal-control-rim-opacity, .7); }\n.opal-textarea:focus-within { background: var(--opal-input-focus-background, rgb(255 255 255 / 90%)); }\n/* Inset the native resize grip from the outer rounded glass corner. */\n.opal-textarea-field { display: block; box-sizing: border-box; width: 100%; min-width: 0; min-height: 88px; resize: vertical; padding: 8px 10px; border: 0; border-radius: 14px; outline: none; background: transparent; color: inherit; font: inherit; line-height: 1.5; }\n.opal-textarea-field::placeholder { color: inherit; opacity: .5; }\n.opal-textarea:has([aria-invalid=\"true\"]) { outline: 2px solid #e5484d; outline-offset: 2px; }\n.opal-textarea[data-disabled] { opacity: .4; }\n.opal-textarea-field:disabled { cursor: not-allowed; resize: none; }\n@media (prefers-reduced-motion: reduce) { .opal-textarea { transition: none; } }\n@media (prefers-reduced-transparency: reduce) { .opal-textarea { background: var(--opal-input-solid, #fff); backdrop-filter: none; -webkit-backdrop-filter: none; } }\n@media (forced-colors: active) { .opal-textarea { border: 1px solid ButtonText; } .opal-textarea:focus-within { outline: 2px solid Highlight; } }\n";
@@ -89,7 +89,7 @@ export function useGlassLens(surface, material, options = {}) {
89
89
  return {
90
90
  filter: frame ? _jsx(LensFilter, { id: filterId, frame: frame, material: m, ink: ink, contract: contract }) : null,
91
91
  sourceStyle: frame && source ? { filter: `url("#${filterId}")` } : undefined,
92
- surfaceStyle: { '--opal-material-frost': `${m.frost}px`, '--opal-material-rim': m.rim, ...(frame && !source ? { '--opal-material-filter': `url("#${id}")` } : {}) },
92
+ surfaceStyle: { '--opal-material-frost': `${m.frost}px`, '--opal-material-rim': m.rim, '--opal-material-edge': m.edge, ...(frame && !source ? { '--opal-material-filter': `url("#${id}")` } : {}) },
93
93
  lensing: !!frame,
94
94
  };
95
95
  }
@@ -1 +1 @@
1
- {"version":3,"file":"lens.js","sourceRoot":"","sources":["../../../src/internal/glass/lens.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAsC,MAAM,OAAO,CAAA;AAC9F,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvC,OAAO,EAAE,eAAe,EAAsB,MAAM,eAAe,CAAA;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AAKrD;+EAC+E;AAC/E,MAAM,UAAU,UAAU,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,GAAG,GAAG,KAAK,EAAE,QAAQ,GAAG,KAAK,EAA4F;IAC/K,6EAA6E;IAC7E,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACtF,MAAM,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,KAAK,CAAA;IAC7B,OAAO,cAAK,KAAK,EAAC,GAAG,EAAC,MAAM,EAAC,GAAG,EAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,EAAE,iBAAc,MAAM,EAAC,SAAS,EAAC,OAAO,YAAC,yBAC5H,kBAAQ,EAAE,EAAE,EAAE,EAAE,WAAW,EAAC,gBAAgB,EAAC,cAAc,EAAC,gBAAgB,EAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,GAAG,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,WAAW,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,YAAY,GAAG,CAAC,EAAE,yBAAyB,EAAC,MAAM,aAClN,wBAAe,EAAE,EAAC,eAAe,EAAC,IAAI,EAAC,QAAQ,EAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,+CAA+C,CAAC,CAAC,CAAC,4CAA4C,EAAE,MAAM,EAAC,KAAK,GAAG,EAChL,yBAAgB,EAAE,EAAC,KAAK,EAAC,YAAY,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,EAAC,MAAM,GAAG,EAC3E,kBAAS,IAAI,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,mBAAmB,EAAC,MAAM,EAAC,MAAM,EAAC,KAAK,GAAG,EAClH,kBAAS,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,mBAAmB,EAAC,MAAM,EAAC,MAAM,EAAC,OAAO,GAAG,EACrH,4BAAmB,EAAE,EAAC,MAAM,EAAC,GAAG,EAAC,KAAK,EAAC,KAAK,EAAE,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,EAAE,gBAAgB,EAAC,GAAG,EAAC,gBAAgB,EAAC,GAAG,EAAC,MAAM,EAAC,QAAQ,GAAG,EACzI,4BAAmB,EAAE,EAAC,MAAM,EAAC,GAAG,EAAC,KAAK,EAAC,KAAK,EAAE,QAAQ,EAAE,gBAAgB,EAAC,GAAG,EAAC,gBAAgB,EAAC,GAAG,EAAC,MAAM,EAAC,QAAQ,GAAG,EACpH,4BAAmB,EAAE,EAAC,MAAM,EAAC,GAAG,EAAC,KAAK,EAAC,KAAK,EAAE,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,EAAE,gBAAgB,EAAC,GAAG,EAAC,gBAAgB,EAAC,GAAG,EAAC,MAAM,EAAC,QAAQ,GAAG,EACzI,wBAAe,EAAE,EAAC,QAAQ,EAAC,MAAM,EAAC,4CAA4C,EAAC,MAAM,EAAC,GAAG,GAAG,EAC5F,wBAAe,EAAE,EAAC,QAAQ,EAAC,MAAM,EAAC,4CAA4C,EAAC,MAAM,EAAC,GAAG,GAAG,EAC5F,wBAAe,EAAE,EAAC,QAAQ,EAAC,MAAM,EAAC,4CAA4C,EAAC,MAAM,EAAC,GAAG,GAAG,EAC5F,kBAAS,EAAE,EAAC,GAAG,EAAC,GAAG,EAAC,GAAG,EAAC,IAAI,EAAC,QAAQ,EAAC,MAAM,EAAC,IAAI,GAAG,EACpD,kBAAS,EAAE,EAAC,IAAI,EAAC,GAAG,EAAC,GAAG,EAAC,IAAI,EAAC,QAAQ,EAAC,MAAM,EAAC,KAAK,GAAG,EACtD,wBAAe,EAAE,EAAC,KAAK,EAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,+CAA+C,CAAC,CAAC,CAAC,4CAA4C,EAAE,MAAM,EAAC,SAAS,GAAG,EAC5J,sBAAa,EAAE,EAAC,SAAS,EAAC,GAAG,EAAC,OAAO,EAAC,QAAQ,EAAC,IAAI,EAAC,MAAM,EAAC,QAAQ,GAAG,EACtE,sBAAa,EAAE,EAAC,eAAe,EAAC,GAAG,EAAC,OAAO,EAAC,QAAQ,EAAC,KAAK,EAAC,MAAM,EAAC,SAAS,GAAG,EAC9E,sBAAa,EAAE,EAAC,QAAQ,EAAC,GAAG,EAAC,SAAS,EAAC,QAAQ,EAAC,YAAY,EAAC,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,GAAG,GAAG,IACtE,GACJ,GAAM,CAAA;AACf,CAAC;AAED;;yEAEyE;AACzE,MAAM,UAAU,YAAY,CAAC,OAAsC,EAAE,QAAuB,EAAE,UAA8H,EAAE;IAC5N,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,GAAG,KAAK,EAAE,QAAQ,GAAG,KAAK,EAAE,OAAO,GAAG,IAAI,EAAE,GAAG,OAAO,CAAA;IACxF,MAAM,EAAE,GAAG,cAAc,KAAK,EAAE,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,EAAE,CAAA;IACjE,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,EAAS,CAAA;IAC3C,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAA;IACxB,MAAM,CAAC,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAA;IACnC,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAA;QAC7B,IAAI,CAAC,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC;YAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;YAAC,OAAM;QAAC,CAAC;QACvD,IAAI,MAAM,IAAI,CAAC,sBAAsB,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC;YAC3D,MAAM,CAAC,OAAO,GAAG,CAAC,CAAA;YAClB,QAAQ,CAAC,SAAS,CAAC,CAAA;YACnB,OAAM;QACR,CAAC;QACD,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,2BAA2B,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,iBAAiB,EAAE,cAAc,CAAC,CAAC;YAAE,OAAM;QACnI,MAAM,YAAY,GAAG,UAAU,CAAC,wCAAwC,CAAC,CAAA;QACzE,MAAM,MAAM,GAAG,UAAU,CAAC,kCAAkC,CAAC,CAAA;QAC7D,MAAM,QAAQ,GAAG,UAAU,CAAC,yBAAyB,CAAC,CAAA;QACtD,IAAI,GAAG,GAAG,CAAC,EAAE,QAAQ,GAAG,CAAC,EAAE,OAAO,GAAG,KAAK,CAAA;QAC1C,MAAM,OAAO,GAAG,CAAC,GAAW,EAAE,EAAE;YAC9B,IAAI,OAAO;gBAAE,OAAM;YACnB,MAAM,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;YACvD,QAAQ,GAAG,GAAG,CAAA;YACd,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;YAC3E,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAA;YAC3H,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,GAAG,IAAI;gBAAE,MAAM,CAAC,OAAO,GAAG,MAAM,CAAA;YACrE,IAAI,CAAC,MAAM,CAAC,OAAO;gBAAE,QAAQ,CAAC,SAAS,CAAC,CAAA;iBACnC,CAAC;gBACJ,MAAM,IAAI,GAAG,KAAK,CAAC,qBAAqB,EAAE,CAAA;gBAC1C,MAAM,OAAO,GAAG,MAAM,EAAE,OAAO,CAAA;gBAC/B,MAAM,MAAM,GAAG,OAAO,EAAE,qBAAqB,EAAE,CAAA;gBAC/C,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAA;gBACtD,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAA;gBACzD,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAA;gBAC/D,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,EAAE,MAAM,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,CAAA;gBACtH,IAAI,CAAC;oBACH,MAAM,GAAG,GAAG,KAAK,IAAI,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;oBACjH,IAAI,GAAG,EAAE,CAAC;wBACR,MAAM,IAAI,GAAG,sBAAsB,kBAAkB,CAAC,kDAAkD,KAAK,aAAa,MAAM,0CAA0C,MAAM,wBAAwB,CAAC,EAAE,CAAA;wBAC3M,MAAM,IAAI,GAAU,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,IAAI,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,IAAI,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,YAAY,CAAC,YAAY,CAAC,KAAK,MAAM,IAAI,gBAAgB,CAAC,KAAK,CAAC,CAAC,WAAW,KAAK,MAAM,EAAE,CAAA;wBAC1X,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,IAAI,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,IAAI,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,IAAI,GAAG,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,IAAI,GAAG,CAAC,WAAW,KAAK,IAAI,CAAC,WAAW,IAAI,GAAG,CAAC,YAAY,KAAK,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;oBACjR,CAAC;gBACH,CAAC;gBAAC,MAAM,CAAC;oBAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;gBAAC,CAAC;YACjC,CAAC;YACD,IAAI,MAAM,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,YAAY,CAAC,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC;gBAAE,GAAG,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAA;QACpI,CAAC,CAAA;QACD,MAAM,OAAO,GAAG,GAAG,EAAE,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAA,CAAC,CAAC,CAAA;QACvG,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC,OAAO,CAAC,CAAA;QAC1C,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;QACrB,IAAI,MAAM,EAAE,OAAO;YAAE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;QACnD,MAAM,KAAK,GAAG,IAAI,gBAAgB,CAAC,OAAO,CAAC,CAAA;QAC3C,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,eAAe,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC,YAAY,CAAC,EAAE,CAAC,CAAA;QAC7G,YAAY,CAAC,gBAAgB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;QAChD,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;QAC1C,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;QAC5C,OAAO,EAAE,CAAA;QACT,OAAO,GAAG,EAAE,GAAG,OAAO,GAAG,IAAI,CAAC,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,YAAY,CAAC,mBAAmB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,mBAAmB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA,CAAC,CAAC,CAAA;IAC1P,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;IAC5E,MAAM,QAAQ,GAAG,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;IACzK,OAAO;QACL,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,KAAC,UAAU,IAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,GAAI,CAAC,CAAC,CAAC,IAAI;QAC5G,WAAW,EAAE,KAAK,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,QAAQ,IAAI,EAAmB,CAAC,CAAC,CAAC,SAAS;QAC7F,YAAY,EAAE,EAAE,uBAAuB,EAAE,GAAG,CAAC,CAAC,KAAK,IAAI,EAAE,qBAAqB,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,wBAAwB,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAmB;QACpL,OAAO,EAAE,CAAC,CAAC,KAAK;KACjB,CAAA;AACH,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,YAAY,CAAC,IAAmC;IAC9D,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IACvC,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAA;QAC5B,IAAI,CAAC,OAAO;YAAE,OAAM;QACpB,MAAM,IAAI,GAAG,CAAC,KAAmB,EAAE,EAAE;YACnC,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,CAAE,KAAK,CAAC,MAA2B,CAAC,QAAQ;gBAAE,OAAO,CAAC,IAAI,CAAC,CAAA;QAC1G,CAAC,CAAA;QACD,MAAM,EAAE,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;QAC/B,OAAO,CAAC,gBAAgB,CAAC,aAAa,EAAE,IAAI,CAAC,CAAA;QAC7C,MAAM,CAAC,gBAAgB,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;QACxC,MAAM,CAAC,gBAAgB,CAAC,eAAe,EAAE,EAAE,CAAC,CAAA;QAC5C,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;QACnC,OAAO,GAAG,EAAE,GAAG,OAAO,CAAC,mBAAmB,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,mBAAmB,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,mBAAmB,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,mBAAmB,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA,CAAC,CAAC,CAAA;IACzM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAA;IACV,OAAO,IAAI,CAAA;AACb,CAAC","sourcesContent":["'use client'\n\nimport { useEffect, useId, useRef, useState, type CSSProperties, type RefObject } from 'react'\nimport { glassLensMap } from './map.js'\nimport { resolveMaterial, type GlassSettings } from './material.js'\nimport { supportsOwnedGlassLens } from './support.js'\n\ntype Box = { x: number; y: number; width: number; height: number; radius: number; map: string; mask: string }\ntype Frame = Box & { amount: number; sourceWidth: number; sourceHeight: number; light: boolean }\n\n/** A single filter graph serves live backgrounds and content owned by a control.\n * Content is composited back outside the lens, so neighbors stay untouched. */\nexport function LensFilter({ id, frame: f, material: m, ink = false, contract = false }: { id: string; frame: Frame; material: GlassSettings; ink?: boolean; contract?: boolean }) {\n // Reversing the sampling direction compresses owned content inside the lens.\n const strength = Math.min(f.width, f.height) * m.bend * f.amount * (contract ? -1 : 1)\n const invert = ink && f.light\n return <svg width=\"0\" height=\"0\" style={{ position: 'absolute', pointerEvents: 'none' }} aria-hidden=\"true\" focusable=\"false\"><defs>\n <filter id={id} filterUnits=\"userSpaceOnUse\" primitiveUnits=\"userSpaceOnUse\" x={-f.sourceWidth * .5} y={-f.sourceHeight * .5} width={f.sourceWidth * 2} height={f.sourceHeight * 2} colorInterpolationFilters=\"sRGB\">\n <feColorMatrix in=\"SourceGraphic\" type=\"matrix\" values={invert ? '-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0' : '1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0'} result=\"ink\" />\n <feGaussianBlur in=\"ink\" stdDeviation={m.frost * f.amount} result=\"soft\" />\n <feImage href={f.map} x={f.x} y={f.y} width={f.width} height={f.height} preserveAspectRatio=\"none\" result=\"map\" />\n <feImage href={f.mask} x={f.x} y={f.y} width={f.width} height={f.height} preserveAspectRatio=\"none\" result=\"shape\" />\n <feDisplacementMap in=\"soft\" in2=\"map\" scale={strength * (1 - m.dispersion)} xChannelSelector=\"R\" yChannelSelector=\"G\" result=\"rays-r\" />\n <feDisplacementMap in=\"soft\" in2=\"map\" scale={strength} xChannelSelector=\"R\" yChannelSelector=\"G\" result=\"rays-g\" />\n <feDisplacementMap in=\"soft\" in2=\"map\" scale={strength * (1 + m.dispersion)} xChannelSelector=\"R\" yChannelSelector=\"G\" result=\"rays-b\" />\n <feColorMatrix in=\"rays-r\" values=\"1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\" result=\"r\" />\n <feColorMatrix in=\"rays-g\" values=\"0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0\" result=\"g\" />\n <feColorMatrix in=\"rays-b\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0\" result=\"b\" />\n <feBlend in=\"r\" in2=\"g\" mode=\"screen\" result=\"rg\" />\n <feBlend in=\"rg\" in2=\"b\" mode=\"screen\" result=\"rgb\" />\n <feColorMatrix in=\"rgb\" values={invert ? '-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0' : '1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0'} result=\"colored\" />\n <feComposite in=\"colored\" in2=\"shape\" operator=\"in\" result=\"inside\" />\n <feComposite in=\"SourceGraphic\" in2=\"shape\" operator=\"out\" result=\"outside\" />\n <feComposite in=\"inside\" in2=\"outside\" operator=\"arithmetic\" k2=\"1\" k3=\"1\" />\n </filter>\n </defs></svg>\n}\n\n/** Lifecycle, measurements, preferences and progressive enhancement live here.\n * Owned content uses ordinary SVG filters; arbitrary backdrops use Chromium's\n * reference-filter support. No DOM or canvas work happens during SSR. */\nexport function useGlassLens(surface: RefObject<HTMLElement | null>, material: GlassSettings, options: { source?: RefObject<HTMLElement | null>; active?: boolean; ink?: boolean; contract?: boolean; enabled?: boolean } = {}) {\n const { source, active = true, ink = false, contract = false, enabled = true } = options\n const id = `opal-glass-${useId().replace(/[^a-zA-Z0-9_-]/g, '')}`\n const [frame, setFrame] = useState<Frame>()\n const amount = useRef(0)\n const m = resolveMaterial(material)\n useEffect(() => {\n const glass = surface.current\n if (!glass || !enabled) { setFrame(undefined); return }\n if (source && !supportsOwnedGlassLens(navigator.userAgent)) {\n amount.current = 0\n setFrame(undefined)\n return\n }\n if (!source && (!/(?:Chrome|Chromium|Edg)\\//.test(navigator.userAgent) || !CSS.supports('backdrop-filter', 'url(\"#lens\")'))) return\n const transparency = matchMedia('(prefers-reduced-transparency: reduce)')\n const motion = matchMedia('(prefers-reduced-motion: reduce)')\n const contrast = matchMedia('(forced-colors: active)')\n let raf = 0, previous = 0, stopped = false\n const measure = (now: number) => {\n if (stopped) return\n const dt = previous ? Math.min(64, now - previous) : 16\n previous = now\n const target = active && !transparency.matches && !contrast.matches ? 1 : 0\n amount.current = motion.matches || !source ? target : amount.current + (target - amount.current) * (1 - Math.exp(-dt / 65))\n if (Math.abs(amount.current - target) < .002) amount.current = target\n if (!amount.current) setFrame(undefined)\n else {\n const rect = glass.getBoundingClientRect()\n const content = source?.current\n const parent = content?.getBoundingClientRect()\n const width = content ? rect.width : glass.offsetWidth\n const height = content ? rect.height : glass.offsetHeight\n const scale = glass.offsetWidth ? width / glass.offsetWidth : 1\n const radius = Math.min(width / 2, height / 2, (parseFloat(getComputedStyle(glass).borderTopLeftRadius) || 0) * scale)\n try {\n const map = width && height ? glassLensMap(Math.round(width), Math.round(height), Math.round(radius)) : undefined\n if (map) {\n const mask = `data:image/svg+xml,${encodeURIComponent(`<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${width}\" height=\"${height}\"><rect width=\"100%\" height=\"100%\" rx=\"${radius}\" fill=\"white\"/></svg>`)}`\n const next: Frame = { x: parent ? rect.left - parent.left : 0, y: parent ? rect.top - parent.top : 0, width, height, radius, map, mask, amount: amount.current, sourceWidth: content?.offsetWidth ?? width, sourceHeight: content?.offsetHeight ?? height, light: glass.closest('[data-theme]')?.getAttribute('data-theme') !== 'dark' && getComputedStyle(glass).colorScheme !== 'dark' }\n setFrame(old => old && old.x === next.x && old.y === next.y && old.width === next.width && old.height === next.height && old.amount === next.amount && old.light === next.light && old.sourceWidth === next.sourceWidth && old.sourceHeight === next.sourceHeight ? old : next)\n }\n } catch { setFrame(undefined) }\n }\n if (source && ((active && !transparency.matches && !contrast.matches) || amount.current > 0)) raf = requestAnimationFrame(measure)\n }\n const refresh = () => { cancelAnimationFrame(raf); previous = 0; raf = requestAnimationFrame(measure) }\n const resize = new ResizeObserver(refresh)\n resize.observe(glass)\n if (source?.current) resize.observe(source.current)\n const theme = new MutationObserver(refresh)\n theme.observe(document.documentElement, { attributes: true, subtree: true, attributeFilter: ['data-theme'] })\n transparency.addEventListener('change', refresh)\n motion.addEventListener('change', refresh)\n contrast.addEventListener('change', refresh)\n refresh()\n return () => { stopped = true; cancelAnimationFrame(raf); resize.disconnect(); theme.disconnect(); transparency.removeEventListener('change', refresh); motion.removeEventListener('change', refresh); contrast.removeEventListener('change', refresh) }\n }, [surface, source, active, enabled, m.bend, m.dispersion, m.frost, m.rim])\n const filterId = source && frame ? `${id}-${Math.round(frame.x * 10)}-${Math.round(frame.y * 10)}-${Math.round(frame.width * 10)}-${Math.round(frame.amount * 100)}` : id\n return {\n filter: frame ? <LensFilter id={filterId} frame={frame} material={m} ink={ink} contract={contract} /> : null,\n sourceStyle: frame && source ? { filter: `url(\"#${filterId}\")` } as CSSProperties : undefined,\n surfaceStyle: { '--opal-material-frost': `${m.frost}px`, '--opal-material-rim': m.rim, ...(frame && !source ? { '--opal-material-filter': `url(\"#${id}\")` } : {}) } as CSSProperties,\n lensing: !!frame,\n }\n}\n\n/** Observe native presses without replacing the control's input handlers. */\nexport function useGlassHeld(root: RefObject<HTMLElement | null>) {\n const [held, setHeld] = useState(false)\n useEffect(() => {\n const element = root.current\n if (!element) return\n const down = (event: PointerEvent) => {\n if (event.isPrimary && event.button === 0 && !(event.target as HTMLInputElement).disabled) setHeld(true)\n }\n const up = () => setHeld(false)\n element.addEventListener('pointerdown', down)\n window.addEventListener('pointerup', up)\n window.addEventListener('pointercancel', up)\n window.addEventListener('blur', up)\n return () => { element.removeEventListener('pointerdown', down); window.removeEventListener('pointerup', up); window.removeEventListener('pointercancel', up); window.removeEventListener('blur', up) }\n }, [root])\n return held\n}\n"]}
1
+ {"version":3,"file":"lens.js","sourceRoot":"","sources":["../../../src/internal/glass/lens.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport { useEffect, useId, useRef, useState, type CSSProperties, type RefObject } from 'react'\nimport { glassLensMap } from './map.js'\nimport { resolveMaterial, type GlassSettings } from './material.js'\nimport { supportsOwnedGlassLens } from './support.js'\n\ntype Box = { x: number; y: number; width: number; height: number; radius: number; map: string; mask: string }\ntype Frame = Box & { amount: number; sourceWidth: number; sourceHeight: number; light: boolean }\n\n/** A single filter graph serves live backgrounds and content owned by a control.\n * Content is composited back outside the lens, so neighbors stay untouched. */\nexport function LensFilter({ id, frame: f, material: m, ink = false, contract = false }: { id: string; frame: Frame; material: GlassSettings; ink?: boolean; contract?: boolean }) {\n // Reversing the sampling direction compresses owned content inside the lens.\n const strength = Math.min(f.width, f.height) * m.bend * f.amount * (contract ? -1 : 1)\n const invert = ink && f.light\n return <svg width=\"0\" height=\"0\" style={{ position: 'absolute', pointerEvents: 'none' }} aria-hidden=\"true\" focusable=\"false\"><defs>\n <filter id={id} filterUnits=\"userSpaceOnUse\" primitiveUnits=\"userSpaceOnUse\" x={-f.sourceWidth * .5} y={-f.sourceHeight * .5} width={f.sourceWidth * 2} height={f.sourceHeight * 2} colorInterpolationFilters=\"sRGB\">\n <feColorMatrix in=\"SourceGraphic\" type=\"matrix\" values={invert ? '-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0' : '1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0'} result=\"ink\" />\n <feGaussianBlur in=\"ink\" stdDeviation={m.frost * f.amount} result=\"soft\" />\n <feImage href={f.map} x={f.x} y={f.y} width={f.width} height={f.height} preserveAspectRatio=\"none\" result=\"map\" />\n <feImage href={f.mask} x={f.x} y={f.y} width={f.width} height={f.height} preserveAspectRatio=\"none\" result=\"shape\" />\n <feDisplacementMap in=\"soft\" in2=\"map\" scale={strength * (1 - m.dispersion)} xChannelSelector=\"R\" yChannelSelector=\"G\" result=\"rays-r\" />\n <feDisplacementMap in=\"soft\" in2=\"map\" scale={strength} xChannelSelector=\"R\" yChannelSelector=\"G\" result=\"rays-g\" />\n <feDisplacementMap in=\"soft\" in2=\"map\" scale={strength * (1 + m.dispersion)} xChannelSelector=\"R\" yChannelSelector=\"G\" result=\"rays-b\" />\n <feColorMatrix in=\"rays-r\" values=\"1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\" result=\"r\" />\n <feColorMatrix in=\"rays-g\" values=\"0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0\" result=\"g\" />\n <feColorMatrix in=\"rays-b\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0\" result=\"b\" />\n <feBlend in=\"r\" in2=\"g\" mode=\"screen\" result=\"rg\" />\n <feBlend in=\"rg\" in2=\"b\" mode=\"screen\" result=\"rgb\" />\n <feColorMatrix in=\"rgb\" values={invert ? '-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0' : '1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0'} result=\"colored\" />\n <feComposite in=\"colored\" in2=\"shape\" operator=\"in\" result=\"inside\" />\n <feComposite in=\"SourceGraphic\" in2=\"shape\" operator=\"out\" result=\"outside\" />\n <feComposite in=\"inside\" in2=\"outside\" operator=\"arithmetic\" k2=\"1\" k3=\"1\" />\n </filter>\n </defs></svg>\n}\n\n/** Lifecycle, measurements, preferences and progressive enhancement live here.\n * Owned content uses ordinary SVG filters; arbitrary backdrops use Chromium's\n * reference-filter support. No DOM or canvas work happens during SSR. */\nexport function useGlassLens(surface: RefObject<HTMLElement | null>, material: GlassSettings, options: { source?: RefObject<HTMLElement | null>; active?: boolean; ink?: boolean; contract?: boolean; enabled?: boolean } = {}) {\n const { source, active = true, ink = false, contract = false, enabled = true } = options\n const id = `opal-glass-${useId().replace(/[^a-zA-Z0-9_-]/g, '')}`\n const [frame, setFrame] = useState<Frame>()\n const amount = useRef(0)\n const m = resolveMaterial(material)\n useEffect(() => {\n const glass = surface.current\n if (!glass || !enabled) { setFrame(undefined); return }\n if (source && !supportsOwnedGlassLens(navigator.userAgent)) {\n amount.current = 0\n setFrame(undefined)\n return\n }\n if (!source && (!/(?:Chrome|Chromium|Edg)\\//.test(navigator.userAgent) || !CSS.supports('backdrop-filter', 'url(\"#lens\")'))) return\n const transparency = matchMedia('(prefers-reduced-transparency: reduce)')\n const motion = matchMedia('(prefers-reduced-motion: reduce)')\n const contrast = matchMedia('(forced-colors: active)')\n let raf = 0, previous = 0, stopped = false\n const measure = (now: number) => {\n if (stopped) return\n const dt = previous ? Math.min(64, now - previous) : 16\n previous = now\n const target = active && !transparency.matches && !contrast.matches ? 1 : 0\n amount.current = motion.matches || !source ? target : amount.current + (target - amount.current) * (1 - Math.exp(-dt / 65))\n if (Math.abs(amount.current - target) < .002) amount.current = target\n if (!amount.current) setFrame(undefined)\n else {\n const rect = glass.getBoundingClientRect()\n const content = source?.current\n const parent = content?.getBoundingClientRect()\n const width = content ? rect.width : glass.offsetWidth\n const height = content ? rect.height : glass.offsetHeight\n const scale = glass.offsetWidth ? width / glass.offsetWidth : 1\n const radius = Math.min(width / 2, height / 2, (parseFloat(getComputedStyle(glass).borderTopLeftRadius) || 0) * scale)\n try {\n const map = width && height ? glassLensMap(Math.round(width), Math.round(height), Math.round(radius)) : undefined\n if (map) {\n const mask = `data:image/svg+xml,${encodeURIComponent(`<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${width}\" height=\"${height}\"><rect width=\"100%\" height=\"100%\" rx=\"${radius}\" fill=\"white\"/></svg>`)}`\n const next: Frame = { x: parent ? rect.left - parent.left : 0, y: parent ? rect.top - parent.top : 0, width, height, radius, map, mask, amount: amount.current, sourceWidth: content?.offsetWidth ?? width, sourceHeight: content?.offsetHeight ?? height, light: glass.closest('[data-theme]')?.getAttribute('data-theme') !== 'dark' && getComputedStyle(glass).colorScheme !== 'dark' }\n setFrame(old => old && old.x === next.x && old.y === next.y && old.width === next.width && old.height === next.height && old.amount === next.amount && old.light === next.light && old.sourceWidth === next.sourceWidth && old.sourceHeight === next.sourceHeight ? old : next)\n }\n } catch { setFrame(undefined) }\n }\n if (source && ((active && !transparency.matches && !contrast.matches) || amount.current > 0)) raf = requestAnimationFrame(measure)\n }\n const refresh = () => { cancelAnimationFrame(raf); previous = 0; raf = requestAnimationFrame(measure) }\n const resize = new ResizeObserver(refresh)\n resize.observe(glass)\n if (source?.current) resize.observe(source.current)\n const theme = new MutationObserver(refresh)\n theme.observe(document.documentElement, { attributes: true, subtree: true, attributeFilter: ['data-theme'] })\n transparency.addEventListener('change', refresh)\n motion.addEventListener('change', refresh)\n contrast.addEventListener('change', refresh)\n refresh()\n return () => { stopped = true; cancelAnimationFrame(raf); resize.disconnect(); theme.disconnect(); transparency.removeEventListener('change', refresh); motion.removeEventListener('change', refresh); contrast.removeEventListener('change', refresh) }\n }, [surface, source, active, enabled, m.bend, m.dispersion, m.frost, m.rim])\n const filterId = source && frame ? `${id}-${Math.round(frame.x * 10)}-${Math.round(frame.y * 10)}-${Math.round(frame.width * 10)}-${Math.round(frame.amount * 100)}` : id\n return {\n filter: frame ? <LensFilter id={filterId} frame={frame} material={m} ink={ink} contract={contract} /> : null,\n sourceStyle: frame && source ? { filter: `url(\"#${filterId}\")` } as CSSProperties : undefined,\n surfaceStyle: { '--opal-material-frost': `${m.frost}px`, '--opal-material-rim': m.rim, '--opal-material-edge': m.edge, ...(frame && !source ? { '--opal-material-filter': `url(\"#${id}\")` } : {}) } as CSSProperties,\n lensing: !!frame,\n }\n}\n\n/** Observe native presses without replacing the control's input handlers. */\nexport function useGlassHeld(root: RefObject<HTMLElement | null>) {\n const [held, setHeld] = useState(false)\n useEffect(() => {\n const element = root.current\n if (!element) return\n const down = (event: PointerEvent) => {\n if (event.isPrimary && event.button === 0 && !(event.target as HTMLInputElement).disabled) setHeld(true)\n }\n const up = () => setHeld(false)\n element.addEventListener('pointerdown', down)\n window.addEventListener('pointerup', up)\n window.addEventListener('pointercancel', up)\n window.addEventListener('blur', up)\n return () => { element.removeEventListener('pointerdown', down); window.removeEventListener('pointerup', up); window.removeEventListener('pointercancel', up); window.removeEventListener('blur', up) }\n }, [root])\n return held\n}\n"]}
@@ -1,10 +1,11 @@
1
1
  /** OpalUI's material vocabulary. Bend is relative to the lens's shorter side;
2
- * dispersion is the proportional RGB spread, frost is CSS pixels, rim is 0–1. */
2
+ * dispersion is the proportional RGB spread, frost is CSS pixels, rim and edge are 0–1. */
3
3
  export type GlassSettings = {
4
4
  bend: number;
5
5
  dispersion: number;
6
6
  frost: number;
7
7
  rim: number;
8
+ edge: number;
8
9
  };
9
10
  export declare const materials: {
10
11
  surface: {
@@ -12,36 +13,42 @@ export declare const materials: {
12
13
  dispersion: number;
13
14
  frost: number;
14
15
  rim: number;
16
+ edge: number;
15
17
  };
16
18
  field: {
17
19
  bend: number;
18
20
  dispersion: number;
19
21
  frost: number;
20
22
  rim: number;
23
+ edge: number;
21
24
  };
22
25
  menu: {
23
26
  bend: number;
24
27
  dispersion: number;
25
28
  frost: number;
26
29
  rim: number;
30
+ edge: number;
27
31
  };
28
32
  thumb: {
29
33
  bend: number;
30
34
  dispersion: number;
31
35
  frost: number;
32
36
  rim: number;
37
+ edge: number;
33
38
  };
34
39
  switch: {
35
40
  bend: number;
36
41
  dispersion: number;
37
42
  frost: number;
38
43
  rim: number;
44
+ edge: number;
39
45
  };
40
46
  selection: {
41
47
  bend: number;
42
48
  dispersion: number;
43
49
  frost: number;
44
50
  rim: number;
51
+ edge: number;
45
52
  };
46
53
  };
47
54
  export declare function resolveMaterial(base: GlassSettings, overrides?: Partial<GlassSettings>): GlassSettings;
@@ -1,22 +1,22 @@
1
1
  export const materials = {
2
- surface: { bend: .22, dispersion: .12, frost: 2, rim: .3 },
3
- field: { bend: 0, dispersion: 0, frost: 12, rim: .22 },
4
- menu: { bend: 0, dispersion: 0, frost: 24, rim: .3 },
5
- thumb: { bend: .25, dispersion: .2, frost: .15, rim: .4 },
6
- switch: { bend: .45, dispersion: .04, frost: .25, rim: .65 },
7
- selection: { bend: .3, dispersion: .16, frost: 0, rim: .4 },
2
+ surface: { bend: .22, dispersion: .12, frost: 2, rim: .3, edge: .5 },
3
+ field: { bend: 0, dispersion: 0, frost: 12, rim: .22, edge: .5 },
4
+ menu: { bend: 0, dispersion: 0, frost: 24, rim: .3, edge: .5 },
5
+ thumb: { bend: .25, dispersion: .2, frost: .15, rim: .4, edge: .5 },
6
+ switch: { bend: .45, dispersion: .04, frost: .25, rim: .65, edge: .5 },
7
+ selection: { bend: .3, dispersion: .16, frost: 0, rim: .4, edge: .5 },
8
8
  };
9
9
  export function resolveMaterial(base, overrides = {}) {
10
10
  const bounded = (key, max) => {
11
11
  const value = overrides[key] ?? base[key];
12
12
  return Number.isFinite(value) ? Math.max(0, Math.min(max, value)) : base[key];
13
13
  };
14
- return { bend: bounded('bend', .6), dispersion: bounded('dispersion', 1), frost: bounded('frost', 24), rim: bounded('rim', 1) };
14
+ return { bend: bounded('bend', .6), dispersion: bounded('dispersion', 1), frost: bounded('frost', 24), rim: bounded('rim', 1), edge: bounded('edge', 1) };
15
15
  }
16
16
  // Shared CSS defaults are derived from these same presets, including the
17
17
  // surface-only controls that don't need a displacement filter.
18
18
  export const materialDefaults = `
19
- :where(.opal-input,.opal-textarea,.opal-select) { --opal-material-frost:${materials.field.frost}px; --opal-material-rim:${materials.field.rim}; }
20
- :where(.opal-menu-panel) { --opal-material-frost:${materials.menu.frost}px; --opal-material-rim:${materials.menu.rim}; }
19
+ :where(.opal-input,.opal-textarea,.opal-select) { --opal-material-frost:${materials.field.frost}px; --opal-material-rim:${materials.field.rim}; --opal-material-edge:${materials.field.edge}; }
20
+ :where(.opal-menu-panel) { --opal-material-frost:${materials.menu.frost}px; --opal-material-rim:${materials.menu.rim}; --opal-material-edge:${materials.menu.edge}; }
21
21
  `;
22
22
  //# sourceMappingURL=material.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"material.js","sourceRoot":"","sources":["../../../src/internal/glass/material.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,OAAO,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE;IAC1D,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE;IACtD,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;IACpD,KAAK,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE;IACzD,MAAM,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE;IAC5D,SAAS,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE;CACpB,CAAA;AACzC,MAAM,UAAU,eAAe,CAAC,IAAmB,EAAE,YAAoC,EAAE;IACzF,MAAM,OAAO,GAAG,CAAC,GAAwB,EAAE,GAAW,EAAE,EAAE;QACxD,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAA;QACzC,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAC/E,CAAC,CAAA;IACD,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,CAAA;AACjI,CAAC;AAED,yEAAyE;AACzE,+DAA+D;AAC/D,MAAM,CAAC,MAAM,gBAAgB,GAAG;0EAC0C,SAAS,CAAC,KAAK,CAAC,KAAK,2BAA2B,SAAS,CAAC,KAAK,CAAC,GAAG;mDAC1F,SAAS,CAAC,IAAI,CAAC,KAAK,2BAA2B,SAAS,CAAC,IAAI,CAAC,GAAG;CACnH,CAAA","sourcesContent":["/** OpalUI's material vocabulary. Bend is relative to the lens's shorter side;\n * dispersion is the proportional RGB spread, frost is CSS pixels, rim is 0–1. */\nexport type GlassSettings = { bend: number; dispersion: number; frost: number; rim: number }\nexport const materials = {\n surface: { bend: .22, dispersion: .12, frost: 2, rim: .3 },\n field: { bend: 0, dispersion: 0, frost: 12, rim: .22 },\n menu: { bend: 0, dispersion: 0, frost: 24, rim: .3 },\n thumb: { bend: .25, dispersion: .2, frost: .15, rim: .4 },\n switch: { bend: .45, dispersion: .04, frost: .25, rim: .65 },\n selection: { bend: .3, dispersion: .16, frost: 0, rim: .4 },\n} satisfies Record<string, GlassSettings>\nexport function resolveMaterial(base: GlassSettings, overrides: Partial<GlassSettings> = {}): GlassSettings {\n const bounded = (key: keyof GlassSettings, max: number) => {\n const value = overrides[key] ?? base[key]\n return Number.isFinite(value) ? Math.max(0, Math.min(max, value)) : base[key]\n }\n return { bend: bounded('bend', .6), dispersion: bounded('dispersion', 1), frost: bounded('frost', 24), rim: bounded('rim', 1) }\n}\n\n// Shared CSS defaults are derived from these same presets, including the\n// surface-only controls that don't need a displacement filter.\nexport const materialDefaults = `\n:where(.opal-input,.opal-textarea,.opal-select) { --opal-material-frost:${materials.field.frost}px; --opal-material-rim:${materials.field.rim}; }\n:where(.opal-menu-panel) { --opal-material-frost:${materials.menu.frost}px; --opal-material-rim:${materials.menu.rim}; }\n`\n"]}
1
+ {"version":3,"file":"material.js","sourceRoot":"","sources":["../../../src/internal/glass/material.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,OAAO,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;IACpE,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE;IAChE,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;IAC9D,KAAK,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;IACnE,MAAM,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE;IACtE,SAAS,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;CAC9B,CAAA;AACzC,MAAM,UAAU,eAAe,CAAC,IAAmB,EAAE,YAAoC,EAAE;IACzF,MAAM,OAAO,GAAG,CAAC,GAAwB,EAAE,GAAW,EAAE,EAAE;QACxD,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAA;QACzC,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAC/E,CAAC,CAAA;IACD,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,CAAA;AAC3J,CAAC;AAED,yEAAyE;AACzE,+DAA+D;AAC/D,MAAM,CAAC,MAAM,gBAAgB,GAAG;0EAC0C,SAAS,CAAC,KAAK,CAAC,KAAK,2BAA2B,SAAS,CAAC,KAAK,CAAC,GAAG,0BAA0B,SAAS,CAAC,KAAK,CAAC,IAAI;mDACxI,SAAS,CAAC,IAAI,CAAC,KAAK,2BAA2B,SAAS,CAAC,IAAI,CAAC,GAAG,0BAA0B,SAAS,CAAC,IAAI,CAAC,IAAI;CAChK,CAAA","sourcesContent":["/** OpalUI's material vocabulary. Bend is relative to the lens's shorter side;\n * dispersion is the proportional RGB spread, frost is CSS pixels, rim and edge are 0–1. */\nexport type GlassSettings = { bend: number; dispersion: number; frost: number; rim: number; edge: number }\nexport const materials = {\n surface: { bend: .22, dispersion: .12, frost: 2, rim: .3, edge: .5 },\n field: { bend: 0, dispersion: 0, frost: 12, rim: .22, edge: .5 },\n menu: { bend: 0, dispersion: 0, frost: 24, rim: .3, edge: .5 },\n thumb: { bend: .25, dispersion: .2, frost: .15, rim: .4, edge: .5 },\n switch: { bend: .45, dispersion: .04, frost: .25, rim: .65, edge: .5 },\n selection: { bend: .3, dispersion: .16, frost: 0, rim: .4, edge: .5 },\n} satisfies Record<string, GlassSettings>\nexport function resolveMaterial(base: GlassSettings, overrides: Partial<GlassSettings> = {}): GlassSettings {\n const bounded = (key: keyof GlassSettings, max: number) => {\n const value = overrides[key] ?? base[key]\n return Number.isFinite(value) ? Math.max(0, Math.min(max, value)) : base[key]\n }\n return { bend: bounded('bend', .6), dispersion: bounded('dispersion', 1), frost: bounded('frost', 24), rim: bounded('rim', 1), edge: bounded('edge', 1) }\n}\n\n// Shared CSS defaults are derived from these same presets, including the\n// surface-only controls that don't need a displacement filter.\nexport const materialDefaults = `\n:where(.opal-input,.opal-textarea,.opal-select) { --opal-material-frost:${materials.field.frost}px; --opal-material-rim:${materials.field.rim}; --opal-material-edge:${materials.field.edge}; }\n:where(.opal-menu-panel) { --opal-material-frost:${materials.menu.frost}px; --opal-material-rim:${materials.menu.rim}; --opal-material-edge:${materials.menu.edge}; }\n`\n"]}
@@ -3,5 +3,32 @@
3
3
  /* Shared material. Public variables can be set once on a theme container. */
4
4
  :where(.opal-glass, .opal-button, .opal-input, .opal-menu-panel, .opal-switch, .opal-slider, .opal-segmented, .opal-textarea, .opal-select) {
5
5
  --_opal-glass-rim: var(--opal-glass-rim, inset 0 0 0 .5px rgb(255 255 255 / 7%), inset .65px 1px .65px rgb(255 255 255 / var(--opal-material-rim, .28)), inset -.4px -.65px .8px rgb(255 255 255 / calc(var(--opal-material-rim, .28) * .43)));
6
+ --_opal-glass-edge: 0 0 0 .5px rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .16)), -.65px 0 0 rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .48)), .65px 0 0 rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .48));
6
7
  --_opal-glass-shadow: var(--opal-glass-shadow, 0 3px 8px rgb(15 34 47 / 12%));
7
8
  }
9
+
10
+ /* One light material for resting controls: clear white face, fine contour,
11
+ a bright inner lip and a broad, low-contrast shadow. */
12
+ :where([data-theme="light"]) {
13
+ --opal-control-background: linear-gradient(180deg, rgb(255 255 255 / 40%) 0%, rgb(255 255 255 / 8%) 32%, rgb(255 255 255 / 4%) 65%, rgb(255 255 255 / 28%) 100%), rgb(255 255 255 / 32%);
14
+ --opal-control-active-background: rgb(255 255 255 / 60%);
15
+ --opal-control-rim-opacity: 1;
16
+ --opal-button-background: var(--opal-control-background);
17
+ --opal-button-hover-background: var(--opal-control-active-background);
18
+ --opal-button-color: #171717;
19
+ --opal-input-background: var(--opal-control-background);
20
+ --opal-input-focus-background: var(--opal-control-active-background);
21
+ --opal-input-color: #171717;
22
+ --opal-segmented-background: var(--opal-control-background);
23
+ --opal-glass-shadow: 0 5px 12px -4px rgb(0 0 0 / 4%);
24
+ --opal-glass-highlight:
25
+ inset 0 0 0 1px rgb(255 255 255 / 78%),
26
+ inset 0 1px 0 rgb(255 255 255 / 100%),
27
+ inset 0 -1px 0 rgb(255 255 255 / 90%);
28
+ }
29
+
30
+ /* Resolve the contour on each surface, where its material settings live.
31
+ edge=.5 reproduces the established 8%/24% light contour exactly. */
32
+ :where([data-theme="light"]) :where(.opal-glass, .opal-button, .opal-input, .opal-menu-panel, .opal-switch, .opal-slider, .opal-segmented, .opal-textarea, .opal-select) {
33
+ --_opal-glass-rim: var(--opal-glass-rim, var(--_opal-glass-edge), var(--opal-glass-highlight));
34
+ }
@@ -1 +1 @@
1
- export default "/* Shared material. Public variables can be set once on a theme container. */\n:where(.opal-glass, .opal-button, .opal-input, .opal-menu-panel, .opal-switch, .opal-slider, .opal-segmented, .opal-textarea, .opal-select) {\n --_opal-glass-rim: var(--opal-glass-rim, inset 0 0 0 .5px rgb(255 255 255 / 7%), inset .65px 1px .65px rgb(255 255 255 / var(--opal-material-rim, .28)), inset -.4px -.65px .8px rgb(255 255 255 / calc(var(--opal-material-rim, .28) * .43)));\n --_opal-glass-shadow: var(--opal-glass-shadow, 0 3px 8px rgb(15 34 47 / 12%));\n}\n";
1
+ export default "/* Shared material. Public variables can be set once on a theme container. */\n:where(.opal-glass, .opal-button, .opal-input, .opal-menu-panel, .opal-switch, .opal-slider, .opal-segmented, .opal-textarea, .opal-select) {\n --_opal-glass-rim: var(--opal-glass-rim, inset 0 0 0 .5px rgb(255 255 255 / 7%), inset .65px 1px .65px rgb(255 255 255 / var(--opal-material-rim, .28)), inset -.4px -.65px .8px rgb(255 255 255 / calc(var(--opal-material-rim, .28) * .43)));\n --_opal-glass-edge: 0 0 0 .5px rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .16)), -.65px 0 0 rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .48)), .65px 0 0 rgb(40 35 30 / calc(var(--opal-material-edge, .5) * .48));\n --_opal-glass-shadow: var(--opal-glass-shadow, 0 3px 8px rgb(15 34 47 / 12%));\n}\n\n/* One light material for resting controls: clear white face, fine contour,\n a bright inner lip and a broad, low-contrast shadow. */\n:where([data-theme=\"light\"]) {\n --opal-control-background: linear-gradient(180deg, rgb(255 255 255 / 40%) 0%, rgb(255 255 255 / 8%) 32%, rgb(255 255 255 / 4%) 65%, rgb(255 255 255 / 28%) 100%), rgb(255 255 255 / 32%);\n --opal-control-active-background: rgb(255 255 255 / 60%);\n --opal-control-rim-opacity: 1;\n --opal-button-background: var(--opal-control-background);\n --opal-button-hover-background: var(--opal-control-active-background);\n --opal-button-color: #171717;\n --opal-input-background: var(--opal-control-background);\n --opal-input-focus-background: var(--opal-control-active-background);\n --opal-input-color: #171717;\n --opal-segmented-background: var(--opal-control-background);\n --opal-glass-shadow: 0 5px 12px -4px rgb(0 0 0 / 4%);\n --opal-glass-highlight:\n inset 0 0 0 1px rgb(255 255 255 / 78%),\n inset 0 1px 0 rgb(255 255 255 / 100%),\n inset 0 -1px 0 rgb(255 255 255 / 90%);\n}\n\n/* Resolve the contour on each surface, where its material settings live.\n edge=.5 reproduces the established 8%/24% light contour exactly. */\n:where([data-theme=\"light\"]) :where(.opal-glass, .opal-button, .opal-input, .opal-menu-panel, .opal-switch, .opal-slider, .opal-segmented, .opal-textarea, .opal-select) {\n --_opal-glass-rim: var(--opal-glass-rim, var(--_opal-glass-edge), var(--opal-glass-highlight));\n}\n";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opalui/react",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Simple React components, styled with plain CSS.",
5
5
  "type": "module",
6
6
  "files": [