@boyernick/standard-ui-react 0.4.2-canary.64 → 0.4.2-canary.66

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boyernick/standard-ui-react",
3
- "version": "0.4.2-canary.64",
3
+ "version": "0.4.2-canary.66",
4
4
  "description": "React components for StandardUI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -24,7 +24,11 @@ export interface GlassConfig {
24
24
  frost?: number;
25
25
  /** Film grain. Default 0.07. */
26
26
  grain?: number;
27
- /** Multiplier on the ground seen through the glass (1 = undimmed). Default 0.8. */
27
+ /**
28
+ * Multiplier on the ground seen through the glass (1 = undimmed). Default
29
+ * 0.8 over a dark ground, 1 over a light one: dimming a white page only
30
+ * turned the glass a flat mid-grey.
31
+ */
28
32
  dim?: number;
29
33
  /** Fraction of the height where the glass starts fading out. Default 1 (no fade). */
30
34
  fadeStart?: number;
@@ -121,6 +125,7 @@ export function resolveGlass(
121
125
  width: number,
122
126
  height: number,
123
127
  cssRadius: number,
128
+ options?: { lightGround?: boolean },
124
129
  ): ResolvedGlass {
125
130
  const short = Math.max(1, Math.min(width, height));
126
131
  const radius = pick(config.cornerRadius, cssRadius, 0, short / 2);
@@ -142,7 +147,7 @@ export function resolveGlass(
142
147
  specular: pick(config.specular, 0.35, 0, 2),
143
148
  frost: pick(config.frost, 3, 0, 16),
144
149
  grain: pick(config.grain, 0.07, 0, 0.5),
145
- dim: pick(config.dim, 0.8, 0, 1.5),
150
+ dim: pick(config.dim, options?.lightGround ? 1 : 0.8, 0, 1.5),
146
151
  fadeStart: pick(config.fadeStart, 1, 0, 1),
147
152
  fadeEnd: pick(config.fadeEnd, 1, 0, 1),
148
153
  shadow: pick(config.shadow, 0.18, 0, 1),
@@ -240,6 +240,33 @@ function imageCopy(image: HTMLImageElement, ready: () => void): CanvasImageSourc
240
240
  return null;
241
241
  }
242
242
 
243
+ /** Whether a CSS colour is light (relative luminance over one half), cached per colour. */
244
+ const groundLightness = new Map<string, boolean>();
245
+ let lightnessProbe: CanvasRenderingContext2D | null = null;
246
+
247
+ function isLightGround(color: string) {
248
+ const known = groundLightness.get(color);
249
+ if (known !== undefined) return known;
250
+ lightnessProbe ??= Object.assign(document.createElement("canvas"), { width: 1, height: 1 })
251
+ .getContext("2d", { willReadFrequently: true });
252
+ let light = false;
253
+ if (lightnessProbe) {
254
+ // The canvas parses any CSS colour the page can use, oklab and all.
255
+ lightnessProbe.clearRect(0, 0, 1, 1);
256
+ lightnessProbe.fillStyle = "#000";
257
+ lightnessProbe.fillStyle = color;
258
+ lightnessProbe.fillRect(0, 0, 1, 1);
259
+ const [r, g, b] = lightnessProbe.getImageData(0, 0, 1, 1).data;
260
+ const linear = (value: number) => {
261
+ const c = value / 255;
262
+ return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
263
+ };
264
+ light = 0.2126 * linear(r) + 0.7152 * linear(g) + 0.0722 * linear(b) > 0.5;
265
+ }
266
+ groundLightness.set(color, light);
267
+ return light;
268
+ }
269
+
243
270
  /** Whether drawing this source would taint a canvas (its pixels are cross-origin). */
244
271
  function unreadable(source: CanvasImageSource) {
245
272
  const probe = document.createElement("canvas");
@@ -580,7 +607,9 @@ export function createLensGlass({ root, glassElements, defaults = {} }: LensGlas
580
607
  const style = getComputedStyle(element);
581
608
  const config = { ...defaults, ...parseConfig(element.dataset.config) };
582
609
  const cssRadius = parseFloat(style.borderTopLeftRadius) || 0;
583
- const glass = resolveGlass(config, rect.width, rect.height, cssRadius);
610
+ const glass = resolveGlass(config, rect.width, rect.height, cssRadius, {
611
+ lightGround: isLightGround(ground),
612
+ });
584
613
 
585
614
  const pad = samplePadding(glass) + OVERFLOW;
586
615
  const region: Box = {
package/src/modal.tsx CHANGED
@@ -149,7 +149,9 @@ export const ModalTrigger = ({
149
149
  )
150
150
 
151
151
  const ModalBackdrop = () => (
152
- <DialogBackdrop className="bg-surface-scrim/95" />
152
+ // DialogBackdrop is black/60 in dark mode; say 95 there too, or the page
153
+ // shows through behind the image.
154
+ <DialogBackdrop className="bg-surface-scrim/95 dark:bg-surface-scrim/95" />
153
155
  )
154
156
 
155
157
  const ModalBackground = ({ src }: { src: string }) => (