@dxos/react-ui-stack 0.8.4-main.a4bbb77 → 0.8.4-main.ae835ea

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 (35) hide show
  1. package/dist/lib/browser/{chunk-K76XHALI.mjs → chunk-T4ZCIFCF.mjs} +84 -56
  2. package/dist/lib/browser/chunk-T4ZCIFCF.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +5 -1
  4. package/dist/lib/browser/meta.json +1 -1
  5. package/dist/lib/browser/playwright/index.mjs +10 -23
  6. package/dist/lib/browser/playwright/index.mjs.map +2 -2
  7. package/dist/lib/browser/testing/index.mjs +1 -1
  8. package/dist/lib/node-esm/{chunk-2UOARV5Q.mjs → chunk-G2QYUH52.mjs} +84 -56
  9. package/dist/lib/node-esm/chunk-G2QYUH52.mjs.map +7 -0
  10. package/dist/lib/node-esm/index.mjs +5 -1
  11. package/dist/lib/node-esm/meta.json +1 -1
  12. package/dist/lib/node-esm/playwright/index.mjs +10 -23
  13. package/dist/lib/node-esm/playwright/index.mjs.map +2 -2
  14. package/dist/lib/node-esm/testing/index.mjs +1 -1
  15. package/dist/types/src/components/Image/Image.d.ts +5 -2
  16. package/dist/types/src/components/Image/Image.d.ts.map +1 -1
  17. package/dist/types/src/components/Image/Image.stories.d.ts +2 -0
  18. package/dist/types/src/components/Image/Image.stories.d.ts.map +1 -1
  19. package/dist/types/src/components/StackItem/StackItem.d.ts +3 -2
  20. package/dist/types/src/components/StackItem/StackItem.d.ts.map +1 -1
  21. package/dist/types/src/components/StackItem/StackItemContent.d.ts +20 -10
  22. package/dist/types/src/components/StackItem/StackItemContent.d.ts.map +1 -1
  23. package/dist/types/src/components/StackItem/StackItemHeading.d.ts.map +1 -1
  24. package/dist/types/src/exemplars/Card/Card.d.ts +15 -4
  25. package/dist/types/src/exemplars/Card/Card.d.ts.map +1 -1
  26. package/dist/types/tsconfig.tsbuildinfo +1 -1
  27. package/package.json +22 -22
  28. package/src/components/Image/Image.stories.tsx +25 -3
  29. package/src/components/Image/Image.tsx +121 -66
  30. package/src/components/StackItem/StackItem.stories.tsx +1 -1
  31. package/src/components/StackItem/StackItemContent.tsx +19 -8
  32. package/src/components/StackItem/StackItemHeading.tsx +1 -5
  33. package/src/exemplars/Card/Card.tsx +20 -2
  34. package/dist/lib/browser/chunk-K76XHALI.mjs.map +0 -7
  35. package/dist/lib/node-esm/chunk-2UOARV5Q.mjs.map +0 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-stack",
3
- "version": "0.8.4-main.a4bbb77",
3
+ "version": "0.8.4-main.ae835ea",
4
4
  "description": "A stack component.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -54,36 +54,36 @@
54
54
  "@radix-ui/react-slot": "1.1.2",
55
55
  "@radix-ui/react-use-controllable-state": "1.1.0",
56
56
  "react-resize-detector": "^11.0.1",
57
- "@dxos/keyboard": "0.8.4-main.a4bbb77",
58
- "@dxos/live-object": "0.8.4-main.a4bbb77",
59
- "@dxos/react-ui-attention": "0.8.4-main.a4bbb77",
60
- "@dxos/react-ui-dnd": "0.8.4-main.a4bbb77",
61
- "@dxos/storybook-utils": "0.8.4-main.a4bbb77",
62
- "@dxos/util": "0.8.4-main.a4bbb77",
63
- "@dxos/echo-schema": "0.8.4-main.a4bbb77"
57
+ "@dxos/echo": "0.8.4-main.ae835ea",
58
+ "@dxos/keyboard": "0.8.4-main.ae835ea",
59
+ "@dxos/live-object": "0.8.4-main.ae835ea",
60
+ "@dxos/react-ui-attention": "0.8.4-main.ae835ea",
61
+ "@dxos/storybook-utils": "0.8.4-main.ae835ea",
62
+ "@dxos/util": "0.8.4-main.ae835ea",
63
+ "@dxos/react-ui-dnd": "0.8.4-main.ae835ea"
64
64
  },
