vireglass 2.3.1 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +37 -6
  2. package/dist/{adapters-BOPIRMnL.d.cts → adapters--bXcGvhW.d.cts} +2 -2
  3. package/dist/{adapters-B_amRBqc.d.ts → adapters-BlMhc6pI.d.ts} +2 -2
  4. package/dist/{chunk-MMPK6DCK.js → chunk-CPSIY2DZ.js} +58 -8
  5. package/dist/chunk-CPSIY2DZ.js.map +1 -0
  6. package/dist/{chunk-UDPJLNFW.js → chunk-RWUR3OV6.js} +4 -4
  7. package/dist/{chunk-UDPJLNFW.js.map → chunk-RWUR3OV6.js.map} +1 -1
  8. package/dist/{chunk-CTTZNZFV.js → chunk-VLDD655U.js} +3 -3
  9. package/dist/{chunk-CTTZNZFV.js.map → chunk-VLDD655U.js.map} +1 -1
  10. package/dist/{chunk-V47YU75P.js → chunk-YAN2OK4H.js} +31 -38
  11. package/dist/chunk-YAN2OK4H.js.map +1 -0
  12. package/dist/{chunk-SLRPHDV7.js → chunk-Z4FKYY47.js} +3 -3
  13. package/dist/{chunk-SLRPHDV7.js.map → chunk-Z4FKYY47.js.map} +1 -1
  14. package/dist/dom.cjs +185 -58
  15. package/dist/dom.cjs.map +1 -1
  16. package/dist/dom.d.cts +73 -26
  17. package/dist/dom.d.ts +73 -26
  18. package/dist/dom.js +121 -57
  19. package/dist/dom.js.map +1 -1
  20. package/dist/{geometry-RhKd7CM5.d.cts → geometry-ip9WSlZv.d.cts} +1 -1
  21. package/dist/{geometry-GvXtt-_S.d.ts → geometry-pabkYV7w.d.ts} +1 -1
  22. package/dist/{group-model-z4N27qsC.d.ts → group-model-CQiI5cCe.d.ts} +1 -1
  23. package/dist/{group-model-DwtixGhG.d.cts → group-model-DX2_W9J_.d.cts} +1 -1
  24. package/dist/index.cjs +92 -40
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +6 -6
  27. package/dist/index.d.ts +6 -6
  28. package/dist/index.js +5 -5
  29. package/dist/law.cjs +56 -6
  30. package/dist/law.cjs.map +1 -1
  31. package/dist/law.d.cts +54 -4
  32. package/dist/law.d.ts +54 -4
  33. package/dist/law.js +1 -1
  34. package/dist/{material-CnUmdKg1.d.ts → material-D9m1EN47.d.cts} +9 -6
  35. package/dist/{material-CnUmdKg1.d.cts → material-D9m1EN47.d.ts} +9 -6
  36. package/dist/react.d.cts +1 -1
  37. package/dist/react.d.ts +1 -1
  38. package/dist/reference.d.cts +2 -2
  39. package/dist/reference.d.ts +2 -2
  40. package/dist/reference.js +2 -2
  41. package/dist/web/index.cjs +91 -39
  42. package/dist/web/index.cjs.map +1 -1
  43. package/dist/web/index.d.cts +3 -3
  44. package/dist/web/index.d.ts +3 -3
  45. package/dist/web/index.js +4 -4
  46. package/docs/dom.md +49 -3
  47. package/docs/law.md +1 -1
  48. package/package.json +1 -1
  49. package/src/dom/body.ts +93 -39
  50. package/src/dom/index.ts +50 -21
  51. package/src/dom/probe.ts +1 -1
  52. package/src/dom/rim.ts +69 -32
  53. package/src/dom/style-hold.ts +8 -0
  54. package/src/law.ts +54 -4
  55. package/src/lens-shader.ts +26 -33
  56. package/src/material.ts +10 -7
  57. package/dist/chunk-MMPK6DCK.js.map +0 -1
  58. package/dist/chunk-V47YU75P.js.map +0 -1
