@tastic/hud 0.16.0 → 0.18.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.
- package/README.md +57 -3
- package/dist/{TriggerGauge-2OJOOF6I.mjs → TriggerGauge-GIHKGAZE.mjs} +5 -5
- package/dist/chrome/index.d.mts +39 -10
- package/dist/chrome/index.d.ts +39 -10
- package/dist/chrome/index.js +309 -196
- package/dist/chrome/index.mjs +19 -7
- package/dist/{chunk-32U2OIRW.mjs → chunk-PUF6TABL.mjs} +76 -16
- package/dist/{chunk-JXRBXQLQ.mjs → chunk-Z2WUUYTE.mjs} +164 -113
- package/dist/guide/index.d.mts +3 -0
- package/dist/guide/index.d.ts +3 -0
- package/dist/guide/index.js +136 -72
- package/dist/guide/index.mjs +25 -13
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +480 -367
- package/dist/index.mjs +19 -7
- package/package.json +10 -10
- package/src/BaseSettingsDialog.tsx +203 -129
- package/src/ConfirmDialog.tsx +21 -26
- package/src/DialogHeader.tsx +72 -0
- package/src/OverlaySurface.tsx +70 -0
- package/src/TriggerGauge.tsx +9 -9
- package/src/chrome/index.ts +4 -1
- package/src/guide/HowToPlayDialog.tsx +37 -17
- package/src/index.ts +4 -1
- package/src/overlayCard.ts +11 -29
- package/src/useCompactDialog.ts +23 -0
package/README.md
CHANGED
|
@@ -76,6 +76,11 @@ corner of the screen.
|
|
|
76
76
|
out after being independently copied into several games. See Rotation below for how each of these
|
|
77
77
|
(plus `SectionedDropdown`/`InlineColorPicker`) handles rotating inside a `@tastic/split-screen`
|
|
78
78
|
zone.
|
|
79
|
+
- **`OverlayCard`** / **`OverlayBackdrop`** / **`useOverlayCardColors`** - the card and dim behind
|
|
80
|
+
`ConfirmDialog` and the how-to-play guide, in the app's own theme colors and blurred when the
|
|
81
|
+
player's Blur setting allows it. See Overlay cards below.
|
|
82
|
+
- **`DialogHeader`** / **`useCompactDialog`** - the one header every dialog opens with (an icon over
|
|
83
|
+
a bold title in the theme's primary), and the rule for its compact form. See Overlay cards below.
|
|
79
84
|
|
|
80
85
|
## Rotation
|
|
81
86
|
|
|
@@ -235,6 +240,54 @@ export function SettingsDialog({ visible, onDismiss, rotation = 0, settings, set
|
|
|
235
240
|
}
|
|
236
241
|
```
|
|
237
242
|
|
|
243
|
+
## Overlay cards (`OverlayCard`, `OverlayBackdrop`, `useOverlayCardColors`)
|
|
244
|
+
|
|
245
|
+
`ConfirmDialog` and `HowToPlayDialog` draw their card and the dim behind it with these, and a game's
|
|
246
|
+
own overlay (a result banner, a game-over card) should too, so every card in the fleet looks the same:
|
|
247
|
+
|
|
248
|
+
```tsx
|
|
249
|
+
import { DialogHeader, OverlayBackdrop, OverlayCard, overlayActionStyles, useCompactDialog, useOverlayCardColors } from '@tastic/hud'
|
|
250
|
+
|
|
251
|
+
function GameOverCard({ onRestart }: { onRestart: () => void }) {
|
|
252
|
+
const { fgMuted } = useOverlayCardColors()
|
|
253
|
+
const { compact, onLayout } = useCompactDialog()
|
|
254
|
+
return (
|
|
255
|
+
<OverlayBackdrop onLayout={onLayout}>
|
|
256
|
+
<OverlayCard accessibilityViewIsModal>
|
|
257
|
+
<DialogHeader icon='flag-checkered' title='Game Over' compact={compact} />
|
|
258
|
+
<Text style={{ color: fgMuted }}>Score 42</Text>
|
|
259
|
+
<View style={overlayActionStyles.actions}>...</View>
|
|
260
|
+
</OverlayCard>
|
|
261
|
+
</OverlayBackdrop>
|
|
262
|
+
)
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
- **Colors come from the app's theme.** Text is `onSurface` (`fg`) and `onSurfaceVariant` (`fgMuted`),
|
|
267
|
+
and the dim is the theme's `backdrop`, the same scrim auto-paper's own `Dialog` uses. The card is the
|
|
268
|
+
Settings sheet's surface: auto-paper's `BlurView` with no elevation (the theme's plain surface, no
|
|
269
|
+
wash of the primary), no border, and the `Dialog`'s corner radius. So every card and the Settings
|
|
270
|
+
sheet look like one family, in each game's palette and light/dark mode.
|
|
271
|
+
- **Every dialog opens with `DialogHeader`.** A 64pt icon over a large bold title, both in the theme's
|
|
272
|
+
primary (pass `color` for another role, as `ConfirmDialog` does with danger when `destructive`).
|
|
273
|
+
`ConfirmDialog`, the how-to-play guide (titled "How to Play") and `BaseSettingsDialog` all use it;
|
|
274
|
+
an app's own cards should too. With `compact` the icon shrinks to 32pt and sits beside a smaller
|
|
275
|
+
title. `useCompactDialog(rotation?)` decides it: put its `onLayout` on the `OverlayBackdrop`, and it
|
|
276
|
+
is compact when the space the dialog lays out in is under `COMPACT_DIALOG_HEIGHT` (528pt) along the
|
|
277
|
+
card's own vertical axis, so a phone held sideways. One-button dialogs end in a single contained
|
|
278
|
+
primary button (`overlayActionStyles.actionButtonSingle`), the Settings sheet's Done included.
|
|
279
|
+
- **Blur is automatic.** The card blurs what's behind it when the player's Blur setting is on and the
|
|
280
|
+
app hands `expo-blur` to auto-paper's `Provider` (`expoBlur={ExpoBlur}`). Otherwise it paints that
|
|
281
|
+
raised surface color instead: on Android (`BlurView` needs a blur target there, which a card has
|
|
282
|
+
none of), in an app without `expo-blur`, or with Blur off. Beyond passing `expoBlur`, a game wires
|
|
283
|
+
nothing for this.
|
|
284
|
+
- **The Blur setting lives in `BaseSettingsDialog`.** Its APPEARANCE section shows a Blur row under the
|
|
285
|
+
appearance picker that flips auto-paper's own `blur` theme setting, which the settings sheet, Paper
|
|
286
|
+
menus and these cards all follow. The row only shows where blur can actually happen: not on
|
|
287
|
+
Android, not in an app that never passed `expoBlur`, and not with `hideAppearance`.
|
|
288
|
+
- **`getOverlayCardColors` is gone.** It returned the old fixed black/white card (`#F2F2F2`/`#111111`),
|
|
289
|
+
which ignored the theme and never blurred. Use `useOverlayCardColors` for a card's text and edges.
|
|
290
|
+
|
|
238
291
|
## Zone-aware popovers (`useZoneClampedAlign`)
|
|
239
292
|
|
|
240
293
|
`useAutoAlign` - and everything built on it, including `SectionedDropdown`, `InlineColorPicker`, and
|
|
@@ -565,7 +618,8 @@ touches the main barrel. It's for an app that has `@tastic/core` and `@rific/upd
|
|
|
565
618
|
- Stats: `BaseStatsScreen`, `StatSection`, `StatRow`, `AchievementRow`, `LOCKED_BADGE_COLOR`,
|
|
566
619
|
`AchievementCatalogSection`, `ActivityStatSection`
|
|
567
620
|
- Chrome: `CornerActionButtons`, `ContentGutter`, `StaggeredWord`, `getStaggeredWordDuration`
|
|
568
|
-
-
|
|
621
|
+
- Overlay cards: `OverlayCard`, `OverlayBackdrop`, `useOverlayCardColors`, `DialogHeader`, `useCompactDialog`, `isCompactDialogHeight`, `COMPACT_DIALOG_HEIGHT`
|
|
622
|
+
- Style tokens: `overlayStyles`, `overlayActionStyles`, `MONO_FONT`
|
|
569
623
|
|
|
570
624
|
Each is the same component the main entry exports and is documented above. Only the import path changes:
|
|
571
625
|
|
|
@@ -619,12 +673,12 @@ resolve module".
|
|
|
619
673
|
| `react`, `react-native` | >=19.0.0, >=0.76.0 | every entry |
|
|
620
674
|
| `react-native-paper` | >=5.0.0 | main entry, `./guide`, `./chrome` |
|
|
621
675
|
| `react-native-reanimated` | >=3.0.0 | main entry (`StaggeredWord`), `./guide`, `./chrome` (`StaggeredWord`) |
|
|
622
|
-
| `@rific/auto-paper` | >=0.12.2 | main entry (`BaseSettingsDialog` needs the nested `Portal.Host` first shipped in 0.12.2), `./guide`, `./chrome` |
|
|
676
|
+
| `@rific/auto-paper` | >=0.12.2 | main entry (`BaseSettingsDialog` needs the nested `Portal.Host` first shipped in 0.12.2; `OverlayCard` uses `BlurView` and `useBlur`, and `BaseSettingsDialog`'s Blur row uses `useBlurModule` and `useThemeSettings`, all in 0.12.2), `./guide`, `./chrome` |
|
|
623
677
|
| `@rific/feedback-press` | >=0.10.2 | main entry (`useSoundSettings` first shipped in 0.10.2), `./guide`, `./chrome` |
|
|
624
678
|
| `react-native-safe-area-context` | >=5.0.0 | main entry (`useZoneClampedAlign`), `./chrome` (`RotatedFrame`, `BaseSettingsDialog`) |
|
|
625
679
|
| `@tastic/core` | >=0.12.0, optional | main entry: `useRotation()` and most dialogs; `RotatedFrame` and `BaseSettingsDialog` need `FakeLandscapeView`'s `rotation`/`passThrough`, first shipped in 0.12.0; `./chrome` (the same dialogs) |
|
|
626
680
|
| `@rific/updater` | >=0.4.0, optional | main entry and `./chrome`: `BaseSettingsDialog`, `UpdateDialog` |
|
|
627
681
|
| `@tastic/profile` | >=0.4.0, optional | main entry: `PlayerSetupPanel`, `AchievementUnlockList` (`ProfileChip`) |
|
|
628
682
|
| `@tastic/split-screen` | >=0.5.0, optional | main entry: `useZoneClampedAlign` |
|
|
629
|
-
| `@shopify/react-native-skia` | >=
|
|
683
|
+
| `@shopify/react-native-skia` | >=2.6.0, optional | `TriggerGaugeHost`'s gauge (lazy-loaded behind `SkiaGate`) and `./skia-gate` |
|
|
630
684
|
| `@tastic/achievements` | >=0.2.0, optional | `AchievementCatalogSection`, `AchievementUnlockList`, type-only (`AchievementCatalogRow`, first exported in 0.2.0; `AchievementTier`); `./chrome` needs it installed only to type-check |
|
|
@@ -54,7 +54,7 @@ function TriggerGauge({ segments, litIndices, size, accentColor, mutedColor, das
|
|
|
54
54
|
const canvasSize = diameter + LIT_THICKNESS;
|
|
55
55
|
const oval = Skia.XYWHRect(LIT_THICKNESS / 2, LIT_THICKNESS / 2, diameter, diameter);
|
|
56
56
|
const unlitPath = useDerivedValue(() => {
|
|
57
|
-
const path = Skia.
|
|
57
|
+
const path = Skia.PathBuilder.Make();
|
|
58
58
|
for (let i = 0; i < segments; i++) {
|
|
59
59
|
if (litIndices.includes(i)) continue;
|
|
60
60
|
let local = 1;
|
|
@@ -68,13 +68,13 @@ function TriggerGauge({ segments, litIndices, size, accentColor, mutedColor, das
|
|
|
68
68
|
const dashCenterDeg = startDeg + (i + 0.5) * step;
|
|
69
69
|
path.addArc(oval, dashCenterDeg - 90 - sweep / 2, sweep);
|
|
70
70
|
}
|
|
71
|
-
return path;
|
|
71
|
+
return path.detach();
|
|
72
72
|
}, [segments, litIndicesKey, step, startDeg, dashAngleDeg, totalDurationMs, oval.x, oval.y, oval.width, oval.height]);
|
|
73
73
|
const litPath = useDerivedValue(() => {
|
|
74
|
-
const path = Skia.
|
|
74
|
+
const path = Skia.PathBuilder.Make();
|
|
75
75
|
if (mountProgress.value >= 1 && singleSelect) {
|
|
76
76
|
path.addArc(oval, angleDeg.value - 90 - dashAngleDeg / 2, dashAngleDeg);
|
|
77
|
-
return path;
|
|
77
|
+
return path.detach();
|
|
78
78
|
}
|
|
79
79
|
for (let i = 0; i < segments; i++) {
|
|
80
80
|
const isLitNow = litIndices.includes(i);
|
|
@@ -98,7 +98,7 @@ function TriggerGauge({ segments, litIndices, size, accentColor, mutedColor, das
|
|
|
98
98
|
const dashCenterDeg = startDeg + (i + 0.5) * step;
|
|
99
99
|
path.addArc(oval, dashCenterDeg - 90 - sweep / 2, sweep);
|
|
100
100
|
}
|
|
101
|
-
return path;
|
|
101
|
+
return path.detach();
|
|
102
102
|
}, [segments, litIndicesKey, transitionFromKey, singleSelect, step, startDeg, dashAngleDeg, totalDurationMs, oval.x, oval.y, oval.width, oval.height]);
|
|
103
103
|
if (segments < 2) return null;
|
|
104
104
|
const gaugeSizeStyle = {
|
package/dist/chrome/index.d.mts
CHANGED
|
@@ -2,7 +2,7 @@ import * as react from 'react';
|
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
import { AchievementCatalogRow } from '@tastic/achievements';
|
|
4
4
|
import { ViewRotation } from '@tastic/core';
|
|
5
|
-
import { StyleProp, ViewStyle, Insets,
|
|
5
|
+
import { StyleProp, ViewStyle, Insets, LayoutChangeEvent, TextStyle } from 'react-native';
|
|
6
6
|
|
|
7
7
|
interface Props$7 {
|
|
8
8
|
rows: AchievementCatalogRow[];
|
|
@@ -117,20 +117,23 @@ interface Props$2 {
|
|
|
117
117
|
}
|
|
118
118
|
declare function CornerActionButtons({ onBack, onSettings, fg, insets, backIcon, backLabel, hitSlop }: Props$2): react.JSX.Element;
|
|
119
119
|
|
|
120
|
+
declare const COMPACT_DIALOG_HEIGHT = 528;
|
|
121
|
+
declare function isCompactDialogHeight(height: number): boolean;
|
|
122
|
+
interface DialogHeaderProps {
|
|
123
|
+
icon: string;
|
|
124
|
+
title: string;
|
|
125
|
+
color?: string;
|
|
126
|
+
compact?: boolean;
|
|
127
|
+
style?: StyleProp<ViewStyle>;
|
|
128
|
+
}
|
|
129
|
+
declare function DialogHeader({ icon, title, color, compact, style }: DialogHeaderProps): react.JSX.Element;
|
|
130
|
+
|
|
120
131
|
declare const MONO_FONT: string;
|
|
121
132
|
|
|
122
|
-
interface OverlayCardColors {
|
|
123
|
-
fg: string;
|
|
124
|
-
fgMuted: string;
|
|
125
|
-
cardBg: string;
|
|
126
|
-
cardBorder: string;
|
|
127
|
-
}
|
|
128
|
-
declare function getOverlayCardColors(dark: boolean): OverlayCardColors;
|
|
129
133
|
declare const overlayStyles: {
|
|
130
134
|
card: {
|
|
131
135
|
alignItems: "center";
|
|
132
136
|
borderRadius: number;
|
|
133
|
-
borderWidth: number;
|
|
134
137
|
gap: number;
|
|
135
138
|
maxWidth: number;
|
|
136
139
|
padding: number;
|
|
@@ -163,6 +166,27 @@ declare const overlayActionStyles: {
|
|
|
163
166
|
};
|
|
164
167
|
};
|
|
165
168
|
|
|
169
|
+
interface OverlayCardThemeColors {
|
|
170
|
+
fg: string;
|
|
171
|
+
fgMuted: string;
|
|
172
|
+
cardBorder: string;
|
|
173
|
+
backdrop: string;
|
|
174
|
+
}
|
|
175
|
+
declare function useOverlayCardColors(): OverlayCardThemeColors;
|
|
176
|
+
interface OverlayBackdropProps {
|
|
177
|
+
children?: ReactNode;
|
|
178
|
+
style?: StyleProp<ViewStyle>;
|
|
179
|
+
onLayout?: (e: LayoutChangeEvent) => void;
|
|
180
|
+
}
|
|
181
|
+
declare function OverlayBackdrop({ children, style, onLayout }: OverlayBackdropProps): react.JSX.Element;
|
|
182
|
+
interface OverlayCardProps {
|
|
183
|
+
children?: ReactNode;
|
|
184
|
+
style?: StyleProp<ViewStyle>;
|
|
185
|
+
accessibilityViewIsModal?: boolean;
|
|
186
|
+
testID?: string;
|
|
187
|
+
}
|
|
188
|
+
declare function OverlayCard({ children, style, ...rest }: OverlayCardProps): react.JSX.Element;
|
|
189
|
+
|
|
166
190
|
interface RotatedFrameProps {
|
|
167
191
|
children: ReactNode;
|
|
168
192
|
/** The angle to turn the frame by. Defaults to the live physical hold (@tastic/core's useRotation); pass it
|
|
@@ -237,4 +261,9 @@ interface UpdateDialogProps {
|
|
|
237
261
|
}
|
|
238
262
|
declare function UpdateDialog({ rotation, autoCheck, autoPrompt, onError }: UpdateDialogProps): react.JSX.Element;
|
|
239
263
|
|
|
240
|
-
|
|
264
|
+
declare function useCompactDialog(rotation?: ViewRotation): {
|
|
265
|
+
compact: boolean;
|
|
266
|
+
onLayout: (e: LayoutChangeEvent) => void;
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
export { AchievementCatalogSection, AchievementRow, ActivityStatSection, BaseSettingsDialog, type BaseSettingsDialogProps, BaseStatsScreen, COMPACT_DIALOG_HEIGHT, ConfirmDialog, type ConfirmDialogProps, ContentGutter, CornerActionButtons, DialogHeader, type DialogHeaderProps, LOCKED_BADGE_COLOR, MONO_FONT, OverlayBackdrop, type OverlayBackdropProps, OverlayCard, type OverlayCardProps, type OverlayCardThemeColors, RotatedFrame, type RotatedFrameProps, StaggeredWord, type StaggeredWordProps, StatRow, StatSection, UpdateDialog, type UpdateDialogProps, getStaggeredWordDuration, isCompactDialogHeight, overlayActionStyles, overlayStyles, useCompactDialog, useOverlayCardColors };
|
package/dist/chrome/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as react from 'react';
|
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
import { AchievementCatalogRow } from '@tastic/achievements';
|
|
4
4
|
import { ViewRotation } from '@tastic/core';
|
|
5
|
-
import { StyleProp, ViewStyle, Insets,
|
|
5
|
+
import { StyleProp, ViewStyle, Insets, LayoutChangeEvent, TextStyle } from 'react-native';
|
|
6
6
|
|
|
7
7
|
interface Props$7 {
|
|
8
8
|
rows: AchievementCatalogRow[];
|
|
@@ -117,20 +117,23 @@ interface Props$2 {
|
|
|
117
117
|
}
|
|
118
118
|
declare function CornerActionButtons({ onBack, onSettings, fg, insets, backIcon, backLabel, hitSlop }: Props$2): react.JSX.Element;
|
|
119
119
|
|
|
120
|
+
declare const COMPACT_DIALOG_HEIGHT = 528;
|
|
121
|
+
declare function isCompactDialogHeight(height: number): boolean;
|
|
122
|
+
interface DialogHeaderProps {
|
|
123
|
+
icon: string;
|
|
124
|
+
title: string;
|
|
125
|
+
color?: string;
|
|
126
|
+
compact?: boolean;
|
|
127
|
+
style?: StyleProp<ViewStyle>;
|
|
128
|
+
}
|
|
129
|
+
declare function DialogHeader({ icon, title, color, compact, style }: DialogHeaderProps): react.JSX.Element;
|
|
130
|
+
|
|
120
131
|
declare const MONO_FONT: string;
|
|
121
132
|
|
|
122
|
-
interface OverlayCardColors {
|
|
123
|
-
fg: string;
|
|
124
|
-
fgMuted: string;
|
|
125
|
-
cardBg: string;
|
|
126
|
-
cardBorder: string;
|
|
127
|
-
}
|
|
128
|
-
declare function getOverlayCardColors(dark: boolean): OverlayCardColors;
|
|
129
133
|
declare const overlayStyles: {
|
|
130
134
|
card: {
|
|
131
135
|
alignItems: "center";
|
|
132
136
|
borderRadius: number;
|
|
133
|
-
borderWidth: number;
|
|
134
137
|
gap: number;
|
|
135
138
|
maxWidth: number;
|
|
136
139
|
padding: number;
|
|
@@ -163,6 +166,27 @@ declare const overlayActionStyles: {
|
|
|
163
166
|
};
|
|
164
167
|
};
|
|
165
168
|
|
|
169
|
+
interface OverlayCardThemeColors {
|
|
170
|
+
fg: string;
|
|
171
|
+
fgMuted: string;
|
|
172
|
+
cardBorder: string;
|
|
173
|
+
backdrop: string;
|
|
174
|
+
}
|
|
175
|
+
declare function useOverlayCardColors(): OverlayCardThemeColors;
|
|
176
|
+
interface OverlayBackdropProps {
|
|
177
|
+
children?: ReactNode;
|
|
178
|
+
style?: StyleProp<ViewStyle>;
|
|
179
|
+
onLayout?: (e: LayoutChangeEvent) => void;
|
|
180
|
+
}
|
|
181
|
+
declare function OverlayBackdrop({ children, style, onLayout }: OverlayBackdropProps): react.JSX.Element;
|
|
182
|
+
interface OverlayCardProps {
|
|
183
|
+
children?: ReactNode;
|
|
184
|
+
style?: StyleProp<ViewStyle>;
|
|
185
|
+
accessibilityViewIsModal?: boolean;
|
|
186
|
+
testID?: string;
|
|
187
|
+
}
|
|
188
|
+
declare function OverlayCard({ children, style, ...rest }: OverlayCardProps): react.JSX.Element;
|
|
189
|
+
|
|
166
190
|
interface RotatedFrameProps {
|
|
167
191
|
children: ReactNode;
|
|
168
192
|
/** The angle to turn the frame by. Defaults to the live physical hold (@tastic/core's useRotation); pass it
|
|
@@ -237,4 +261,9 @@ interface UpdateDialogProps {
|
|
|
237
261
|
}
|
|
238
262
|
declare function UpdateDialog({ rotation, autoCheck, autoPrompt, onError }: UpdateDialogProps): react.JSX.Element;
|
|
239
263
|
|
|
240
|
-
|
|
264
|
+
declare function useCompactDialog(rotation?: ViewRotation): {
|
|
265
|
+
compact: boolean;
|
|
266
|
+
onLayout: (e: LayoutChangeEvent) => void;
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
export { AchievementCatalogSection, AchievementRow, ActivityStatSection, BaseSettingsDialog, type BaseSettingsDialogProps, BaseStatsScreen, COMPACT_DIALOG_HEIGHT, ConfirmDialog, type ConfirmDialogProps, ContentGutter, CornerActionButtons, DialogHeader, type DialogHeaderProps, LOCKED_BADGE_COLOR, MONO_FONT, OverlayBackdrop, type OverlayBackdropProps, OverlayCard, type OverlayCardProps, type OverlayCardThemeColors, RotatedFrame, type RotatedFrameProps, StaggeredWord, type StaggeredWordProps, StatRow, StatSection, UpdateDialog, type UpdateDialogProps, getStaggeredWordDuration, isCompactDialogHeight, overlayActionStyles, overlayStyles, useCompactDialog, useOverlayCardColors };
|