65
65
  "devDependencies": {
66
- "@types/react": "~19.2.0",
67
- "@types/react-dom": "~19.2.0",
66
+ "@types/react": "~19.2.2",
67
+ "@types/react-dom": "~19.2.2",
68
68
  "react": "~19.2.0",
69
69
  "react-dom": "~19.2.0",
70
70
  "vite": "7.1.9",
71
- "@dxos/app-graph": "0.8.4-main.a4bbb77",
72
- "@dxos/client": "0.8.4-main.a4bbb77",
73
- "@dxos/react-ui": "0.8.4-main.a4bbb77",
74
- "@dxos/react-ui-theme": "0.8.4-main.a4bbb77",
75
- "@dxos/storybook-utils": "0.8.4-main.a4bbb77",
76
- "@dxos/test-utils": "0.8.4-main.a4bbb77",
77
- "@dxos/random": "0.8.4-main.a4bbb77",
78
- "@dxos/echo-schema": "0.8.4-main.a4bbb77"
71
+ "@dxos/client": "0.8.4-main.ae835ea",
72
+ "@dxos/echo": "0.8.4-main.ae835ea",
73
+ "@dxos/random": "0.8.4-main.ae835ea",
74
+ "@dxos/react-ui": "0.8.4-main.ae835ea",
75
+ "@dxos/react-ui-theme": "0.8.4-main.ae835ea",
76
+ "@dxos/storybook-utils": "0.8.4-main.ae835ea",
77
+ "@dxos/test-utils": "0.8.4-main.ae835ea",
78
+ "@dxos/app-graph": "0.8.4-main.ae835ea"
79
79
  },
80
80
  "peerDependencies": {
81
81
  "react": "^19.0.0",
82
82
  "react-dom": "^19.0.0",
83
- "@dxos/client": "0.8.4-main.a4bbb77",
84
- "@dxos/random": "0.8.4-main.a4bbb77",
85
- "@dxos/react-ui": "0.8.4-main.a4bbb77",
86
- "@dxos/react-ui-theme": "0.8.4-main.a4bbb77"
83
+ "@dxos/client": "0.8.4-main.ae835ea",
84
+ "@dxos/react-ui": "0.8.4-main.ae835ea",
85
+ "@dxos/random": "0.8.4-main.ae835ea",
86
+ "@dxos/react-ui-theme": "0.8.4-main.ae835ea"
87
87
  },
88
88
  "publishConfig": {
89
89
  "access": "public"
@@ -3,14 +3,16 @@
3
3
  //
4
4
 
5
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
6
- import React from 'react';
6
+ import React, { useMemo } from 'react';
7
7
 
8
8
  import { faker } from '@dxos/random';
9
9
  import { withTheme } from '@dxos/react-ui/testing';
10
10
 
11
11
  import { Image } from './Image';
12
12
 
13
- faker.seed(1);
13
+ const seed = Math.random();
14
+
15
+ faker.seed(seed);
14
16
 
15
17
  const meta = {
16
18
  title: 'ui/react-ui-stack/Image',
@@ -22,7 +24,7 @@ const meta = {
22
24
  ),
23
25
  decorators: [withTheme],
24
26
  parameters: {
25
- layout: 'fullscreen',
27
+ layout: 'centered',
26
28
  },
27
29
  } satisfies Meta<typeof Image>;
28
30
 
@@ -54,3 +56,23 @@ export const SVG: Story = {
54
56
  classNames: 'w-[20rem]',
55
57
  },
56
58
  };