package/dist/dom.cjs CHANGED
@@ -41,7 +41,41 @@ var RIM = {
41
41
  */
42
42
  darkEdge: 0.22,
43
43
  /** Width of the hairline, CSS px. "About a point, not a band and not a bevel" (§2). */
44
- widthPx: 1
44
+ widthPx: 1,
45
+ /**
46
+ * How strongly the line is drawn, by the lightness around the element. The rim is a reflection
47
+ * of the surroundings, so it is faint over a dark one and plain over a bright one, not white at
48
+ * full strength everywhere (§2, §6).
49
+ *
50
+ * MEASURED off the controls of A 2:17 (over dark video), M 2:52 (over sky) and A 2:18 (over
51
+ * grass), as the brightest rim bin's OKLab lightness over the surround's: 0.24 to 0.27 over dark,
52
+ * 0.25 over sky, 0.37 over grass. Through the DOM path onto the same backdrops these land within
53
+ * 0.08 of the smooth rows (docs/dom.md); a line white at full strength reads 0.70 to 0.80 over the
54
+ * dark frames.
55
+ */
56
+ strengthDark: 0.37,
57
+ strengthLight: 0.95,
58
+ /**
59
+ * The surround lightness between which the strength moves from one to the other. UNMEASURED: the
60
+ * two ends are, and the controls in between were not looked for.
61
+ */
62
+ ambientFrom: 0.15,
63
+ ambientTo: 0.45,
64
+ /**
65
+ * The share of the peak that the line keeps along the whole contour, away from the key light.
66
+ * MEASURED: the reference line goes all the way round. Its dimmest bin sits 0.06 to 0.12 of
67
+ * lightness over the surround on the dark frames, against a peak of 0.24 to 0.27 (A 2:17).
68
+ */
69
+ contour: 0.3,
70
+ /** How far the line's colour is lifted from the surroundings' toward white. UNMEASURED: the
71
+ * lens always had 0.75 here, and this is that figure with a name. */
72
+ whiteShare: 0.75,
73
+ /**
74
+ * The reflectance (`fresnelStrength`) at which the line is drawn at the strengths above: that
75
+ * of ordinary glass, index 1.5. A denser medium does not draw it brighter than they say; a
76
+ * thinner one draws it fainter. UNMEASURED.
77
+ */
78
+ reflectanceRef: 0.7
45
79
  };
