braid-design-system 34.1.0 → 34.2.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/CHANGELOG.md +58 -0
  2. package/dist/color-mode/query-param.d.cts +1 -1
  3. package/dist/color-mode/query-param.d.mts +1 -1
  4. package/dist/css.cjs +3 -2
  5. package/dist/css.d.cts +4 -0
  6. package/dist/css.d.mts +4 -0
  7. package/dist/css.mjs +3 -2
  8. package/dist/lib/components/Badge/Badge.cjs +3 -1
  9. package/dist/lib/components/Badge/Badge.d.cts +2 -0
  10. package/dist/lib/components/Badge/Badge.d.mts +2 -0
  11. package/dist/lib/components/Badge/Badge.mjs +3 -1
  12. package/dist/lib/components/Drawer/Drawer.d.cts +1 -1
  13. package/dist/lib/components/Drawer/Drawer.d.mts +1 -1
  14. package/dist/lib/components/MenuRenderer/MenuRenderer.cjs +1 -1
  15. package/dist/lib/components/MenuRenderer/MenuRenderer.mjs +1 -1
  16. package/dist/lib/components/private/Modal/Modal.css.cjs +20 -3
  17. package/dist/lib/components/private/Modal/Modal.css.mjs +18 -5
  18. package/dist/lib/components/private/Modal/ModalContent.cjs +131 -67
  19. package/dist/lib/components/private/Modal/ModalContent.d.cts +9 -3
  20. package/dist/lib/components/private/Modal/ModalContent.d.mts +9 -3
  21. package/dist/lib/components/private/Modal/ModalContent.mjs +134 -70
  22. package/dist/lib/components/private/ScrollContainer/ScrollContainer.cjs +25 -6
  23. package/dist/lib/components/private/ScrollContainer/ScrollContainer.css.cjs +14 -6
  24. package/dist/lib/components/private/ScrollContainer/ScrollContainer.css.mjs +15 -7
  25. package/dist/lib/components/private/ScrollContainer/ScrollContainer.mjs +25 -6
  26. package/dist/lib/components/private/scrollbars.css.cjs +27 -0
  27. package/dist/lib/components/private/scrollbars.css.mjs +26 -0
  28. package/dist/lib/playroom/snippets/BrandedContainer.cjs +16 -0
  29. package/dist/lib/playroom/snippets/BrandedContainer.mjs +15 -0
  30. package/dist/lib/playroom/snippets/CardList.cjs +10 -0
  31. package/dist/lib/playroom/snippets/CardList.mjs +9 -0
  32. package/dist/lib/playroom/snippets/CompactPage.cjs +10 -0
  33. package/dist/lib/playroom/snippets/CompactPage.mjs +9 -0
  34. package/dist/lib/playroom/snippets/DividedList.cjs +10 -0
  35. package/dist/lib/playroom/snippets/DividedList.mjs +9 -0
  36. package/dist/lib/playroom/snippets/Form.cjs +14 -0
  37. package/dist/lib/playroom/snippets/Form.mjs +13 -0
  38. package/dist/lib/playroom/snippets/SpaciousPage.cjs +10 -0
  39. package/dist/lib/playroom/snippets/SpaciousPage.mjs +9 -0
  40. package/dist/lib/playroom/snippets/StandardPage.cjs +10 -0
  41. package/dist/lib/playroom/snippets/StandardPage.mjs +9 -0
  42. package/dist/lib/playroom/snippets/StandardSection.cjs +10 -0
  43. package/dist/lib/playroom/snippets/StandardSection.mjs +9 -0
  44. package/dist/lib/playroom/snippets/SteppedSection.cjs +10 -0
  45. package/dist/lib/playroom/snippets/SteppedSection.mjs +9 -0
  46. package/dist/lib/playroom/snippets/TabbedSection.cjs +10 -0
  47. package/dist/lib/playroom/snippets/TabbedSection.mjs +9 -0
  48. package/dist/lib/playroom/snippets/TableSection.cjs +10 -0
  49. package/dist/lib/playroom/snippets/TableSection.mjs +9 -0
  50. package/dist/lib/playroom/snippets.cjs +22 -4
  51. package/dist/lib/playroom/snippets.mjs +108 -90
  52. package/dist/lib/themes/baseTokens/nvl.cjs +1 -1
  53. package/dist/lib/themes/baseTokens/nvl.mjs +1 -1
  54. package/package.json +3 -4
  55. package/dist/lib/playroom/snippets/layouts.cjs +0 -22
  56. package/dist/lib/playroom/snippets/layouts.mjs +0 -21
  57. package/dist/lib/playroom/snippets/sections.cjs +0 -59
  58. package/dist/lib/playroom/snippets/sections.mjs +0 -58
@@ -4,7 +4,7 @@ import { BoxProps } from "../../Box/Box.cjs";
4
4
  import { ReactNode, Ref } from "react";
5
5
 
6
6
  //#region src/lib/components/private/Modal/ModalContent.d.ts