59
+
60
+ export const Many: Story = {
61
+ args: {
62
+ src: 'https://dxos.network/bg-kube.svg',
63
+ },
64
+ render: () => {
65
+ const images = useMemo(
66
+ () => Array.from({ length: 9 }, (_, i) => `https://picsum.photos/seed/${seed + i}/500/500`),
67
+ [],
68
+ );
69
+ console.log(images);
70
+ return (
71
+ <div className='is-[60rem] grid grid-cols-3 grid-rows-3 gap-8'>
72
+ {images.map((src, i) => (
73
+ <Image key={i} src={src} classNames='is-[18rem] bs-[12rem]' />
74
+ ))}
75
+ </div>
76
+ );
77
+ },
78
+ };
@@ -7,13 +7,13 @@ import React, { type SyntheticEvent, useRef, useState } from 'react';
7
7
  import { type ThemedClassName } from '@dxos/react-ui';
8
8
  import { mx } from '@dxos/react-ui-theme';
9
9
 
10
- export type ImageProps = ThemedClassName<{
11
- src: string;
12
- alt?: string;
13
- crossOrigin?: 'anonymous' | 'use-credentials' | '';
14
- sampleSize?: number;
15
- contrast?: number;
16
- }>;
10
+ export type ImageProps = ThemedClassName<
11
+ {
12
+ src: string;
13
+ alt?: string;
14
+ crossOrigin?: 'anonymous' | 'use-credentials' | '';
15
+ } & ColorOptions
16
+ >;
17
17
 