46
80
  var BODY = {
47
81
  /** Lightness ceiling for the body when legibility is loose. */
@@ -55,10 +89,14 @@ var BODY = {
55
89
  busyEdge: 0.75,
56
90
  /** Fine texture the body has to stop being a window for. */
57
91
  groundSpan: 0.06,
58
- /** Where the tint flips direction, by the element's AVERAGE lightness — a local threshold cuts
59
- * a diagonal step across the body on a gradient. */
60
- darkSideFrom: 0.42,
61
- darkSideTo: 0.58,
92
+ /**
93
+ * The spread under the element at which the ink's legibility requirement starts to bind, and the
94
+ * spread where it binds fully. Over a flat backdrop polarity flips before the ink drowns, so
95
+ * the body does not darken under a light glyph there; over texture it does. UNMEASURED: the
96
+ * reference shows a lighter body under a white glyph over sky, nothing about texture.
97
+ */
98
+ inkBusyFrom: 0.02,
99
+ inkBusyTo: 0.12,
62
100
  /** Ceiling on any single demand, so one requirement cannot make the glass opaque on its own. */
63
101
  maxDemand: 0.92,
64
102
  /**
@@ -67,7 +105,13 @@ var BODY = {
67
105
  * separation is the ceiling, and glass at `maxDemand` has stopped being a window. Better to
68
106
  * separate as far as transparency allows and no further. UNMEASURED.
69
107
  */
70
- presenceDemand: 0.5
108
+ presenceDemand: 0.5,
109
+ /**
110
+ * The density at which the body is wholly the ink's own pole. Between `presenceDemand` and this
111
+ * the body moves from the light a thin body carries to the pole: a density no presence floor
112
+ * reaches is one the ink's legibility, or a system setting, has asked for. UNMEASURED.
113
+ */
114
+ inkPoleTo: 0.8
71
115
  };
72
116
  var SHADOW = {
73
117
  /**
@@ -135,6 +179,21 @@ var BEVEL = {
135
179
  slopeMax: 3.2
136
180
  };
137
181
  var EPSILON = 1e-5;
182
+ var MEDIUM = {
183
+ /** The lightness content under the glass is pulled toward, and how hard. The medium both removes
184
+ * light and mixes scattered light back in: over a light backdrop the body darkens, over a dark
185
+ * one it lightens. A layer that only ADDED light pushed the body the wrong way over light
186
+ * backdrops (M 2:35). */
187
+ luma: 0.4,
188
+ pull: 0.07,
189
+ /** Ambient light reaching the body on top of that pull. */
190
+ ambientSpill: 0.01,
191
+ /** Glass concentrates light: the body is a touch lighter than what lies beneath it (M 2:29). */
192
+ concentrate: 0.01,
193
+ /** Scattered light on a frosted element is an ADDITION over the backdrop, not a fraction of the
194
+ * way to the tint — a fraction goes to zero once the backdrop reaches the tint's lightness and
195
+ * darkens past it (docs/benchmarks.md). */
196
+ matteLift: 0.1};
138
197
  var LENS = {
139
198
  /** Same reason, for the profile's slope. */
140
199
  slopeMax: 40,
@@ -1028,8 +1087,10 @@ var {
1028
1087
  tintLight: TINT_LIGHT,
1029
1088
  groundSpan: GROUND_SPAN,
1030
1089
  busyEdge: BUSY_EDGE,
1031
- darkSideFrom: DARK_SIDE_FROM,
1032
- darkSideTo: DARK_SIDE_TO,
1090
+ inkBusyFrom: INK_BUSY_FROM,
1091
+ inkBusyTo: INK_BUSY_TO,
1092
+ inkPoleTo: INK_POLE_TO,
1093
+ presenceDemand: PRESENCE_DEMAND,
1033
1094
  maxDemand: MAX_DEMAND
1034
1095
  } = BODY;
1035
1096
  var clamp3 = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
@@ -1038,6 +1099,10 @@ function smoothstep2(edge0, edge1, x) {
1038
1099
  const t = clamp3((x - edge0) / (edge1 - edge0), 0, 1);
1039
1100
  return t * t * (3 - 2 * t);
1040
1101
  }
1102
+ function tintFor(density, need, pole) {
1103
+ const asked = Math.max(clamp3(need / Math.max(density, 1e-4), 0, 1), smoothstep2(PRESENCE_DEMAND, INK_POLE_TO, density));
1104
+ return mix(TINT_LIGHT, pole, asked);
1105
+ }
1041
1106
  function resolveBody(optics, sample, ink) {
1042
1107
  const strict = clamp3(optics.legibility * 2, 0, 1);
1043
1108
  const capLight = mix(BODY_CAP_LOOSE, BODY_CAP_TIGHT, strict);
@@ -1046,33 +1111,45 @@ function resolveBody(optics, sample, ink) {
1046
1111
  const demand = clamp3(optics.legibility * 4, 0, 1);
1047
1112
  const mean = clamp3(sample.luma, 0, 1);
1048
1113
  const busy = Math.max(sample.busy, 0);
1049
- const darkSide = smoothstep2(DARK_SIDE_FROM, DARK_SIDE_TO, mean);
1050
- const away = mix(TINT_LIGHT, TINT_DARK, darkSide);
1051
- const tintLuma = mix(away, mix(TINT_LIGHT, TINT_DARK, pol), demand);
1114
+ const pole = mix(TINT_LIGHT, TINT_DARK, pol);
1052
1115
  const edge = busy * BUSY_EDGE;
1053
1116
  const inkHi = Math.min(mean + edge, 1);
1054
1117
  const inkLo = Math.max(mean - edge, 0);
1055
1118
  const needForLight = inkHi > capLight ? clamp3((inkHi - capLight) / Math.max(inkHi - TINT_DARK, 1e-4), 0, MAX_DEMAND) : 0;
1056
1119
  const needForDark = inkLo < floorDark ? clamp3((floorDark - inkLo) / Math.max(TINT_LIGHT - inkLo, 1e-4), 0, MAX_DEMAND) : 0;
1057
- const needForInk = mix(needForDark, needForLight, pol) * demand;
1120
+ const structured = smoothstep2(INK_BUSY_FROM, INK_BUSY_TO, busy);
1121
+ const needForInk = mix(needForDark, needForLight, pol) * demand * structured;
1058
1122
  const ground = busy * optics.legibility * GROUND_SPAN;
1059
- return { density: Math.max(Math.max(optics.bodyDensity, ground), needForInk), tintLuma };
1123
+ const density = Math.max(Math.max(optics.bodyDensity, ground), needForInk);
1124
+ return { density, tintLuma: tintFor(density, needForInk, pole), need: needForInk, pole };
1060
1125
  }
1061
- function withPresence(body, optics, sample) {
1126
+ function withPresence(body, optics, _sample) {
1062
1127
  if (optics.presence <= 0) return body;
1063
- const mean = clamp3(sample.luma, 0, 1);
1064
- const above = Math.abs(body.tintLuma - mean) > 1e-4 ? body.tintLuma > mean : mean < 0.5;
1065
- const pole = above ? TINT_LIGHT : TINT_DARK;
1066
- const separation = Math.min(optics.presence, Math.abs(pole - mean));
1067
- const reached = Math.abs(body.tintLuma - mean) * body.density;
1068
- if (reached >= separation) return body;
1069
- const needed = separation / Math.max(Math.abs(pole - mean), 1e-4);
1070
- return {
1071
- density: Math.max(body.density, Math.min(needed, BODY.presenceDemand)),
1072
- // Left where the tint law put it, unless it landed exactly on the backdrop — which is the
1073
- // case that used to be created here rather than avoided.
1074
- tintLuma: Math.abs(body.tintLuma - mean) > 1e-4 ? body.tintLuma : pole
1128
+ const density = Math.max(body.density, Math.min(optics.presence / TINT_LIGHT, PRESENCE_DEMAND));
1129
+ return { ...body, density, tintLuma: tintFor(density, body.need, body.pole) };
1130
+ }
1131
+ function hueOf(c) {
1132
+ const l = 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2];
1133
+ if (l < 0.02) return [1, 1, 1];
1134
+ return [clamp3(c[0] / l, 0, 2), clamp3(c[1] / l, 0, 2), clamp3(c[2] / l, 0, 2)];
1135
+ }
1136
+ function bodyLayer(optics, sample, ink, ambient, appear = 1) {
1137
+ const a = clamp3(appear, 0, 1);
1138
+ const body = withPresence(resolveBody(optics, sample, ink), optics);
1139
+ const hue = hueOf(ambient);
1140
+ const pickup = clamp3(optics.colorPickup, 0, 1);
1141
+ const density = clamp3(body.density * a, 0, 1);
1142
+ const matte = optics.legibility * MEDIUM.matteLift * a;
1143
+ const pull = MEDIUM.pull * a;
1144
+ const spill = optics.edgeLight * MEDIUM.ambientSpill * a;
1145
+ const lift = MEDIUM.concentrate * a;
1146
+ const alpha = 1 - (1 - lift) * (1 - pull) * (1 - density);
1147
+ const channel = (i) => {
1148
+ const tintHue = mix(1, hue[i], pickup);
1149
+ const added = (1 - pull) * (density * tintHue * body.tintLuma + matte * tintHue) + pull * hue[i] * MEDIUM.luma + ambient[i] * spill;
1150
+ return alpha > 1e-6 ? clamp3(((1 - lift) * added + lift) / alpha, 0, 1) : 0;
1075
1151
  };
1152
+ return { r: channel(0), g: channel(1), b: channel(2), alpha };
1076
1153
  }
1077
1154
 
1078
1155
  // src/accessibility.ts
@@ -1202,7 +1279,14 @@ function concentricRadius(outerRadius, inset, minimum = 0) {
1202
1279
  }
1203
1280
 
1204
1281
  // src/dom/rim.ts
1205
- var { lobeExponent: LOBE_EXPONENT, opposingArc: OPPOSING_ARC, darkEdge: DARK_EDGE } = RIM;
1282
+ var {
1283
+ lobeExponent: LOBE_EXPONENT,
1284
+ opposingArc: OPPOSING_ARC,
1285
+ darkEdge: DARK_EDGE,
1286
+ contour: CONTOUR,
1287
+ whiteShare: WHITE_SHARE,
1288
+ reflectanceRef: REFLECTANCE_REF
1289
+ } = RIM;
1206
1290
  var STOPS = 48;
1207
1291
  function lightConicAngle(light) {
1208
1292
  const [lx, ly] = light;
@@ -1217,30 +1301,48 @@ function rimLobe(deltaDeg, specular) {
1217
1301
  }
1218
1302
  var clamp016 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
1219
1303
  var to2552 = (v) => Math.round(clamp016(v) * 255);
1304
+ var mixTo = (a, b, t) => a + (b - a) * t;
1305
+ function smoothstep3(edge0, edge1, x) {
1306
+ const t = clamp016((x - edge0) / (edge1 - edge0));
1307
+ return t * t * (3 - 2 * t);
1308
+ }
1309
+ function ambientLuma(ambient) {
1310
+ return 0.2126 * ambient[0] + 0.7152 * ambient[1] + 0.0722 * ambient[2];
1311
+ }
1312
+ function rimStrength(luma3) {
1313
+ return mixTo(RIM.strengthDark, RIM.strengthLight, smoothstep3(RIM.ambientFrom, RIM.ambientTo, luma3));
1314
+ }
1315
+ function rimLineAlpha(deltaDeg, optics, luma3) {
1316
+ const reflect = Math.min(optics.fresnel / REFLECTANCE_REF, 1);
1317
+ const damping = optics.fresnel > 0 ? optics.specular / optics.fresnel : 0;
1318
+ const lobe = rimLobe(deltaDeg, damping);
1319
+ return clamp016(rimStrength(luma3) * reflect * (CONTOUR + (1 - CONTOUR) * lobe));
1320
+ }
1220
1321
  function rimGradientCss(optics, ambient, light, appear = 1) {
1221
1322
  const present = clamp016(appear);
1222
1323
  const start = lightConicAngle(light);
1223
1324
  const [ar, ag, ab] = ambient;
1224
- const stops = [];
1325
+ const luma3 = ambientLuma(ambient);
1326
+ const line = [];
1225
1327
  for (let i = 0; i <= STOPS; i += 1) {
1226
1328
  const angle = i / STOPS * 360;
1227
- const key = rimLobe(angle, optics.specular);
1228
1329
  const facing = Math.cos(angle * Math.PI / 180);
1229
1330
  const fringe = facing < 0 ? optics.iridescence * OPPOSING_ARC : 0;
1230
- const lift = clamp016(key * optics.fresnel);
1231
- const r = ar + (1 - ar) * clamp016(lift + fringe);
1232
- const g = ag + (1 - ag) * lift;
1233
- const b = ab + (1 - ab) * clamp016(lift - fringe);
1234
- const alpha = clamp016(DARK_EDGE + lift * (1 - DARK_EDGE)) * present;
1235
- const colour = lift > 1e-3 ? `rgba(${to2552(r)},${to2552(g)},${to2552(b)},${alpha.toFixed(3)})` : `rgba(0,0,0,${alpha.toFixed(3)})`;
1236
- stops.push(`${colour} ${angle.toFixed(1)}deg`);
1331
+ const r = ar + (1 - ar) * clamp016(WHITE_SHARE + fringe);
1332
+ const g = ag + (1 - ag) * WHITE_SHARE;
1333
+ const b = ab + (1 - ab) * clamp016(WHITE_SHARE - fringe);
1334
+ const alpha = rimLineAlpha(angle, optics, luma3) * present;
1335
+ line.push(`rgba(${to2552(r)},${to2552(g)},${to2552(b)},${alpha.toFixed(3)}) ${angle.toFixed(1)}deg`);
1237
1336
  }
1238
- return `conic-gradient(from ${start.toFixed(1)}deg, ${stops.join(", ")})`;
1337
+ const dark = `rgba(0,0,0,${(DARK_EDGE * present).toFixed(3)})`;
1338
+ return `conic-gradient(from ${start.toFixed(1)}deg, ${line.join(", ")}), conic-gradient(${dark} 0deg, ${dark} 360deg)`;
1239
1339
  }
1240
1340
  var RIM_WIDTH_PX = RIM.widthPx;
1241
- function contrastRimCss(bodyLuma, rimLuma) {
1341
+ function contrastRimCss(bodyLuma, rimLuma, appear = 1) {
1242
1342
  const v = to2552(rimLuma);
1243
- return `conic-gradient(rgb(${v},${v},${v}) 0deg, rgb(${v},${v},${v}) 360deg)`;
1343
+ const present = clamp016(appear);
1344
+ const colour = present >= 1 ? `rgb(${v},${v},${v})` : `rgba(${v},${v},${v},${present.toFixed(3)})`;
1345
+ return `conic-gradient(${colour} 0deg, ${colour} 360deg)`;
1244
1346
  }
1245
1347
 
1246
1348
  // src/dom/nesting.ts
@@ -1256,6 +1358,15 @@ function warnIfNested(el) {
1256
1358
  `vireglass: glass inside glass \u2014 this element is a descendant of <${where}>, which is also glass. Its backdrop is the outer element's output, so the refraction is applied twice and the blurs multiply. Put the glass on one of the two.`
1257
1359
  );
1258
1360
  }
1361
+
1362
+ // src/dom/style-hold.ts
1363
+ function holdProperty(style, name) {
1364
+ const saved = style.getPropertyValue(name);
1365
+ return () => {
1366
+ if (saved) style.setProperty(name, saved);
1367
+ else style.removeProperty(name);
1368
+ };
1369
+ }
1259
1370
  var REST_LIGHT = [-0.577, -0.817];
1260
1371
 
1261
1372
  // src/touch-response.ts
@@ -1755,16 +1866,16 @@ var to2553 = (v) => Math.round(Math.min(Math.max(v, 0), 1) * 255);
1755
1866
  function rgbCss(r, g, b) {
1756
1867
  return `rgb(${to2553(r)} ${to2553(g)} ${to2553(b)})`;
1757
1868
  }
1758
- function bodyCss(optics, sample, ink, accent) {
1759
- const body = withPresence(resolveBody(optics, sample, ink), optics, sample);
1869
+ function bodyCss(optics, sample, ink, accent, appear) {
1760
1870
  if (accent) {
1761
- const [r2, g2, b2] = accentTone(accent.color, sample.luma);
1871
+ const settled = applyAppear(optics, appear);
1872
+ const body = withPresence(resolveBody(settled, sample, ink), settled);
1873
+ const [r, g, b] = accentTone(accent.color, sample.luma);
1762
1874
  const amount = Math.max(accentAmount(accent), body.density);
1763
- return `rgba(${to2553(r2)}, ${to2553(g2)}, ${to2553(b2)}, ${amount.toFixed(3)})`;
1875
+ return `rgba(${to2553(r)}, ${to2553(g)}, ${to2553(b)}, ${amount.toFixed(3)})`;
1764
1876
  }
1765
- const [r, g, b] = ambientFrom(sample);
1766
- const hue = (c) => body.tintLuma + (c - body.tintLuma) * optics.colorPickup;
1767
- return `rgba(${to2553(hue(r))}, ${to2553(hue(g))}, ${to2553(hue(b))}, ${body.density.toFixed(3)})`;
1877
+ const layer = bodyLayer(optics, sample, ink, ambientFrom(sample), appear);
1878
+ return `rgba(${to2553(layer.r)}, ${to2553(layer.g)}, ${to2553(layer.b)}, ${layer.alpha.toFixed(3)})`;
1768
1879
  }
1769
1880
  function attachGlass(el, opts = {}) {
1770
1881
  if (typeof document === "undefined") {
@@ -1781,6 +1892,7 @@ function attachGlass(el, opts = {}) {
1781
1892
  const onlyWhenTouched = opts.variant === "interactive";
1782
1893
  let appear = onlyWhenTouched ? 0 : opts.appear ?? 1;
1783
1894
  let accent = opts.accent;
1895
+ let supplied = opts.sample;
1784
1896
  let a11y = systemAccessibility();
1785
1897
  const stopWatchingA11y = watchAccessibility((next) => {
1786
1898
  a11y = next;
@@ -1792,13 +1904,16 @@ function attachGlass(el, opts = {}) {
1792
1904
  const host = hostFor(el);
1793
1905
  ensureRimStyles(host);
1794
1906
  el.setAttribute(GLASS_ATTR, "");
1907
+ const restoreBackground = holdProperty(el.style, "background-color");
1795
1908
  const positionWasStatic = getComputedStyle(el).position === "static";
1796
1909
  if (positionWasStatic) el.style.setProperty("position", "relative");
1797
- function optics(patch) {
1910
+ function settledOptics(patch) {
1798
1911
  const base = resolveOptics(patch ?? opts.material);
1799
1912
  const scaled = applyGlassScale(base, opts.scale ?? GLASS_SCALE_DEFAULT);
1800
- const settled = applyAccessibility(scaled, opts.accessibility ?? a11y);
1801
- return applyAppear(settled, appear);
1913
+ return applyAccessibility(scaled, opts.accessibility ?? a11y);
1914
+ }
1915
+ function optics(patch) {
1916
+ return applyAppear(settledOptics(patch), appear);
1802
1917
  }
1803
1918
  function morphState() {
1804
1919
  if (!morph) return {};
@@ -1813,7 +1928,10 @@ function attachGlass(el, opts = {}) {
1813
1928
  if (destroyed) return;
1814
1929
  const geometry = opts.geometry ?? measureGeometry(el);
1815
1930
  const opticsNow = optics();
1816
- const sample = opts.sample ?? probeBackdrop(el);
1931
+ const sample = supplied ?? probeBackdrop(el);
1932
+ const light = shouldInkBeLight(sample, wasLight);
1933
+ wasLight = light;
1934
+ const body = opts.body === false ? null : bodyCss(settledOptics(), sample, light ? 1 : 0, accent, appear);
1817
1935
  if (svgSupported) {
1818
1936
  const key = `a${appear.toFixed(3)}|m${morphKey()}|${geometry.width}x${geometry.height}r${geometry.cornerRadius}@${devicePixelRatio()}:${opticsNow.refraction}:${opticsNow.refractionScale}:${opticsNow.bevelDp}:${opticsNow.blur}`;
1819
1937
  if (key !== mapKey || !filterEl) {
@@ -1831,10 +1949,8 @@ function attachGlass(el, opts = {}) {
1831
1949
  } else {
1832
1950
  el.style.setProperty("backdrop-filter", fallbackFilterCss(opticsNow));
1833
1951
  el.style.setProperty("-webkit-backdrop-filter", fallbackFilterCss(opticsNow));
1834
- el.style.backgroundColor = bodyCss(opticsNow, sample, wasLight ? 1 : 0, accent);
1835
1952
  }
1836
- const light = shouldInkBeLight(sample, wasLight);
1837
- wasLight = light;
1953
+ if (body) el.style.backgroundColor = body;
1838
1954
  const [tr, tg, tb] = ambientFrom(sample);
1839
1955
  el.style.setProperty("--vireglass-ink", light ? "1" : "0");
1840
1956
  el.style.setProperty("--vireglass-tint", `${tr} ${tg} ${tb}`);
@@ -1842,11 +1958,14 @@ function attachGlass(el, opts = {}) {
1842
1958
  const inkLevel = Math.round((light ? INK_LIGHT : INK_DARK) * 255);
1843
1959
  el.style.setProperty("--vireglass-ink-color", `rgb(${inkLevel} ${inkLevel} ${inkLevel})`);
1844
1960
  el.style.setProperty("--vireglass-tint-color", rgbCss(tr, tg, tb));
1845
- el.style.setProperty("--vireglass-body-color", bodyCss(opticsNow, sample, light ? 1 : 0, accent));
1846
- const pole = withPresence(resolveBody(opticsNow, sample, light ? 1 : 0), opticsNow, sample).tintLuma;
1961
+ el.style.setProperty(
1962
+ "--vireglass-body-color",
1963
+ body ?? bodyCss(settledOptics(), sample, light ? 1 : 0, accent, appear)
1964
+ );
1965
+ const pole = withPresence(resolveBody(opticsNow, sample, light ? 1 : 0), opticsNow).tintLuma;
1847
1966
  el.style.setProperty(
1848
1967
  "--vireglass-rim",
1849
- (opts.accessibility ?? a11y).increaseContrast ? contrastRimCss(pole, contrastRimLuma(pole)) : rimGradientCss(opticsNow, [tr, tg, tb], opts.light ?? REST_LIGHT, appear)
1968
+ (opts.accessibility ?? a11y).increaseContrast ? contrastRimCss(pole, contrastRimLuma(pole), appear) : rimGradientCss(opticsNow, [tr, tg, tb], opts.light ?? REST_LIGHT, appear)
1850
1969
  );
1851
1970
  el.style.setProperty("--vireglass-rim-width", `${RIM_WIDTH_PX}px`);
1852
1971
  el.style.setProperty("--vireglass-radius", `${geometry.cornerRadius}px`);
@@ -1904,7 +2023,7 @@ function attachGlass(el, opts = {}) {
1904
2023
  el.style.setProperty("--vireglass-glow-opacity", "0");
1905
2024
  return;
1906
2025
  }
1907
- const [gr, gg, gb] = ambientFrom(opts.sample ?? probeBackdrop(el));
2026
+ const [gr, gg, gb] = ambientFrom(supplied ?? probeBackdrop(el));
1908
2027
  const cx = (x + geometry.width / 2) / geometry.width * 100;
1909
2028
  const cy = (y + geometry.height / 2) / geometry.height * 100;
1910
2029
  const reach = halfMinDp(geometry) * TOUCH_RADIUS_FRACTION;
@@ -1994,7 +2113,7 @@ function attachGlass(el, opts = {}) {
1994
2113
  mapKey = "";
1995
2114
  el.style.removeProperty("backdrop-filter");
1996
2115
  el.style.removeProperty("-webkit-backdrop-filter");
1997
- el.style.removeProperty("background-color");
2116
+ if (opts.body !== false) restoreBackground();
1998
2117
  el.style.removeProperty("--vireglass-ink");
1999
2118
  el.style.removeProperty("--vireglass-tint");
2000
2119
  el.style.removeProperty("--vireglass-body-density");
@@ -2042,6 +2161,10 @@ function attachGlass(el, opts = {}) {
2042
2161
  mapKey = "";
2043
2162
  apply();
2044
2163
  },
2164
+ setSample(next) {
2165
+ supplied = next ?? void 0;
2166
+ apply();
2167
+ },
2045
2168
  destroy
2046
2169
  };
2047
2170
  attached.set(el, handle);
@@ -2049,8 +2172,10 @@ function attachGlass(el, opts = {}) {
2049
2172
  }
2050
2173
 
2051
2174
  exports.RIM_WIDTH_PX = RIM_WIDTH_PX;
2175
+ exports.ambientLuma = ambientLuma;
2052
2176
  exports.attachGlass = attachGlass;
2053
2177
  exports.attachScrollEdge = attachScrollEdge;
2178
+ exports.bodyLayer = bodyLayer;
2054
2179
  exports.boxShadowCss = boxShadowCss;
2055
2180
  exports.buildDisplacementMap = buildDisplacementMap;
2056
2181
  exports.buildSpectralMap = buildSpectralMap;
@@ -2070,7 +2195,9 @@ exports.renderSpectralPixels = renderSpectralPixels;
2070
2195
  exports.resetSupportCache = resetSupportCache;
2071
2196
  exports.resolveBody = resolveBody;
2072
2197
  exports.rimGradientCss = rimGradientCss;
2198
+ exports.rimLineAlpha = rimLineAlpha;
2073
2199
  exports.rimLobe = rimLobe;
2200
+ exports.rimStrength = rimStrength;
2074
2201
  exports.shadowAlphaFrom = shadowAlphaFrom;
2075
2202
  exports.statsFromSamples = statsFromSamples;
2076
2203
  exports.supportsBackdropFilter = supportsBackdropFilter;