@monimesl/dot-renderer-react 1.0.0-beta.10 → 1.0.0-beta.12

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/dist/color.js CHANGED
@@ -91,4 +91,4 @@ function fromHsl(hue, saturation, lightness) {
91
91
  ];
92
92
  }
93
93
  //#endregion
94
- export { darken, mix };
94
+ export { darken, mix, toHex };
@@ -1,6 +1,6 @@
1
1
  import { CARD_RADIUS_SCALE } from "./tokens.js";
2
2
  import { isTone, toneBackground, toneColor } from "./tones.js";
3
- import { BACKGROUNDS } from "@monimesl/dot-core";
3
+ import { BACKGROUNDS, BORDER_TONES } from "@monimesl/dot-core";
4
4
  //#region src/components/box.ts
5
5
  const RADIUS_SCALE = {
6
6
  none: 0,
@@ -10,16 +10,17 @@ const RADIUS_SCALE = {
10
10
  };
11
11
  const FULL_RADIUS = 9999;
12
12
  const BACKGROUND_WORDS = new Set(BACKGROUNDS.words);
13
+ const BORDER_TONE_WORDS = new Set(BORDER_TONES.words);
13
14
  function boxStyle(props, theme, look = {}) {
14
15
  const background = props.background ?? look.background;
15
- const border = props.border ?? look.border;
16
+ const border = props.border ?? (props.borderTone ? "subtle" : look.border);
16
17
  const radius = props.radius ?? look.radius;
17
18
  const style = {};
18
19
  if (background !== void 0) style.background = backgroundColor(background, theme);
19
20
  if (border === "none") style.border = "none";
20
- else if (border !== void 0) style.border = `1px solid ${border === "strong" ? theme.colors.subtle : theme.colors.border}`;
21
+ else if (border !== void 0) style.border = `1px solid ${borderColor(border, props.borderTone, theme)}`;
21
22
  if (radius !== void 0) {
22
- style.borderRadius = radius === "full" ? FULL_RADIUS : theme.radius * RADIUS_SCALE[radius];
23
+ style.borderRadius = cornerRadius(radius === "full" ? FULL_RADIUS : theme.radius * RADIUS_SCALE[radius], props.corners);
23
24
  style.overflow = "hidden";
24
25
  }
25
26
  if (props.width !== void 0) {
@@ -35,6 +36,17 @@ function boxStyle(props, theme, look = {}) {
35
36
  if (props.maxWidth !== void 0) style.maxWidth = props.maxWidth;
36
37
  return style;
37
38
  }
39
+ function borderColor(border, tone, theme) {
40
+ const usual = border === "strong" ? theme.colors.subtle : theme.colors.border;
41
+ if (tone === void 0) return usual;
42
+ if (BORDER_TONE_WORDS.has(tone)) return theme.colors[tone];
43
+ return customColor(tone, theme) ?? usual;
44
+ }
45
+ function cornerRadius(size, corners) {
46
+ if (corners === "top") return `${size}px ${size}px 0 0`;
47
+ if (corners === "bottom") return `0 0 ${size}px ${size}px`;
48
+ return size;
49
+ }
38
50
  function backgroundColor(background, theme) {
39
51
  if (isTone(background)) return toneBackground(theme.colors, background);
40
52
  if (BACKGROUND_WORDS.has(background)) return theme.colors[background];
@@ -1,10 +1,11 @@
1
1
  import { SHADOW_REACH, SPACE } from "./tokens.js";
2
2
  import { useTheme } from "../theme.js";
3
+ import { childElements } from "./child-elements.js";
3
4
  import { clamp } from "./numbers.js";
4
5
  import { paddingStyle } from "./padding.js";
5
6
  import { SideRoomContext } from "./side-room.js";
6
7
  import { createEvent } from "@monimesl/dot-core";
7
- import { Children, Fragment, isValidElement, useContext, useEffect, useEffectEvent, useRef, useState } from "react";
8
+ import { useContext, useEffect, useEffectEvent, useRef, useState } from "react";
8
9
  import { jsx, jsxs } from "react/jsx-runtime";
9
10
  //#region src/components/carousel.tsx
10
11
  const SLIDE_DURATION = 350;
@@ -16,7 +17,7 @@ const TAP_SLOP = 6;
16
17
  const Carousel$1 = ({ autoplay = false, interval = 4e3, loop = false, indicator = "dots", gap = "none", effect = "slide", index, onChange, children, ...padding }) => {
17
18
  const { colors, space, elevation } = useTheme();
18
19
  const mayUseSides = useContext(SideRoomContext);
19
- const slides = slidesOf(children);
20
+ const slides = childElements(children);
20
21
  const count = slides.length;
21
22
  const looping = loop && count > 1;
22
23
  const firstPosition = looping ? 1 : 0;
@@ -250,19 +251,6 @@ const Carousel$1 = ({ autoplay = false, interval = 4e3, loop = false, indicator
250
251
  }) : null]
251
252
  });
252
253
  };
253
- function slidesOf(children, keyPrefix = "") {
254
- const slides = [];
255
- for (const child of Children.toArray(children)) {
256
- if (!isValidElement(child)) continue;
257
- const key = `${keyPrefix}${String(child.key)}`;
258
- if (child.type === Fragment) slides.push(...slidesOf(child.props.children, `${key}/`));
259
- else slides.push({
260
- key,
261
- node: child
262
- });
263
- }
264
- return slides;
265
- }
266
254
  function slideIndex(index, count) {
267
255
  return clamp(Math.round(index), 0, count - 1);
268
256
  }
@@ -0,0 +1,25 @@
1
+ import { Children, Fragment, isValidElement } from "react";
2
+ //#region src/components/child-elements.ts
3
+ function childElements(children, keyPrefix = "") {
4
+ const elements = [];
5
+ for (const child of Children.toArray(children)) {
6
+ if (!isValidElement(child)) continue;
7
+ const key = `${keyPrefix}${String(child.key)}`;
8
+ if (child.type === Fragment) elements.push(...childElements(child.props.children, `${key}/`));
9
+ else elements.push({
10
+ key,
11
+ node: child
12
+ });
13
+ }
14
+ return elements;
15
+ }
16
+ function hasContent(children) {
17
+ for (const child of Children.toArray(children)) {
18
+ if (child === "") continue;
19
+ if (!isValidElement(child) || child.type !== Fragment) return true;
20
+ if (hasContent(child.props.children)) return true;
21
+ }
22
+ return false;
23
+ }
24
+ //#endregion
25
+ export { childElements, hasContent };
@@ -28,6 +28,7 @@ const Disclosure$1 = ({ title, subtitle, open: startsOpen = false, disabled, chi
28
28
  },
29
29
  style: {
30
30
  display: "flex",
31
+ listStyle: "none",
31
32
  alignItems: "center",
32
33
  gap: SPACE.medium,
33
34
  padding: "12px 0",
@@ -1,5 +1,6 @@
1
1
  import { CENTERED, ICON_SIZE, SPACE, WEIGHTS } from "./tokens.js";
2
2
  import { useTheme } from "../theme.js";
3
+ import { hasContent } from "./child-elements.js";
3
4
  import { insets, paddingStyle } from "./padding.js";
4
5
  import { IconGraphic } from "./icon.js";
5
6
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -50,7 +51,7 @@ const EmptyState = ({ icon, title, text, children, ...padding }) => {
50
51
  },
51
52
  children: text
52
53
  }) : null,
53
- children ? /* @__PURE__ */ jsx("div", {
54
+ hasContent(children) ? /* @__PURE__ */ jsx("div", {
54
55
  style: {
55
56
  display: "flex",
56
57
  gap: SPACE.medium,
@@ -0,0 +1,28 @@
1
+ import { useLayoutEffect, useRef } from "react";
2
+ //#region src/components/enter.ts
3
+ const ENTER_DURATION = 250;
4
+ const ENTER_FRAMES = {
5
+ fade: {
6
+ offset: 0,
7
+ opacity: 0
8
+ },
9
+ rise: {
10
+ offset: 0,
11
+ opacity: 0,
12
+ transform: "translateY(12px)"
13
+ }
14
+ };
15
+ function useEnterAnimation(enter, hidden) {
16
+ const ref = useRef(null);
17
+ useLayoutEffect(() => {
18
+ if (hidden || !Object.hasOwn(ENTER_FRAMES, enter)) return;
19
+ if (matchMedia("(prefers-reduced-motion: reduce)").matches) return;
20
+ ref.current?.animate([ENTER_FRAMES[enter]], {
21
+ duration: ENTER_DURATION,
22
+ easing: "ease-out"
23
+ });
24
+ }, [enter, hidden]);
25
+ return ref;
26
+ }
27
+ //#endregion
28
+ export { useEnterAnimation };
@@ -3,6 +3,7 @@ import { useTheme } from "../theme.js";
3
3
  import { CLOSE_ARTWORK } from "./icon-artwork.js";
4
4
  import { IconSvg } from "./icon-svg.js";
5
5
  import { STATUS_ARTWORK, toneBackground, toneColor } from "./tones.js";
6
+ import { hasContent } from "./child-elements.js";
6
7
  import { clamp } from "./numbers.js";
7
8
  import { insets, paddingStyle } from "./padding.js";
8
9
  import { IconButton } from "./icon-button.js";
@@ -64,7 +65,7 @@ const Notice$1 = ({ tone = "default", title, children, onClose, ...padding }) =>
64
65
  children: [title ? /* @__PURE__ */ jsx("span", {
65
66
  style: { fontWeight: WEIGHTS.bold },
66
67
  children: title
67
- }) : null, children ? /* @__PURE__ */ jsx("span", { children }) : null]
68
+ }) : null, hasContent(children) ? /* @__PURE__ */ jsx("span", { children }) : null]
68
69
  }),
69
70
  onClose ? /* @__PURE__ */ jsx(IconButton, {
70
71
  artwork: CLOSE_ARTWORK,
@@ -2,6 +2,7 @@ import { CARD_RADIUS_SCALE, CENTERED, DISABLED_OPACITY, ICON_SIZE, SHADOWS, WEIG
2
2
  import { useTheme } from "../theme.js";
3
3
  import { CHEVRON_RIGHT_ARTWORK } from "./icon-artwork.js";
4
4
  import { IconSvg } from "./icon-svg.js";
5
+ import { hasContent } from "./child-elements.js";
5
6
  import { insets, paddingStyle } from "./padding.js";
6
7
  import { IconGraphic } from "./icon.js";
7
8
  import { createEvent } from "@monimesl/dot-core";
@@ -95,7 +96,7 @@ const ListItem$1 = ({ title, subtitle, value, icon, image, disabled = false, onT
95
96
  color: colors.text
96
97
  })
97
98
  });
98
- const trailing = children || value;
99
+ const trailing = hasContent(children) ? children : value;
99
100
  const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [
100
101
  leading,
101
102
  /* @__PURE__ */ jsxs("div", {
@@ -33,7 +33,8 @@ const SheetPanel = ({ open, title, showCloseIcon = false, onClose, children, ...
33
33
  position: "fixed",
34
34
  inset: 0,
35
35
  zIndex: 100,
36
- background: "rgba(0, 0, 0, 0.4)"
36
+ background: "rgba(0, 0, 0, 0.4)",
37
+ transition: "opacity 0.2s"
37
38
  }
38
39
  }), /* @__PURE__ */ jsx(DialogPopup, {
39
40
  className: "dot-sheet",
@@ -43,6 +44,7 @@ const SheetPanel = ({ open, title, showCloseIcon = false, onClose, children, ...
43
44
  insetInline: 0,
44
45
  bottom: 0,
45
46
  zIndex: 101,
47
+ transition: "transform 0.2s ease-out",
46
48
  maxHeight: "85vh",
47
49
  overflowY: "auto",
48
50
  borderTopLeftRadius: cornerRadius,
@@ -1,7 +1,7 @@
1
1
  import { useTheme } from "../theme.js";
2
+ import { childElements } from "./child-elements.js";
2
3
  import { SideRoomContext } from "./side-room.js";
3
4
  import { boxStyle } from "./box.js";
4
- import { Children } from "react";
5
5
  import { jsx } from "react/jsx-runtime";
6
6
  //#region src/components/stack.tsx
7
7
  const Stack = (props) => {
@@ -14,14 +14,14 @@ const Stack = (props) => {
14
14
  },
15
15
  children: /* @__PURE__ */ jsx(SideRoomContext.Provider, {
16
16
  value: false,
17
- children: Children.map(props.children, (child) => /* @__PURE__ */ jsx("div", {
17
+ children: childElements(props.children).map(({ key, node }) => /* @__PURE__ */ jsx("div", {
18
18
  style: {
19
19
  gridArea: "1 / 1",
20
20
  display: "grid",
21
21
  minWidth: 0
22
22
  },
23
- children: child
24
- }))
23
+ children: node
24
+ }, key))
25
25
  })
26
26
  });
27
27
  };
@@ -3,6 +3,7 @@ import { useTheme } from "../theme.js";
3
3
  import { paddingStyle } from "./padding.js";
4
4
  import { textToneColor } from "./box.js";
5
5
  import { InsideProse } from "./prose-context.js";
6
+ import { warnIfHardToRead } from "../development-warnings.js";
6
7
  import { DEFAULT_VARIANTS } from "@monimesl/dot-core";
7
8
  import { createContext, useContext } from "react";
8
9
  import { jsx } from "react/jsx-runtime";
@@ -50,7 +51,11 @@ const DECORATION = {
50
51
  underline: "underline",
51
52
  strikethrough: "line-through"
52
53
  };
53
- const Text$1 = ({ variant: namedVariant, tone, weight, align, maxLines, decoration, numeric, leading, italic, selectable, children, ...padding }) => {
54
+ function letterSpacing(variant, caps) {
55
+ if (caps) return "0.06em";
56
+ return variant === "display" ? "-0.02em" : "normal";
57
+ }
58
+ const Text$1 = ({ variant: namedVariant, tone, weight, align, maxLines, decoration, numeric, leading, italic, caps, selectable, children, ...padding }) => {
54
59
  const theme = useTheme();
55
60
  const insideText = useContext(InsideText);
56
61
  const inProse = useContext(InsideProse);
@@ -67,7 +72,8 @@ const Text$1 = ({ variant: namedVariant, tone, weight, align, maxLines, decorati
67
72
  fontFamily: heading ? theme.headingFontFamily : void 0,
68
73
  fontWeight: WEIGHTS[weight ?? (heading ? theme.headingWeight : void 0) ?? defaultWeight],
69
74
  fontStyle: italic ? "italic" : void 0,
70
- letterSpacing: variant === "display" ? "-0.02em" : "normal",
75
+ letterSpacing: letterSpacing(variant, caps),
76
+ textTransform: caps ? "uppercase" : void 0,
71
77
  color: tone ? textToneColor(tone, theme) : "inherit",
72
78
  textAlign: align,
73
79
  textDecorationLine: decoration ? DECORATION[decoration] : void 0,
@@ -90,12 +96,15 @@ const Text$1 = ({ variant: namedVariant, tone, weight, align, maxLines, decorati
90
96
  value: true,
91
97
  children
92
98
  });
99
+ const ref = tone === "muted" ? void 0 : warnIfHardToRead;
93
100
  if ((variant === "title" || variant === "subtitle") && !insideText) return /* @__PURE__ */ jsx(variant === "title" ? "h2" : "h3", {
101
+ ref,
94
102
  className: "dot-text dot-heading",
95
103
  style,
96
104
  children: content
97
105
  });
98
106
  return /* @__PURE__ */ jsx("span", {
107
+ ref,
99
108
  className: "dot-text",
100
109
  style,
101
110
  children: content
@@ -20,6 +20,11 @@ const WEIGHTS = {
20
20
  };
21
21
  const CARD_RADIUS_SCALE = 1.5;
22
22
  const DISABLED_OPACITY = .6;
23
+ const OPACITY = {
24
+ full: 1,
25
+ soft: .7,
26
+ faint: .4
27
+ };
23
28
  function pressLook(disabled) {
24
29
  return {
25
30
  opacity: disabled ? DISABLED_OPACITY : 1,
@@ -71,4 +76,4 @@ const SELECTABLE = {
71
76
  WebkitUserSelect: "text"
72
77
  };
73
78
  //#endregion
74
- export { CARD_RADIUS_SCALE, CENTERED, DISABLED_OPACITY, FONT_SIZE, ICON_SIZE, PILL_STYLE, SCROLL_SHADOW_ROOM, SELECTABLE, SHADOWS, SHADOW_REACH, SPACE, WEIGHTS, pressLook };
79
+ export { CARD_RADIUS_SCALE, CENTERED, DISABLED_OPACITY, FONT_SIZE, ICON_SIZE, OPACITY, PILL_STYLE, SCROLL_SHADOW_ROOM, SELECTABLE, SHADOWS, SHADOW_REACH, SPACE, WEIGHTS, pressLook };
@@ -1,10 +1,11 @@
1
- import { DISABLED_OPACITY, SHADOWS } from "./tokens.js";
1
+ import { DISABLED_OPACITY, OPACITY, SHADOWS } from "./tokens.js";
2
2
  import { useTheme } from "../theme.js";
3
3
  import { insets, paddingStyle } from "./padding.js";
4
4
  import { SideRoomContext } from "./side-room.js";
5
5
  import { boxStyle } from "./box.js";
6
6
  import { pressable } from "./pressable.js";
7
7
  import { HiddenViewContext } from "./hidden-view.js";
8
+ import { useEnterAnimation } from "./enter.js";
8
9
  import { DEFAULT_VARIANTS, createEvent } from "@monimesl/dot-core";
9
10
  import { useContext } from "react";
10
11
  import { jsx } from "react/jsx-runtime";
@@ -31,10 +32,11 @@ const RAISED_CARD_LOOK = {
31
32
  border: "none"
32
33
  };
33
34
  const View$1 = (props) => {
34
- const { id, hidden = false, disabled = false, direction = "column", gap = "medium", align = "stretch", justify = "start", wrap = false, grow = false, variant = DEFAULT_VARIANTS.View, onTap, children } = props;
35
+ const { id, hidden = false, disabled = false, direction = "column", gap = "medium", align = "stretch", justify = "start", wrap = false, grow = false, variant = DEFAULT_VARIANTS.View, opacity = "full", enter = "none", onTap, children } = props;
35
36
  const theme = useTheme();
36
37
  const sideRoomAround = useContext(SideRoomContext);
37
38
  const hiddenAround = useContext(HiddenViewContext);
39
+ const enterRef = useEnterAnimation(enter, hidden || hiddenAround);
38
40
  const card = variant === "card";
39
41
  const raisedCard = card && theme.elevation === "raised" && props.elevation !== "none";
40
42
  let cardLook;
@@ -54,7 +56,7 @@ const View$1 = (props) => {
54
56
  "--dot-view-start": `${padding.paddingInlineStart}px`,
55
57
  "--dot-view-end": `${padding.paddingInlineEnd}px`,
56
58
  ...boxStyle(props, theme, cardLook),
57
- opacity: disabled ? DISABLED_OPACITY : void 0,
59
+ opacity: disabled ? DISABLED_OPACITY : OPACITY[opacity],
58
60
  cursor: onTap && !disabled ? "pointer" : void 0
59
61
  };
60
62
  if (elevation !== "none") style.boxShadow = SHADOWS[theme.scheme][elevation];
@@ -64,6 +66,7 @@ const View$1 = (props) => {
64
66
  }
65
67
  const press = onTap && !disabled ? pressable(() => onTap(createEvent("tap"))) : void 0;
66
68
  return /* @__PURE__ */ jsx("div", {
69
+ ref: enterRef,
67
70
  className: "dot-view",
68
71
  "data-dot-view": id,
69
72
  hidden,
@@ -0,0 +1,47 @@
1
+ import { toHex } from "./color.js";
2
+ import { contrast, variantProblem } from "@monimesl/dot-core";
3
+ //#region src/development-warnings.ts
4
+ const developing = process.env.NODE_ENV !== "production";
5
+ const MINIMUM_CONTRAST = 3;
6
+ const warned = /* @__PURE__ */ new Set();
7
+ function warnOnce(problem, about = problem) {
8
+ if (!warned.has(about)) {
9
+ warned.add(about);
10
+ console.warn(problem);
11
+ }
12
+ }
13
+ const warnAboutVariant = developing ? (name, props, components) => {
14
+ const problem = variantProblem(name, props, components);
15
+ if (problem !== void 0) warnOnce(problem);
16
+ } : void 0;
17
+ const warnIfHardToRead = developing ? (element) => {
18
+ if (element === null || element.closest(".dot-stack")) return;
19
+ const color = opaqueHex(getComputedStyle(element).color);
20
+ const background = solidBackgroundBehind(element);
21
+ if (color === void 0 || background === void 0) return;
22
+ const measured = contrast(color, background);
23
+ if (measured < MINIMUM_CONTRAST) {
24
+ const pair = `${color} on ${background}`;
25
+ warnOnce(`"${element.textContent?.slice(0, 40)}" is hard to read: ${pair} has a contrast of ${measured.toFixed(1)}. Change its tone or its background to reach at least ${MINIMUM_CONTRAST}.`, pair);
26
+ }
27
+ } : void 0;
28
+ function solidBackgroundBehind(element) {
29
+ for (let current = element; current; current = current.parentElement) {
30
+ const style = getComputedStyle(current);
31
+ if (style.backgroundImage !== "none" && style.backgroundImage !== "") return;
32
+ const background = opaqueHex(style.backgroundColor);
33
+ if (background !== void 0) return background;
34
+ }
35
+ }
36
+ function opaqueHex(computed) {
37
+ if (!computed.startsWith("rgb")) return;
38
+ const [red, green, blue, alpha = 1] = computed.match(/[\d.]+/g)?.map(Number) ?? [];
39
+ if (blue === void 0 || alpha < 1) return;
40
+ return toHex([
41
+ red,
42
+ green,
43
+ blue
44
+ ]);
45
+ }
46
+ //#endregion
47
+ export { warnAboutVariant, warnIfHardToRead };
package/dist/page-root.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { useTheme } from "./theme.js";
2
+ import { warnAboutVariant } from "./development-warnings.js";
2
3
  import { toList, toText, withComponentDefaults } from "@monimesl/dot-core";
3
4
  import { Fragment, createElement, useMemo, useSyncExternalStore } from "react";
4
5
  //#region src/page-root.tsx
@@ -9,7 +10,9 @@ function PageRoot({ page, components }) {
9
10
  h(type, props, ...children) {
10
11
  const component = components[type];
11
12
  if (!component) throw new Error(`The renderer has no component for <${type}>. Check the tag's name, or pass a component for it to the renderer.`);
12
- return createElement(component, withComponentDefaults(type, props, themeComponents), ...children);
13
+ const merged = withComponentDefaults(type, props, themeComponents);
14
+ warnAboutVariant?.(type, merged, themeComponents);
15
+ return createElement(component, merged, ...children);
13
16
  },
14
17
  text: toText,
15
18
  on: (handler, data) => page.on(handler, data),
package/dist/root.js CHANGED
@@ -27,7 +27,10 @@ function Root({ page, dotContext, components, wrapper: Wrapper }) {
27
27
  ref: setRootElement,
28
28
  className: "dot-theme-root",
29
29
  "data-dot-scheme": resolvedTheme.scheme,
30
- style: { overflow: "clip" },
30
+ style: {
31
+ overflow: "clip",
32
+ WebkitTapHighlightColor: "transparent"
33
+ },
31
34
  children: /* @__PURE__ */ jsx(PortalContainerContext.Provider, {
32
35
  value: rootElement,
33
36
  children: /* @__PURE__ */ jsx(PullToRefresh, {
package/dist/styles.css CHANGED
@@ -1 +1 @@
1
- @layer base{.dot-theme-root,.dot-theme-root *,.dot-theme-root :before,.dot-theme-root :after{box-sizing:border-box}.dot-theme-root :is(button,input,select,textarea){font:inherit}[data-dot-scheme=light]{color-scheme:light}[data-dot-scheme=dark]{color-scheme:dark}.dot-theme-root{-webkit-user-select:none;user-select:none}.dot-theme-root :is(input,textarea){-webkit-user-select:text;user-select:text}}@keyframes dot-skeleton-pulse{0%,to{opacity:1}50%{opacity:.4}}.dot-image{max-width:100%;display:block}.dot-list>.dot-list-item:not(:last-child){border-bottom:1px solid var(--dot-list-divider)}.dot-scroll::-webkit-scrollbar{display:none}.dot-scroll>*{scroll-snap-align:var(--dot-scroll-snap);flex:none;width:min-content}.dot-scroll .dot-image{max-width:none}.dot-bar-top:not(:not(.dot-toast,[hidden])~*){margin-top:calc(-1 * var(--dot-view-top,0px))}.dot-bar-bottom:not(:has(~:not(.dot-toast,[hidden]))){margin-bottom:calc(-1 * var(--dot-view-bottom,0px) - env(safe-area-inset-bottom,0px))}.dot-disclosure>summary{list-style:none}.dot-disclosure>summary::-webkit-details-marker{display:none}.dot-steps>.dot-step:last-child .dot-step-line{display:none}.dot-sheet-backdrop{transition:opacity .2s}.dot-sheet{transition:transform .2s ease-out}.dot-sheet-backdrop:is([data-starting-style],[data-ending-style]){opacity:0}.dot-sheet:is([data-starting-style],[data-ending-style]){transform:translateY(100%)}.dot-prose>.dot-heading{margin-block-start:.5em}.dot-prose>.dot-heading:first-child{margin-block-start:0}
1
+ @layer base{.dot-theme-root,.dot-theme-root *,.dot-theme-root :before,.dot-theme-root :after{box-sizing:border-box}.dot-theme-root :is(button,input,select,textarea){font:inherit}[data-dot-scheme=light]{color-scheme:light}[data-dot-scheme=dark]{color-scheme:dark}.dot-theme-root{-webkit-user-select:none;user-select:none}.dot-theme-root :is(input,textarea){-webkit-user-select:text;user-select:text}}@keyframes dot-skeleton-pulse{0%,to{opacity:1}50%{opacity:.4}}.dot-image{max-width:100%;display:block}.dot-list>.dot-list-item:not(:last-child){border-bottom:1px solid var(--dot-list-divider)}.dot-scroll::-webkit-scrollbar{display:none}.dot-scroll>*{scroll-snap-align:var(--dot-scroll-snap);flex:none;width:min-content}.dot-scroll .dot-image{max-width:none}.dot-bar-top:not(:not(.dot-toast,[hidden])~*){margin-top:calc(-1 * var(--dot-view-top,0px))}.dot-bar-bottom:not(:has(~:not(.dot-toast,[hidden]))){margin-bottom:calc(-1 * var(--dot-view-bottom,0px) - env(safe-area-inset-bottom,0px))}html{overscroll-behavior-y:none}:is(button,a,summary,[role=button]):active:not(:disabled,[aria-disabled=true],:has(:is(button,a,summary,[role=button]):active)){filter:opacity(.7)}.dot-disclosure>summary::-webkit-details-marker{display:none}.dot-steps>.dot-step:last-child .dot-step-line{display:none}.dot-sheet-backdrop:is([data-starting-style],[data-ending-style]){opacity:0}.dot-sheet:is([data-starting-style],[data-ending-style]){transform:translateY(100%)}.dot-prose>.dot-heading{margin-block-start:.5em}.dot-prose>.dot-heading:first-child{margin-block-start:0}
package/dist/theme.js CHANGED
@@ -128,6 +128,7 @@ function resolveGradients(gradients, colors, customColors) {
128
128
  return resolved;
129
129
  }
130
130
  function themeColor(name, colors, customColors) {
131
+ if (name === "transparent") return name;
131
132
  if (Object.hasOwn(colors, name)) return colors[name];
132
133
  if (Object.hasOwn(customColors, name)) return customColors[name];
133
134
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monimesl/dot-renderer-react",
3
- "version": "1.0.0-beta.10",
3
+ "version": "1.0.0-beta.12",
4
4
  "description": "React renderer for Dot webviews",
5
5
  "type": "module",
6
6
  "repository": {
@@ -29,8 +29,8 @@
29
29
  "react": "19.3.0",
30
30
  "react-dom": "19.3.0",
31
31
  "uqr": "^0.1.3",
32
- "@monimesl/dot-core": "1.0.0-beta.10",
33
- "@monimesl/dot-webview": "1.0.0-beta.10"
32
+ "@monimesl/dot-core": "1.0.0-beta.12",
33
+ "@monimesl/dot-webview": "1.0.0-beta.12"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@base-ui/react": "1.8.0",
@@ -46,7 +46,7 @@
46
46
  "tsdown": "^0.23.0",
47
47
  "typescript": "^5.9.3",
48
48
  "vitest": "^5.0.1",
49
- "@monimesl/dot-service": "1.0.0-beta.10"
49
+ "@monimesl/dot-service": "1.0.0-beta.12"
50
50
  },
51
51
  "publishConfig": {
52
52
  "access": "public"