18
18
  export const Image = ({
19
19
  classNames,
@@ -28,74 +28,26 @@ export const Image = ({
28
28
  const [imageLoaded, setImageLoaded] = useState<boolean>(false);
29
29
  const canvasRef = useRef<HTMLCanvasElement>(null);
30
30
 
31
- // TODO(burdon): Cache?
32
- const extractDominantColor = (img: HTMLImageElement): void => {
33
- const canvas = canvasRef.current;
34
- const ctx = canvas?.getContext('2d');
35
- if (!canvas || !ctx) {
31
+ // CORS not supported by server.
32
+ const handleImageError = (): void => {
33
+ setCrossOriginState(undefined);
34
+ };
35
+
36
+ const handleImageLoad = ({ target }: SyntheticEvent<HTMLImageElement>): void => {
37
+ const img = target as HTMLImageElement;
38
+ if (!canvasRef.current) {
36
39
  return;
37
40
  }
38
41
 
39
- // Draw the image scaled down.
40
- canvas.width = sampleSize;
41
- canvas.height = sampleSize;
42
- ctx.drawImage(img, 0, 0, sampleSize, sampleSize);
43
-
44
42
  try {
45
- // Get image data.
46
- const imageData = ctx.getImageData(0, 0, sampleSize, sampleSize);
47
- const pixels = imageData.data;
48
-
49
- // Calculate average color with more weight to vibrant colors.
50
- let r = 0;
51
- let g = 0;
52
- let b = 0;
53
- let totalWeight = 0;
54
- for (let i = 0; i < pixels.length; i += 4) {
55
- const red = pixels[i];
56
- const green = pixels[i + 1];
57
- const blue = pixels[i + 2];
58
- const alpha = pixels[i + 3];
59
-
60
- // Skip transparent pixels.
61
- if (alpha === 0) continue;
62
-
63
- // Calculate saturation to weight vibrant colors more.
64
- const max = Math.max(red, green, blue);
65
- const min = Math.min(red, green, blue);
66
- const saturation = max === 0 ? 0 : (max - min) / max;
67
- const weight = 1 + saturation * 2; // Give more weight to saturated colors.
68
-
69
- r += red * weight;
70
- g += green * weight;
71
- b += blue * weight;
72
- totalWeight += weight;
73
- }
74
-
75
- if (totalWeight > 0) {
76
- r = Math.round(r / totalWeight);
77
- g = Math.round(g / totalWeight);
78
- b = Math.round(b / totalWeight);
79
-
80
- // Slightly darken the color for better contrast.
81
- r = Math.round(r * contrast);
82
- g = Math.round(g * contrast);
83
- b = Math.round(b * contrast);
84
- setDominantColor(`rgb(${r}, ${g}, ${b})`);
43
+ const color = extractDominantColor(canvasRef.current, img, { sampleSize, contrast });
44
+ if (color) {
45
+ setDominantColor(`rgb(${color[0]}, ${color[1]}, ${color[2]})`);
85
46
  }
86
47
  } catch {
87
48
  setCrossOriginState(undefined);
88
49
  }
89
- };
90
50
 
91
- // CORS not supported by server.
92
- const handleImageError = (): void => {
93
- setCrossOriginState(undefined);
94
- };
95
-
96
- const handleImageLoad = (ev: SyntheticEvent<HTMLImageElement>): void => {
97
- const img = ev.target as HTMLImageElement;
98
- extractDominantColor(img);
99
51
  setImageLoaded(true);
100
52
  };
101
53
 
@@ -135,3 +87,106 @@ export const Image = ({
135
87
  </div>
136
88
  );
137
89
  };
90
+
91
+ type ColorOptions = {
92
+ sampleSize?: number;
93
+ contrast?: number;
94
+ };
95
+
96
+ // TODO(burdon): Cache?
97
+ const extractDominantColor = (
98
+ canvas: HTMLCanvasElement,
99
+ img: HTMLImageElement,
100
+ { sampleSize = 64, contrast = 0.95 }: ColorOptions,
101
+ ): [number, number, number] | null => {
102
+ const ctx = canvas.getContext('2d');
103
+ if (!ctx) {
104
+ return null;
105
+ }
106
+
107
+ // Draw the image scaled down.
108
+ canvas.width = sampleSize;
109
+ canvas.height = sampleSize;
110
+ ctx.drawImage(img, 0, 0, sampleSize, sampleSize);
111
+
112
+ // Get image data.
113
+ const imageData = ctx.getImageData(0, 0, sampleSize, sampleSize);
114
+ const pixels = imageData.data;
115
+
116
+ // Check for transparent background.
117
+ if (isTransparent(pixels, sampleSize)) {
118
+ return null;
119
+ }
120
+
121
+ let r = 0;
122
+ let g = 0;
123
+ let b = 0;
124
+ let totalWeight = 0;
125
+
126
+ // Calculate average color with more weight to vibrant colors.
127
+ for (let i = 0; i < pixels.length; i += 4) {
128
+ const red = pixels[i];
129
+ const green = pixels[i + 1];
130
+ const blue = pixels[i + 2];
131
+ const alpha = pixels[i + 3];
132
+
133
+ // Skip transparent pixels.
134
+ if (alpha === 0) continue;
135
+
136
+ // Calculate saturation to weight vibrant colors more.
137
+ const max = Math.max(red, green, blue);
138
+ const min = Math.min(red, green, blue);
139
+ // Give more weight to saturated colors.
140
+ const saturation = max === 0 ? 0 : (max - min) / max;
141
+ const weight = 1 + saturation * 2;
142
+
143
+ r += red * weight;
144
+ g += green * weight;
145
+ b += blue * weight;
146
+ totalWeight += weight;
147
+ }
148
+
149
+ if (totalWeight > 0) {
150
+ // Slightly darken the color for better contrast.
151
+ r = Math.round(Math.round(r / totalWeight) * contrast);
152
+ g = Math.round(Math.round(g / totalWeight) * contrast);
153
+ b = Math.round(Math.round(b / totalWeight) * contrast);
154
+ return [r, g, b];
155
+ }
156
+
157
+ return null;
158
+ };
159
+
160
+ /**
161
+ * Detects if an image has a transparent background by examining edge pixels.
162
+ * @param pixels - Image pixel data from canvas
163
+ * @param sampleSize - Size of the sampled image
164
+ * @param threshold - Percentage threshold for considering background transparent (default: 0.5)
165
+ * @returns True if the image has a transparent background
166
+ */
167
+ const isTransparent = (pixels: Uint8ClampedArray, sampleSize: number, threshold: number = 0.5): boolean => {
168
+ let edgeTransparentPixels = 0;
169
+ const edgePixels = sampleSize * 4 - 4; // Perimeter minus corners counted twice.
170
+
171
+ for (let x = 0; x < sampleSize; x++) {
172
+ // Top edge.
173
+ const topIndex = x * 4;
174
+ if (pixels[topIndex + 3] === 0) edgeTransparentPixels++;
175
+
176
+ // Bottom edge.
177
+ const bottomIndex = ((sampleSize - 1) * sampleSize + x) * 4;
178
+ if (pixels[bottomIndex + 3] === 0) edgeTransparentPixels++;
179
+ }
180
+
181
+ for (let y = 1; y < sampleSize - 1; y++) {
182
+ // Left edge.
183
+ const leftIndex = y * sampleSize * 4;
184
+ if (pixels[leftIndex + 3] === 0) edgeTransparentPixels++;
185
+
186
+ // Right edge.
187
+ const rightIndex = (y * sampleSize + sampleSize - 1) * 4;
188
+ if (pixels[rightIndex + 3] === 0) edgeTransparentPixels++;
189
+ }
190
+
191
+ return edgeTransparentPixels / edgePixels > threshold;
192
+ };
@@ -25,7 +25,7 @@ const DefaultStory = (props: StackItemRootProps) => {
25
25
  </DropdownMenu.Root>
26
26
  </div>
27
27
  </StackItem.Heading>
28
- <StackItem.Content classNames='p-2'>Content</StackItem.Content>
28
+ <StackItem.Content>Content</StackItem.Content>
29
29
  </StackItem.Root>
30
30
  );
31
31
  };
@@ -9,7 +9,8 @@ import { mx } from '@dxos/react-ui-theme';
9
9
 
10
10
  import { useStack, useStackItem } from '../StackContext';
11
11
 
12
- export type StackItemContentProps = ThemedClassName<Omit<ComponentPropsWithoutRef<'div'>, 'role'>> & {
12
+ // TODO(burdon): Add prop for container-max-width?
13
+ export type StackItemContentProps = ThemedClassName<Omit<ComponentPropsWithoutRef<'div'>, 'role' | 'scrollable'>> & {
13
14
  /**
14
15
  * This flag is required in order to clarify a developer experience that seemed like it needed extra boilerplate
15
16
  * (`row-span-2`) or was buggy. See the description of the StackItem.Content component itself for more information.
@@ -22,15 +23,21 @@ export type StackItemContentProps = ThemedClassName<Omit<ComponentPropsWithoutRe
22
23
  statusbar?: boolean;
23
24
 
24
25
  /**
25
- * Whether the consumer intends to do something custom and typical affordances should not apply
26
+ * Whether to support y-axis scrolling.
26
27
  */
27
- layoutManaged?: boolean;
28
+ scrollable?: boolean;
28
29
 
29
30
  /**
30
- * Whether to set a certain aspect ratio on the content, including the toolbar and statusbar. This is provided for
31
- * convenience and consistency; it can instead be specified by the `classNames` or `style` props as needed.
31
+ * Whether to set a certain aspect ratio on the content, including the toolbar and statusbar.
32
+ * This is provided for convenience and consistency; it can instead be specified by the `classNames` or `style` props as needed.
32
33
  */
33
34
  size?: 'intrinsic' | 'video' | 'square';
35
+
36
+ /**
37
+ * Whether the consumer intends to do something custom and typical affordances should not apply.
38
+ * @deprecated Replace with override for gridTempateRows.
39
+ */
40
+ layoutManaged?: boolean;
34
41
  };
35
42
 
36
43
  /**
@@ -38,7 +45,10 @@ export type StackItemContentProps = ThemedClassName<Omit<ComponentPropsWithoutRe
38
45
  * The `toolbar` flag must be provided since this component provides for the layout of content with the toolbar.
39
46
  */
40
47
  export const StackItemContent = forwardRef<HTMLDivElement, StackItemContentProps>(
41
- ({ children, toolbar, statusbar, layoutManaged, classNames, size = 'intrinsic', ...props }, forwardedRef) => {
48
+ (
49
+ { children, toolbar, statusbar, layoutManaged, classNames, size = 'intrinsic', scrollable, ...props },
50
+ forwardedRef,
51
+ ) => {
42
52
  const { size: stackItemSize } = useStack();
43
53
  const { role } = useStackItem();
44
54
  const style = useMemo(
@@ -61,12 +71,13 @@ export const StackItemContent = forwardRef<HTMLDivElement, StackItemContentProps
61
71
  {...props}
62
72
  className={mx(
63
73
  'group grid grid-cols-[100%] density-coarse',
64
- stackItemSize === 'contain' && 'min-bs-0 overflow-hidden',
65
74
  size === 'video' ? 'aspect-video' : size === 'square' && 'aspect-square',
66
- toolbar && '[&>.dx-toolbar]:relative [&>.dx-toolbar]:border-be [&>.dx-toolbar]:border-subduedSeparator',
75
+ stackItemSize === 'contain' && 'min-bs-0 overflow-hidden',
76
+ scrollable ? 'min-bs-0 overflow-y-auto scrollbar-thin contain-layout' : 'overflow-hidden',
67
77
  role === 'section' &&
68
78
  toolbar &&
69
79
  '[&_.dx-toolbar]:sticky [&_.dx-toolbar]:z-[1] [&_.dx-toolbar]:block-start-0 [&_.dx-toolbar]:-mbe-px [&_.dx-toolbar]:min-is-0',
80
+ toolbar && '[&>.dx-toolbar]:relative [&>.dx-toolbar]:border-be [&>.dx-toolbar]:border-subduedSeparator',
70
81
  classNames,
71
82
  )}
72
83
  style={style}
@@ -2,7 +2,6 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { useFocusableGroup } from '@fluentui/react-tabster';
6
5
  import { Slot } from '@radix-ui/react-slot';
7
6
  import React, {
8
7
  type ComponentPropsWithRef,
@@ -30,7 +29,6 @@ export const StackItemHeading = ({
30
29
  ...props
31
30
  }: StackItemHeadingProps) => {
32
31
  const { orientation } = useStack();
33
- const focusableGroupAttrs = useFocusableGroup({ tabBehavior: 'limited' });
34
32
 
35
33
  const Root = asChild ? Slot : 'div';
36
34
 
@@ -38,10 +36,8 @@ export const StackItemHeading = ({
38
36
  <Root
39
37
  role='heading'
40
38
  {...props}
41
- tabIndex={0}
42
- {...focusableGroupAttrs}
43
39
  className={mx(
44
- 'flex items-center dx-focus-ring-inset-over-all relative !border-is-0 bg-headerSurface',
40
+ 'flex items-center !border-is-0 bg-headerSurface',
45
41
  separateOnScroll
46
42
  ? 'border-transparent [[data-scroll-separator="true"]_&]:border-subduedSeparator'
47
43
  : 'border-subduedSeparator',
@@ -22,6 +22,16 @@ import { cardChrome, cardHeading, cardRoot, cardSpacing, cardText } from './frag
22
22
 
23
23
  type SharedCardProps = ThemedClassName<ComponentPropsWithoutRef<'div'>> & { asChild?: boolean };
24
24
 
25
+ /**
26
+ * The default width of cards. It should be no larger than 320px per WCAG 2.1 SC 1.4.10.
27
+ */
28
+ const cardDefaultInlineSize = 18;
29
+ /**
30
+ * This is `cardDefaultInlineSize` plus 2 times the sum of the inner and outer spacing applied by CardStack on the
31
+ * inline axis.
32
+ */
33
+ const cardStackDefaultInlineSizeRem = cardDefaultInlineSize + 2.125;
34
+
25
35
  const CardStaticRoot = forwardRef<HTMLDivElement, SharedCardProps>(
26
36
  ({ children, classNames, asChild, role = 'group', ...props }, forwardedRef) => {
27
37
  const Root = asChild ? Slot : 'div';
@@ -126,7 +136,7 @@ type CardPosterProps = {
126
136
  const CardPoster = (props: CardPosterProps) => {
127
137
  const aspect = props.aspect === 'auto' ? 'aspect-auto' : 'aspect-video';
128
138
  if (props.image) {
129
- return <Image classNames={[`dx-card__poster is-full __bs-auto`, aspect]} src={props.image} alt={props.alt} />;
139
+ return <Image classNames={[`dx-card__poster is-full`, aspect]} src={props.image} alt={props.alt} />;
130
140
  }
131
141
 
132
142
  if (props.icon) {
@@ -183,4 +193,12 @@ export const Card = {
183
193
  Text: CardText,
184
194
  };
185
195
 
186
- export { cardRoot, cardHeading, cardText, cardChrome, cardSpacing };
196
+ export {
197
+ cardRoot,
198
+ cardHeading,
199
+ cardText,
200
+ cardChrome,
201
+ cardSpacing,
202
+ cardStackDefaultInlineSizeRem,
203
+ cardDefaultInlineSize,
204
+ };