7
- interface ModalContentProps {
7
+ type ModalContentCommonProps = {
8
8
  id?: string;
9
9
  title: string;
10
10
  children: ReactNode;
@@ -12,13 +12,19 @@ interface ModalContentProps {
12
12
  closeLabel?: string;
13
13
  width: BoxProps['maxWidth'] | 'content';
14
14
  description?: ReactNodeNoStrings;
15
- illustration?: ReactNodeNoStrings;
16
15
  position: 'center' | 'right' | 'left';
17
16
  headingLevel: '2' | '3';
18
17
  scrollLock?: boolean;
19
18
  headingRef?: Ref<HTMLElement>;
20
19
  modalRef?: Ref<HTMLElement>;
21
20
  data?: DataAttributeMap;
22
- }
21
+ };
22
+ type ModalContentProps = ModalContentCommonProps & ({
23
+ coverImage?: never;
24
+ illustration?: ReactNodeNoStrings;
25
+ } | {
26
+ coverImage?: string;
27
+ illustration?: never;
28
+ });
23
29
  //#endregion
24
30
  export { ModalContentProps };
@@ -3,7 +3,7 @@ import { DataAttributeMap } from "../buildDataAttributes.mjs";
3
3
  import { BoxProps } from "../../Box/Box.mjs";
4
4
  import { ReactNode, Ref } from "react";
5
5
  //#region src/lib/components/private/Modal/ModalContent.d.ts
6
- interface ModalContentProps {
6
+ type ModalContentCommonProps = {
7
7
  id?: string;
8
8
  title: string;
9
9
  children: ReactNode;
@@ -11,13 +11,19 @@ interface ModalContentProps {
11
11
  closeLabel?: string;
12
12
  width: BoxProps['maxWidth'] | 'content';
13
13
  description?: ReactNodeNoStrings;
14
- illustration?: ReactNodeNoStrings;
15
14
  position: 'center' | 'right' | 'left';
16
15
  headingLevel: '2' | '3';
17
16
  scrollLock?: boolean;
18
17
  headingRef?: Ref<HTMLElement>;
19
18
  modalRef?: Ref<HTMLElement>;
20
19
  data?: DataAttributeMap;
21
- }
20
+ };
21
+ type ModalContentProps = ModalContentCommonProps & ({
22
+ coverImage?: never;
23
+ illustration?: ReactNodeNoStrings;
24
+ } | {
25
+ coverImage?: string;
26
+ illustration?: never;
27
+ });
22
28
  //#endregion
23
29
  export { ModalContentProps };
@@ -3,14 +3,18 @@ import { Box } from "../../Box/Box.mjs";
3
3
  import { Stack } from "../../Stack/Stack.mjs";
4
4
  import { useFallbackId } from "../../../hooks/useFallbackId.mjs";
5
5
  import { IconClear } from "../../icons/IconClear/IconClear.mjs";
6
+ import { Column } from "../../Column/Column.mjs";
7
+ import { Columns } from "../../Columns/Columns.mjs";
6
8
  import { Bleed } from "../../Bleed/Bleed.mjs";
7
9
  import { ButtonIcon } from "../../ButtonIcon/ButtonIcon.mjs";
8
10
  import { normalizeKey } from "../normalizeKey.mjs";
9
11
  import { Heading } from "../../Heading/Heading.mjs";
10
12
  import { pageBlockGutters } from "../../PageBlock/pageBlockGutters.mjs";
11
- import { closeIconOffset, headingRoot, maxSize, pointerEventsAll } from "./Modal.css.mjs";
12
- import { Fragment, forwardRef, useId, useRef } from "react";
13
- import { jsx, jsxs } from "react/jsx-runtime";
13
+ import { ScrollContainer } from "../ScrollContainer/ScrollContainer.mjs";
14
+ import { closeIconOffset, coverImage, coverImageHeightLimit, coverImageVar, headingRoot, hideOverflowAboveMobile, maxSize, pointerEventsAll } from "./Modal.css.mjs";
15
+ import { forwardRef, useId, useRef } from "react";
16
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
17
+ import { assignInlineVars } from "@vanilla-extract/dynamic";
14
18
  import { RemoveScroll } from "react-remove-scroll";
15
19
 
16
20
  //#region src/lib/components/private/Modal/ModalContent.tsx
@@ -18,48 +22,130 @@ const modalPadding = {
18
22
  mobile: "gutter",
19
23
  tablet: "large"
20
24
  };
21
- const ModalContentHeader = forwardRef(({ title, headingLevel, description, descriptionId, center }, ref) => /* @__PURE__ */ jsxs(Stack, {
22
- space: headingLevel === "2" ? {
23
- mobile: "small",
24
- tablet: "medium"
25
- } : "small",
26
- children: [/* @__PURE__ */ jsx(Heading, {
27
- level: headingLevel,
28
- align: center ? "center" : void 0,
25
+ const ModalContentHeader = forwardRef(({ title, headingLevel, description, descriptionId, illustration, reserveCloseArea }, ref) => {
26
+ const header = /* @__PURE__ */ jsxs(Stack, {
27
+ space: headingLevel === "2" ? {
28
+ mobile: "small",
29
+ tablet: "medium"
30
+ } : "small",
31
+ children: [/* @__PURE__ */ jsx(Heading, {
32
+ level: headingLevel,
33
+ align: illustration ? "center" : void 0,
34
+ children: /* @__PURE__ */ jsx(Box, {
35
+ ref,
36
+ tabIndex: -1,
37
+ component: "span",
38
+ position: "relative",
39
+ outline: "focus",
40
+ borderRadius: "small",
41
+ className: headingRoot,
42
+ children: title
43
+ })
44
+ }), description ? /* @__PURE__ */ jsx(Box, {
45
+ id: descriptionId,
46
+ children: description
47
+ }) : null]
48
+ });
49
+ const resolvedLayout = illustration ? /* @__PURE__ */ jsxs(Stack, {
50
+ space: "medium",
51
+ align: "center",
52
+ children: [/* @__PURE__ */ jsx(Box, {
53
+ paddingX: "gutter",
54
+ children: illustration
55
+ }), header]
56
+ }) : header;
57
+ return reserveCloseArea && !illustration ? /* @__PURE__ */ jsxs(Box, {
58
+ display: "flex",
59
+ children: [/* @__PURE__ */ jsx(Box, {
60
+ width: "full",
61
+ minWidth: 0,
62
+ children: resolvedLayout
63
+ }), /* @__PURE__ */ jsx(Box, {
64
+ width: "touchable",
65
+ flexShrink: 0,
66
+ flexGrow: 0
67
+ })]
68
+ }) : resolvedLayout;
69
+ });
70
+ const ModalContentScrollLayout = ({ children, applyCoverImageHeightLimit, contentStartRef, coverImageEnabled, applyPageBlockGutters, applyFullHeight }) => /* @__PURE__ */ jsxs(ScrollContainer, {
71
+ direction: "vertical",
72
+ children: [coverImageEnabled && /* @__PURE__ */ jsx("span", { ref: contentStartRef }), /* @__PURE__ */ jsx(Box, {
73
+ className: applyCoverImageHeightLimit ? coverImageHeightLimit : void 0,
74
+ height: applyFullHeight ? "full" : void 0,
29
75
  children: /* @__PURE__ */ jsx(Box, {
30
- ref,
31
- tabIndex: -1,
32
- component: "span",
33
- position: "relative",
34
- outline: "focus",
35
- borderRadius: "small",
36
- className: headingRoot,
37
- children: title
76
+ display: "flex",
77
+ gap: "large",
78
+ flexDirection: "column",
79
+ height: applyFullHeight ? "full" : void 0,
80
+ paddingY: modalPadding,
81
+ paddingX: applyPageBlockGutters ? pageBlockGutters : modalPadding,
82
+ children
83
+ })
84
+ })]
85
+ });
86
+ const ModalCoverImageLayout = ({ width, image, children }) => {
87
+ const forceStack = width === "xsmall";
88
+ const coverImage$1 = /* @__PURE__ */ jsx(Box, {
89
+ position: "relative",
90
+ height: "full",
91
+ children: /* @__PURE__ */ jsx(Box, {
92
+ position: { tablet: forceStack ? void 0 : "absolute" },
93
+ inset: 0,
94
+ className: coverImage,
95
+ width: "full",
96
+ height: "full",
97
+ style: assignInlineVars({ [coverImageVar]: `url(${image})` })
38
98
  })
39
- }), description ? /* @__PURE__ */ jsx(Box, {
40
- id: descriptionId,
41
- children: description
42
- }) : null]
43
- }));
44
- const ModalContent = ({ id, children, description, onClose, width, closeLabel = "Close", illustration, title, headingRef: headingRefProp, modalRef: modalRefProp, scrollLock = true, position, headingLevel, data, ...restProps }) => {
99
+ });
100
+ return forceStack ? /* @__PURE__ */ jsxs(Stack, {
101
+ space: "none",
102
+ children: [coverImage$1, /* @__PURE__ */ jsx(Fragment$1, { children })]
103
+ }) : /* @__PURE__ */ jsxs(Columns, {
104
+ space: "none",
105
+ collapseBelow: "tablet",
106
+ reverse: true,
107
+ children: [/* @__PURE__ */ jsx(Column, { children: coverImage$1 }), /* @__PURE__ */ jsx(Column, { children })]
108
+ });
109
+ };
110
+ const ModalContent = ({ id, children, description, onClose, width, closeLabel = "Close", illustration, coverImage, title, headingRef: headingRefProp, modalRef: modalRefProp, scrollLock = true, position, headingLevel, data, ...restProps }) => {
45
111
  const resolvedId = useFallbackId(id);
46
112
  const descriptionId = useId();
47
113
  const defaultModalRef = useRef(null);
48
114
  const modalRef = modalRefProp || defaultModalRef;
49
115
  const defaultHeadingRef = useRef(null);
50
116
  const headingRef = headingRefProp || defaultHeadingRef;
117
+ const contentStartRef = useRef(null);
51
118
  const handleEscape = (event) => {
52
119
  if (normalizeKey(event) === "Escape") {
53
120
  event.stopPropagation();
54
121
  onClose();
55
122
  }
56
123
  };
124
+ const isDrawer = position === "left" || position === "right";
125
+ const coverImageEnabled = Boolean(coverImage) && !isDrawer;
126
+ const allowColumnLayout = coverImageEnabled && width !== "xsmall";
57
127
  const justifyContent = {
58
128
  left: "flexStart",
59
129
  center: "center",
60
130
  right: "flexEnd"
61
131
  }[position];
62
- const modalRadius = position === "center" ? "xlarge" : void 0;
132
+ const modalRadius = !isDrawer ? "xlarge" : void 0;
133
+ const modalLayout = /* @__PURE__ */ jsxs(ModalContentScrollLayout, {
134
+ applyCoverImageHeightLimit: allowColumnLayout,
135
+ applyPageBlockGutters: isDrawer,
136
+ applyFullHeight: isDrawer,
137
+ contentStartRef,
138
+ coverImageEnabled,
139
+ children: [/* @__PURE__ */ jsx(ModalContentHeader, {
140
+ title,
141
+ headingLevel,
142
+ description,
143
+ descriptionId,
144
+ illustration: illustration && !coverImageEnabled ? illustration : void 0,
145
+ ref: headingRef,
146
+ reserveCloseArea: true
147
+ }), children]
148
+ });
63
149
  return /* @__PURE__ */ jsx(Box, {
64
150
  role: "dialog",
65
151
  "aria-label": title,
@@ -69,7 +155,7 @@ const ModalContent = ({ id, children, description, onClose, width, closeLabel =
69
155
  onKeyDown: handleEscape,
70
156
  position: "relative",
71
157
  width: "full",
72
- height: "full",
158
+ height: isDrawer ? "full" : void 0,
73
159
  display: "flex",
74
160
  alignItems: "center",
75
161
  textAlign: "left",
@@ -80,8 +166,8 @@ const ModalContent = ({ id, children, description, onClose, width, closeLabel =
80
166
  display: "flex",
81
167
  alignItems: "center",
82
168
  justifyContent,
83
- height: position === "right" || position === "left" ? "full" : void 0,
84
- overflow: position !== "center" ? "hidden" : void 0,
169
+ height: isDrawer ? "full" : void 0,
170
+ overflow: "hidden",
85
171
  boxShadow: "large",
86
172
  borderRadius: modalRadius,
87
173
  width: width !== "content" ? "full" : void 0,
@@ -90,7 +176,7 @@ const ModalContent = ({ id, children, description, onClose, width, closeLabel =
90
176
  noRelative: true,
91
177
  forwardProps: true,
92
178
  enabled: scrollLock,
93
- children: /* @__PURE__ */ jsxs(Box, {
179
+ children: /* @__PURE__ */ jsx(Box, {
94
180
  display: "flex",
95
181
  gap: "large",
96
182
  flexDirection: "column",
@@ -99,47 +185,25 @@ const ModalContent = ({ id, children, description, onClose, width, closeLabel =
99
185
  overflow: "auto",
100
186
  position: "relative",
101
187
  width: width !== "content" ? "full" : void 0,
102
- height: position === "right" || position === "left" ? "full" : void 0,
103
- paddingY: modalPadding,
104
- paddingX: position !== "center" ? pageBlockGutters : modalPadding,
105
- className: [pointerEventsAll, position === "center" && maxSize[position]],
188
+ height: isDrawer ? "full" : void 0,
189
+ className: {
190
+ [pointerEventsAll]: true,
191
+ [maxSize[position]]: !isDrawer,
192
+ [hideOverflowAboveMobile]: allowColumnLayout
193
+ },
106
194
  ...buildDataAttributes_default({
107
195
  data,
108
196
  validateRestProps: restProps
109
197
  }),
110
- children: [illustration ? /* @__PURE__ */ jsxs(Stack, {
111
- space: "medium",
112
- align: "center",
113
- children: [/* @__PURE__ */ jsx(Box, {
114
- paddingX: "gutter",
115
- children: illustration
116
- }), /* @__PURE__ */ jsx(ModalContentHeader, {
117
- title,
118
- headingLevel,
119
- description,
120
- descriptionId,
121
- center: Boolean(illustration),
122
- ref: headingRef
123
- })]
124
- }) : /* @__PURE__ */ jsxs(Box, {
125
- display: "flex",
126
- children: [/* @__PURE__ */ jsx(Box, {
127
- width: "full",
128
- minWidth: 0,
129
- children: /* @__PURE__ */ jsx(ModalContentHeader, {
130
- title,
131
- headingLevel,
132
- description,
133
- descriptionId,
134
- center: Boolean(illustration),
135
- ref: headingRef
136
- })
137
- }), /* @__PURE__ */ jsx(Box, {
138
- width: "touchable",
139
- flexShrink: 0,
140
- flexGrow: 0
141
- })]
142
- }), /* @__PURE__ */ jsx(Fragment, { children })]
198
+ children: /* @__PURE__ */ jsx(ScrollContainer, {
199
+ direction: "vertical",
200
+ startRef: contentStartRef,
201
+ children: coverImageEnabled && coverImage ? /* @__PURE__ */ jsx(ModalCoverImageLayout, {
202
+ width,
203
+ image: coverImage,
204
+ children: modalLayout
205
+ }) : modalLayout
206
+ })
143
207
  })
144
208
  }), /* @__PURE__ */ jsx(Box, {
145
209
  position: "absolute",
@@ -150,10 +214,11 @@ const ModalContent = ({ id, children, description, onClose, width, closeLabel =
150
214
  display: "flex",
151
215
  justifyContent: "flexEnd",
152
216
  paddingTop: modalPadding,
153
- paddingRight: position !== "center" ? pageBlockGutters : modalPadding,
154
- className: position === "center" && maxSize[position],
217
+ paddingRight: isDrawer ? pageBlockGutters : modalPadding,
218
+ className: !isDrawer && maxSize[position],
155
219
  children: /* @__PURE__ */ jsx(Bleed, {
156
220
  space: "xsmall",
221
+ horizontal: "xxsmall",
157
222
  children: /* @__PURE__ */ jsx(Box, {
158
223
  position: "relative",
159
224
  background: "surface",
@@ -164,7 +229,6 @@ const ModalContent = ({ id, children, description, onClose, width, closeLabel =
164
229
  label: closeLabel,
165
230
  icon: /* @__PURE__ */ jsx(IconClear, { tone: "secondary" }),
166
231
  variant: "transparent",
167
- size: "large",
168
232
  onClick: onClose
169
233
  })
170
234
  })
@@ -3,36 +3,55 @@ const require_buildDataAttributes = require('../buildDataAttributes.cjs');
3
3
  const require_Box = require('../../Box/Box.cjs');
4
4
  const require_useIsomorphicLayoutEffect = require('../../../hooks/useIsomorphicLayoutEffect.cjs');
5
5
  const require_ScrollContainer_css = require('./ScrollContainer.css.cjs');
6
+ const require_scrollbars_css = require('../scrollbars.css.cjs');
6
7
  let react = require("react");
7
8
  let react_jsx_runtime = require("react/jsx-runtime");
8
9
  let throttle_debounce = require("throttle-debounce");
9
10
 
10
11
  //#region src/lib/components/private/ScrollContainer/ScrollContainer.tsx
11
12
  const scrollOffset = 2;
12
- const maskOverflow = (element, direction) => setTimeout(() => {
13
+ const maskOverflow = ({ element, direction, gateStart }) => setTimeout(() => {
13
14
  const atTop = element.scrollTop <= 0;
14
15
  const atBottom = element.scrollHeight - element.offsetHeight - element.scrollTop < scrollOffset;
15
16
  const atLeft = element.scrollLeft <= 0;
16
17
  const atRight = element.scrollWidth - element.offsetWidth - element.scrollLeft < scrollOffset;
17
18
  if (direction === "vertical" || direction === "all") {
18
- element.classList[atTop ? "remove" : "add"](require_ScrollContainer_css.maskTop);
19
+ element.classList[atTop || gateStart ? "remove" : "add"](require_ScrollContainer_css.maskTop);
19
20
  element.classList[atBottom ? "remove" : "add"](require_ScrollContainer_css.maskBottom);
20
21
  }
21
22
  if (direction === "horizontal" || direction === "all") {
22
- element.classList[atLeft ? "remove" : "add"](require_ScrollContainer_css.maskLeft);
23
+ element.classList[atLeft || gateStart ? "remove" : "add"](require_ScrollContainer_css.maskLeft);
23
24
  element.classList[atRight ? "remove" : "add"](require_ScrollContainer_css.maskRight);
24
25
  }
25
26
  });
26
- const ScrollContainer = ({ children, direction: direction$1 = "horizontal", fadeSize: fadeSize$1 = "medium", hideScrollbar: hideScrollbar$1 = false, data, ...restProps }) => {
27
+ const ScrollContainer = ({ children, direction: direction$1 = "horizontal", fadeSize: fadeSize$1 = "medium", hideScrollbar: hideScrollbar$1 = false, startRef, data, ...restProps }) => {
27
28
  const containerRef = (0, react.useRef)(null);
29
+ const gateStartRef = (0, react.useRef)(Boolean(startRef?.current));
28
30
  const updateMask = (0, throttle_debounce.throttle)(100, (0, react.useCallback)(() => {
29
- if (containerRef.current) maskOverflow(containerRef.current, direction$1);
31
+ if (containerRef.current) maskOverflow({
32
+ element: containerRef.current,
33
+ direction: direction$1,
34
+ gateStart: gateStartRef.current
35
+ });
30
36
  }, [containerRef, direction$1]));
31
37
  require_useIsomorphicLayoutEffect.useIsomorphicLayoutEffect(() => {
32
38
  if (containerRef.current) updateMask();
33
39
  window.addEventListener("resize", updateMask);
34
40
  return () => window.removeEventListener("resize", updateMask);
35
41
  }, [updateMask]);
42
+ require_useIsomorphicLayoutEffect.useIsomorphicLayoutEffect(() => {
43
+ if (!startRef?.current || !containerRef.current) return;
44
+ const observer = new IntersectionObserver(([entry]) => {
45
+ if (!entry.rootBounds) return;
46
+ gateStartRef.current = !(direction$1 === "horizontal" || direction$1 === "all" ? entry.boundingClientRect.left < entry.rootBounds.left : entry.boundingClientRect.top < entry.rootBounds.top);
47
+ updateMask();
48
+ }, {
49
+ root: containerRef.current,
50
+ threshold: [0, 1]
51
+ });
52
+ observer.observe(startRef.current);
53
+ return () => observer.disconnect();
54
+ }, [startRef, updateMask]);
36
55
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Box.Box, {
37
56
  ref: containerRef,
38
57
  onScroll: updateMask,
@@ -41,7 +60,7 @@ const ScrollContainer = ({ children, direction: direction$1 = "horizontal", fade
41
60
  className: [
42
61
  require_ScrollContainer_css.container,
43
62
  require_ScrollContainer_css.mask,
44
- hideScrollbar$1 ? require_ScrollContainer_css.hideScrollbar : null,
63
+ hideScrollbar$1 ? require_ScrollContainer_css.hideScrollbar : require_scrollbars_css.scrollbars,
45
64
  require_ScrollContainer_css.fadeSize[fadeSize$1],
46
65
  require_ScrollContainer_css.direction[direction$1]
47
66
  ],
@@ -39,12 +39,20 @@ const left = (0, _vanilla_extract_css.createVar)("left");
39
39
  const right = (0, _vanilla_extract_css.createVar)("right");
40
40
  const top = (0, _vanilla_extract_css.createVar)("top");
41
41
  const bottom = (0, _vanilla_extract_css.createVar)("bottom");
42
- const mask = (0, _vanilla_extract_css.style)({ maskImage: [
43
- `linear-gradient(to bottom, transparent 0, black ${(0, _vanilla_extract_css.fallbackVar)(top, "0")})`,
44
- `linear-gradient(to right, transparent 0, black ${(0, _vanilla_extract_css.fallbackVar)(left, "0")})`,
45
- `linear-gradient(to left, transparent 0, black ${(0, _vanilla_extract_css.fallbackVar)(right, "0")})`,
46
- `linear-gradient(to top, transparent 0, black ${(0, _vanilla_extract_css.fallbackVar)(bottom, "0")})`
47
- ].join(",") }, "mask");
42
+ const mask = (0, _vanilla_extract_css.style)({
43
+ vars: {
44
+ [left]: "0",
45
+ [right]: "0",
46
+ [top]: "0",
47
+ [bottom]: "0"
48
+ },
49
+ maskImage: [
50
+ `linear-gradient(to bottom, transparent 0, black ${top})`,
51
+ `linear-gradient(to right, transparent 0, black ${left})`,
52
+ `linear-gradient(to left, transparent 0, black ${right})`,
53
+ `linear-gradient(to top, transparent 0, black ${bottom})`
54
+ ].join(",")
55
+ }, "mask");
48
56
  const maskLeft = (0, _vanilla_extract_css.style)({ vars: { [left]: scrollOverlaySize } }, "maskLeft");
49
57
  const maskRight = (0, _vanilla_extract_css.style)({ vars: { [right]: scrollOverlaySize } }, "maskRight");
50
58
  const maskTop = (0, _vanilla_extract_css.style)({ vars: { [top]: scrollOverlaySize } }, "maskTop");
@@ -1,5 +1,5 @@
1
1
  import { endFileScope, setFileScope } from "@vanilla-extract/css/fileScope";
2
- import { createVar, fallbackVar, style, styleVariants } from "@vanilla-extract/css";
2
+ import { createVar, style, styleVariants } from "@vanilla-extract/css";
3
3
 
4
4
  //#region src/lib/components/private/ScrollContainer/ScrollContainer.css.ts
5
5
  setFileScope("src/lib/components/private/ScrollContainer/ScrollContainer.css.ts", "braid-design-system");
@@ -38,12 +38,20 @@ const left = createVar("left");
38
38
  const right = createVar("right");
39
39
  const top = createVar("top");
40
40
  const bottom = createVar("bottom");
41
- const mask = style({ maskImage: [
42
- `linear-gradient(to bottom, transparent 0, black ${fallbackVar(top, "0")})`,
43
- `linear-gradient(to right, transparent 0, black ${fallbackVar(left, "0")})`,
44
- `linear-gradient(to left, transparent 0, black ${fallbackVar(right, "0")})`,
45
- `linear-gradient(to top, transparent 0, black ${fallbackVar(bottom, "0")})`
46
- ].join(",") }, "mask");
41
+ const mask = style({
42
+ vars: {
43
+ [left]: "0",
44
+ [right]: "0",
45
+ [top]: "0",
46
+ [bottom]: "0"
47
+ },
48
+ maskImage: [
49
+ `linear-gradient(to bottom, transparent 0, black ${top})`,
50
+ `linear-gradient(to right, transparent 0, black ${left})`,
51
+ `linear-gradient(to left, transparent 0, black ${right})`,
52
+ `linear-gradient(to top, transparent 0, black ${bottom})`
53
+ ].join(",")
54
+ }, "mask");
47
55
  const maskLeft = style({ vars: { [left]: scrollOverlaySize } }, "maskLeft");
48
56
  const maskRight = style({ vars: { [right]: scrollOverlaySize } }, "maskRight");
49
57
  const maskTop = style({ vars: { [top]: scrollOverlaySize } }, "maskTop");
@@ -2,36 +2,55 @@ import buildDataAttributes_default from "../buildDataAttributes.mjs";
2
2
  import { Box } from "../../Box/Box.mjs";
3
3
  import { useIsomorphicLayoutEffect } from "../../../hooks/useIsomorphicLayoutEffect.mjs";
4
4
  import { container, direction, fadeSize, hideScrollbar, mask, maskBottom, maskLeft, maskRight, maskTop } from "./ScrollContainer.css.mjs";
5
+ import { scrollbars } from "../scrollbars.css.mjs";
5
6
  import { useCallback, useRef } from "react";
6
7
  import { jsx } from "react/jsx-runtime";
7
8
  import { throttle } from "throttle-debounce";
8
9
 
9
10
  //#region src/lib/components/private/ScrollContainer/ScrollContainer.tsx
10
11
  const scrollOffset = 2;
11
- const maskOverflow = (element, direction) => setTimeout(() => {
12
+ const maskOverflow = ({ element, direction, gateStart }) => setTimeout(() => {
12
13
  const atTop = element.scrollTop <= 0;
13
14
  const atBottom = element.scrollHeight - element.offsetHeight - element.scrollTop < scrollOffset;
14
15
  const atLeft = element.scrollLeft <= 0;
15
16
  const atRight = element.scrollWidth - element.offsetWidth - element.scrollLeft < scrollOffset;
16
17
  if (direction === "vertical" || direction === "all") {
17
- element.classList[atTop ? "remove" : "add"](maskTop);
18
+ element.classList[atTop || gateStart ? "remove" : "add"](maskTop);
18
19
  element.classList[atBottom ? "remove" : "add"](maskBottom);
19
20
  }
20
21
  if (direction === "horizontal" || direction === "all") {
21
- element.classList[atLeft ? "remove" : "add"](maskLeft);
22
+ element.classList[atLeft || gateStart ? "remove" : "add"](maskLeft);
22
23
  element.classList[atRight ? "remove" : "add"](maskRight);
23
24
  }
24
25
  });
25
- const ScrollContainer = ({ children, direction: direction$1 = "horizontal", fadeSize: fadeSize$1 = "medium", hideScrollbar: hideScrollbar$1 = false, data, ...restProps }) => {
26
+ const ScrollContainer = ({ children, direction: direction$1 = "horizontal", fadeSize: fadeSize$1 = "medium", hideScrollbar: hideScrollbar$1 = false, startRef, data, ...restProps }) => {
26
27
  const containerRef = useRef(null);
28
+ const gateStartRef = useRef(Boolean(startRef?.current));
27
29
  const updateMask = throttle(100, useCallback(() => {
28
- if (containerRef.current) maskOverflow(containerRef.current, direction$1);
30
+ if (containerRef.current) maskOverflow({
31
+ element: containerRef.current,
32
+ direction: direction$1,
33
+ gateStart: gateStartRef.current
34
+ });
29
35
  }, [containerRef, direction$1]));
30
36
  useIsomorphicLayoutEffect(() => {
31
37
  if (containerRef.current) updateMask();
32
38
  window.addEventListener("resize", updateMask);
33
39
  return () => window.removeEventListener("resize", updateMask);
34
40
  }, [updateMask]);
41
+ useIsomorphicLayoutEffect(() => {
42
+ if (!startRef?.current || !containerRef.current) return;
43
+ const observer = new IntersectionObserver(([entry]) => {
44
+ if (!entry.rootBounds) return;
45
+ gateStartRef.current = !(direction$1 === "horizontal" || direction$1 === "all" ? entry.boundingClientRect.left < entry.rootBounds.left : entry.boundingClientRect.top < entry.rootBounds.top);
46
+ updateMask();
47
+ }, {
48
+ root: containerRef.current,
49
+ threshold: [0, 1]
50
+ });
51
+ observer.observe(startRef.current);
52
+ return () => observer.disconnect();
53
+ }, [startRef, updateMask]);
35
54
  return /* @__PURE__ */ jsx(Box, {
36
55
  ref: containerRef,
37
56
  onScroll: updateMask,
@@ -40,7 +59,7 @@ const ScrollContainer = ({ children, direction: direction$1 = "horizontal", fade
40
59
  className: [
41
60
  container,
42
61
  mask,
43
- hideScrollbar$1 ? hideScrollbar : null,
62
+ hideScrollbar$1 ? hideScrollbar : scrollbars,
44
63
  fadeSize[fadeSize$1],
45
64
  direction[direction$1]
46
65
  ],
@@ -0,0 +1,27 @@
1
+ const require_runtime = require('../../../_virtual/_rolldown/runtime.cjs');
2
+ const require_colorModeStyle = require('../../css/colorModeStyle.cjs');
3
+ let _vanilla_extract_css_fileScope = require("@vanilla-extract/css/fileScope");
4
+ let _vanilla_extract_css = require("@vanilla-extract/css");
5
+
6
+ //#region src/lib/components/private/scrollbars.css.ts
7
+ (0, _vanilla_extract_css_fileScope.setFileScope)("src/lib/components/private/scrollbars.css.ts", "braid-design-system");
8
+ const thumbColor = (0, _vanilla_extract_css.createVar)("thumbColor");
9
+ const trackColor = "transparent";
10
+ const scrollbars = (0, _vanilla_extract_css.style)([require_colorModeStyle.colorModeStyle({
11
+ lightMode: { vars: { [thumbColor]: "rgba(0 0 0 / 0.4)" } },
12
+ darkMode: { vars: { [thumbColor]: "rgba(255 255 255 / 0.4)" } }
13
+ }), {
14
+ scrollbarColor: `${thumbColor} ${trackColor}`,
15
+ "@supports": { "not (scrollbar-color: auto)": { selectors: {
16
+ "&::-webkit-scrollbar": {
17
+ background: trackColor,
18
+ width: 8,
19
+ height: 8
20
+ },
21
+ "&::-webkit-scrollbar-thumb": { background: thumbColor }
22
+ } } }
23
+ }], "scrollbars");
24
+ (0, _vanilla_extract_css_fileScope.endFileScope)();
25
+
26
+ //#endregion
27
+ exports.scrollbars = scrollbars;
@@ -0,0 +1,26 @@
1
+ import { colorModeStyle } from "../../css/colorModeStyle.mjs";
2
+ import { endFileScope, setFileScope } from "@vanilla-extract/css/fileScope";
3
+ import { createVar, style } from "@vanilla-extract/css";
4
+
5
+ //#region src/lib/components/private/scrollbars.css.ts
6
+ setFileScope("src/lib/components/private/scrollbars.css.ts", "braid-design-system");
7
+ const thumbColor = createVar("thumbColor");
8
+ const trackColor = "transparent";
9
+ const scrollbars = style([colorModeStyle({
10
+ lightMode: { vars: { [thumbColor]: "rgba(0 0 0 / 0.4)" } },
11
+ darkMode: { vars: { [thumbColor]: "rgba(255 255 255 / 0.4)" } }
12
+ }), {
13
+ scrollbarColor: `${thumbColor} ${trackColor}`,
14
+ "@supports": { "not (scrollbar-color: auto)": { selectors: {
15
+ "&::-webkit-scrollbar": {
16
+ background: trackColor,
17
+ width: 8,
18
+ height: 8
19
+ },
20
+ "&::-webkit-scrollbar-thumb": { background: thumbColor }
21
+ } } }
22
+ }], "scrollbars");
23
+ endFileScope();
24
+
25
+ //#endregion
26
+ export { scrollbars };
@@ -0,0 +1,16 @@
1
+
2
+ //#region src/lib/playroom/snippets/BrandedContainer.ts
3
+ const snippets = [{
4
+ group: "Sections",
5
+ name: "Branded container",
6
+ description: "Full bleed",
7
+ code: "<Box background=\"brand\" paddingY=\"xlarge\">\n <PageBlock width=\"medium\">\n <Stack space=\"large\">\n <Heading level=\"2\">Heading</Heading>\n\n <Text>\n Combines a <Strong>Heading level 2</Strong> with a{\" \"}\n <Strong>large Stack</Strong> spacing content groups within the section.\n <br />\n Uses a <Strong>PageBlock</Strong> inside the section to maintain screen\n gutters on small screens.\n </Text>\n\n <Text>\n Lorem ipsum dolor sit amet, consectetur adipiscing elit. In convallis\n cursus quam nec volutpat. In hac habitasse platea dictumst. Praesent\n egestas erat id mollis imperdiet. Vestibulum non commodo nisi, sed\n tempus magna. Duis a malesuada diam.\n </Text>\n </Stack>\n </PageBlock>\n</Box>"
8
+ }, {
9
+ group: "Sections",
10
+ name: "Branded container",
11
+ description: "Rounded from desktop (full bleed below)",
12
+ code: "<Box paddingX={{ desktop: \"gutter\" }}>\n <ContentBlock width=\"large\">\n <Box\n background=\"brand\"\n paddingY=\"xlarge\"\n borderRadius={{ desktop: \"xlarge\" }}\n >\n <PageBlock width=\"medium\">\n <Stack space=\"large\">\n <Heading level=\"2\">Heading</Heading>\n\n <Text>\n Combines a <Strong>Heading level 2</Strong> with a{\" \"}\n <Strong>large Stack</Strong> spacing content groups within the\n section.\n <br />\n Uses a <Strong>PageBlock</Strong> inside the section to maintain\n screen gutters on small screens.\n </Text>\n\n <Text>\n Lorem ipsum dolor sit amet, consectetur adipiscing elit. In\n convallis cursus quam nec volutpat. In hac habitasse platea\n dictumst. Praesent egestas erat id mollis imperdiet. Vestibulum non\n commodo nisi, sed tempus magna. Duis a malesuada diam.\n </Text>\n </Stack>\n </PageBlock>\n </Box>\n </ContentBlock>\n</Box>"
13
+ }];
14
+
15
+ //#endregion
16
+ exports.snippets = snippets;
@@ -0,0 +1,15 @@
1
+ //#region src/lib/playroom/snippets/BrandedContainer.ts
2
+ const snippets = [{
3
+ group: "Sections",
4
+ name: "Branded container",
5
+ description: "Full bleed",
6
+ code: "<Box background=\"brand\" paddingY=\"xlarge\">\n <PageBlock width=\"medium\">\n <Stack space=\"large\">\n <Heading level=\"2\">Heading</Heading>\n\n <Text>\n Combines a <Strong>Heading level 2</Strong> with a{\" \"}\n <Strong>large Stack</Strong> spacing content groups within the section.\n <br />\n Uses a <Strong>PageBlock</Strong> inside the section to maintain screen\n gutters on small screens.\n </Text>\n\n <Text>\n Lorem ipsum dolor sit amet, consectetur adipiscing elit. In convallis\n cursus quam nec volutpat. In hac habitasse platea dictumst. Praesent\n egestas erat id mollis imperdiet. Vestibulum non commodo nisi, sed\n tempus magna. Duis a malesuada diam.\n </Text>\n </Stack>\n </PageBlock>\n</Box>"
7
+ }, {
8
+ group: "Sections",
9
+ name: "Branded container",
10
+ description: "Rounded from desktop (full bleed below)",
11
+ code: "<Box paddingX={{ desktop: \"gutter\" }}>\n <ContentBlock width=\"large\">\n <Box\n background=\"brand\"\n paddingY=\"xlarge\"\n borderRadius={{ desktop: \"xlarge\" }}\n >\n <PageBlock width=\"medium\">\n <Stack space=\"large\">\n <Heading level=\"2\">Heading</Heading>\n\n <Text>\n Combines a <Strong>Heading level 2</Strong> with a{\" \"}\n <Strong>large Stack</Strong> spacing content groups within the\n section.\n <br />\n Uses a <Strong>PageBlock</Strong> inside the section to maintain\n screen gutters on small screens.\n </Text>\n\n <Text>\n Lorem ipsum dolor sit amet, consectetur adipiscing elit. In\n convallis cursus quam nec volutpat. In hac habitasse platea\n dictumst. Praesent egestas erat id mollis imperdiet. Vestibulum non\n commodo nisi, sed tempus magna. Duis a malesuada diam.\n </Text>\n </Stack>\n </PageBlock>\n </Box>\n </ContentBlock>\n</Box>"
12
+ }];
13
+
14
+ //#endregion
15
+ export { snippets };