vireglass 2.3.1 → 3.0.0

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 +180 -55
  15. package/dist/dom.cjs.map +1 -1
  16. package/dist/dom.d.cts +72 -25
  17. package/dist/dom.d.ts +72 -25
  18. package/dist/dom.js +116 -54
  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 +49 -20
  51. package/src/dom/probe.ts +1 -1
  52. package/src/dom/rim.ts +63 -28
  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,25 +1301,41 @@ 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
1341
  function contrastRimCss(bodyLuma, rimLuma) {
@@ -1256,6 +1356,15 @@ function warnIfNested(el) {
1256
1356
  `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
1357
  );
1258
1358
  }
1359
+
1360
+ // src/dom/style-hold.ts
1361
+ function holdProperty(style, name) {
1362
+ const saved = style.getPropertyValue(name);
1363
+ return () => {
1364
+ if (saved) style.setProperty(name, saved);
1365
+ else style.removeProperty(name);
1366
+ };
1367
+ }
1259
1368
  var REST_LIGHT = [-0.577, -0.817];
1260
1369
 
1261
1370
  // src/touch-response.ts
@@ -1755,16 +1864,16 @@ var to2553 = (v) => Math.round(Math.min(Math.max(v, 0), 1) * 255);
1755
1864
  function rgbCss(r, g, b) {
1756
1865
  return `rgb(${to2553(r)} ${to2553(g)} ${to2553(b)})`;
1757
1866
  }
1758
- function bodyCss(optics, sample, ink, accent) {
1759
- const body = withPresence(resolveBody(optics, sample, ink), optics, sample);
1867
+ function bodyCss(optics, sample, ink, accent, appear) {
1760
1868
  if (accent) {
1761
- const [r2, g2, b2] = accentTone(accent.color, sample.luma);
1869
+ const settled = applyAppear(optics, appear);
1870
+ const body = withPresence(resolveBody(settled, sample, ink), settled);
1871
+ const [r, g, b] = accentTone(accent.color, sample.luma);
1762
1872
  const amount = Math.max(accentAmount(accent), body.density);
1763
- return `rgba(${to2553(r2)}, ${to2553(g2)}, ${to2553(b2)}, ${amount.toFixed(3)})`;
1873
+ return `rgba(${to2553(r)}, ${to2553(g)}, ${to2553(b)}, ${amount.toFixed(3)})`;
1764
1874
  }
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)})`;
1875
+ const layer = bodyLayer(optics, sample, ink, ambientFrom(sample), appear);
1876
+ return `rgba(${to2553(layer.r)}, ${to2553(layer.g)}, ${to2553(layer.b)}, ${layer.alpha.toFixed(3)})`;
1768
1877
  }
1769
1878
  function attachGlass(el, opts = {}) {
1770
1879
  if (typeof document === "undefined") {
@@ -1781,6 +1890,7 @@ function attachGlass(el, opts = {}) {
1781
1890
  const onlyWhenTouched = opts.variant === "interactive";
1782
1891
  let appear = onlyWhenTouched ? 0 : opts.appear ?? 1;
1783
1892
  let accent = opts.accent;
1893
+ let supplied = opts.sample;
1784
1894
  let a11y = systemAccessibility();
1785
1895
  const stopWatchingA11y = watchAccessibility((next) => {
1786
1896
  a11y = next;
@@ -1792,13 +1902,16 @@ function attachGlass(el, opts = {}) {
1792
1902
  const host = hostFor(el);
1793
1903
  ensureRimStyles(host);
1794
1904
  el.setAttribute(GLASS_ATTR, "");
1905
+ const restoreBackground = holdProperty(el.style, "background-color");
1795
1906
  const positionWasStatic = getComputedStyle(el).position === "static";
1796
1907
  if (positionWasStatic) el.style.setProperty("position", "relative");
1797
- function optics(patch) {
1908
+ function settledOptics(patch) {
1798
1909
  const base = resolveOptics(patch ?? opts.material);
1799
1910
  const scaled = applyGlassScale(base, opts.scale ?? GLASS_SCALE_DEFAULT);
1800
- const settled = applyAccessibility(scaled, opts.accessibility ?? a11y);
1801
- return applyAppear(settled, appear);
1911
+ return applyAccessibility(scaled, opts.accessibility ?? a11y);
1912
+ }
1913
+ function optics(patch) {
1914
+ return applyAppear(settledOptics(patch), appear);
1802
1915
  }
1803
1916
  function morphState() {
1804
1917
  if (!morph) return {};
@@ -1813,7 +1926,10 @@ function attachGlass(el, opts = {}) {
1813
1926
  if (destroyed) return;
1814
1927
  const geometry = opts.geometry ?? measureGeometry(el);
1815
1928
  const opticsNow = optics();
1816
- const sample = opts.sample ?? probeBackdrop(el);
1929
+ const sample = supplied ?? probeBackdrop(el);
1930
+ const light = shouldInkBeLight(sample, wasLight);
1931
+ wasLight = light;
1932
+ const body = opts.body === false ? null : bodyCss(settledOptics(), sample, light ? 1 : 0, accent, appear);
1817
1933
  if (svgSupported) {
1818
1934
  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
1935
  if (key !== mapKey || !filterEl) {
@@ -1831,10 +1947,8 @@ function attachGlass(el, opts = {}) {
1831
1947
  } else {
1832
1948
  el.style.setProperty("backdrop-filter", fallbackFilterCss(opticsNow));
1833
1949
  el.style.setProperty("-webkit-backdrop-filter", fallbackFilterCss(opticsNow));
1834
- el.style.backgroundColor = bodyCss(opticsNow, sample, wasLight ? 1 : 0, accent);
1835
1950
  }
1836
- const light = shouldInkBeLight(sample, wasLight);
1837
- wasLight = light;
1951
+ if (body) el.style.backgroundColor = body;
1838
1952
  const [tr, tg, tb] = ambientFrom(sample);
1839
1953
  el.style.setProperty("--vireglass-ink", light ? "1" : "0");
1840
1954
  el.style.setProperty("--vireglass-tint", `${tr} ${tg} ${tb}`);
@@ -1842,8 +1956,11 @@ function attachGlass(el, opts = {}) {
1842
1956
  const inkLevel = Math.round((light ? INK_LIGHT : INK_DARK) * 255);
1843
1957
  el.style.setProperty("--vireglass-ink-color", `rgb(${inkLevel} ${inkLevel} ${inkLevel})`);
1844
1958
  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;
1959
+ el.style.setProperty(
1960
+ "--vireglass-body-color",
1961
+ body ?? bodyCss(settledOptics(), sample, light ? 1 : 0, accent, appear)
1962
+ );
1963
+ const pole = withPresence(resolveBody(opticsNow, sample, light ? 1 : 0), opticsNow).tintLuma;
1847
1964
  el.style.setProperty(
1848
1965
  "--vireglass-rim",
1849
1966
  (opts.accessibility ?? a11y).increaseContrast ? contrastRimCss(pole, contrastRimLuma(pole)) : rimGradientCss(opticsNow, [tr, tg, tb], opts.light ?? REST_LIGHT, appear)
@@ -1904,7 +2021,7 @@ function attachGlass(el, opts = {}) {
1904
2021
  el.style.setProperty("--vireglass-glow-opacity", "0");
1905
2022
  return;
1906
2023
  }
1907
- const [gr, gg, gb] = ambientFrom(opts.sample ?? probeBackdrop(el));
2024
+ const [gr, gg, gb] = ambientFrom(supplied ?? probeBackdrop(el));
1908
2025
  const cx = (x + geometry.width / 2) / geometry.width * 100;
1909
2026
  const cy = (y + geometry.height / 2) / geometry.height * 100;
1910
2027
  const reach = halfMinDp(geometry) * TOUCH_RADIUS_FRACTION;
@@ -1994,7 +2111,7 @@ function attachGlass(el, opts = {}) {
1994
2111
  mapKey = "";
1995
2112
  el.style.removeProperty("backdrop-filter");
1996
2113
  el.style.removeProperty("-webkit-backdrop-filter");
1997
- el.style.removeProperty("background-color");
2114
+ if (opts.body !== false) restoreBackground();
1998
2115
  el.style.removeProperty("--vireglass-ink");
1999
2116
  el.style.removeProperty("--vireglass-tint");
2000
2117
  el.style.removeProperty("--vireglass-body-density");
@@ -2042,6 +2159,10 @@ function attachGlass(el, opts = {}) {
2042
2159
  mapKey = "";
2043
2160
  apply();
2044
2161
  },
2162
+ setSample(next) {
2163
+ supplied = next ?? void 0;
2164
+ apply();
2165
+ },
2045
2166
  destroy
2046
2167
  };
2047
2168
  attached.set(el, handle);
@@ -2049,8 +2170,10 @@ function attachGlass(el, opts = {}) {
2049
2170
  }
2050
2171
 
2051
2172
  exports.RIM_WIDTH_PX = RIM_WIDTH_PX;
2173
+ exports.ambientLuma = ambientLuma;
2052
2174
  exports.attachGlass = attachGlass;
2053
2175
  exports.attachScrollEdge = attachScrollEdge;
2176
+ exports.bodyLayer = bodyLayer;
2054
2177
  exports.boxShadowCss = boxShadowCss;
2055
2178
  exports.buildDisplacementMap = buildDisplacementMap;
2056
2179
  exports.buildSpectralMap = buildSpectralMap;
@@ -2070,7 +2193,9 @@ exports.renderSpectralPixels = renderSpectralPixels;
2070
2193
  exports.resetSupportCache = resetSupportCache;
2071
2194
  exports.resolveBody = resolveBody;
2072
2195
  exports.rimGradientCss = rimGradientCss;
2196
+ exports.rimLineAlpha = rimLineAlpha;
2073
2197
  exports.rimLobe = rimLobe;
2198
+ exports.rimStrength = rimStrength;
2074
2199
  exports.shadowAlphaFrom = shadowAlphaFrom;
2075
2200
  exports.statsFromSamples = statsFromSamples;
2076
2201
  exports.supportsBackdropFilter = supportsBackdropFilter;