@reactberry/system 2.0.0-beta

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 (165) hide show
  1. package/README.md +48 -0
  2. package/package.json +74 -0
  3. package/src/blocks/Accordion/index.tsx +158 -0
  4. package/src/blocks/AnimatedCarousel/index.tsx +188 -0
  5. package/src/blocks/AppleGlow/index.tsx +144 -0
  6. package/src/blocks/Avatar/index.tsx +167 -0
  7. package/src/blocks/Await/index.tsx +45 -0
  8. package/src/blocks/Cards/AnimatedCard/index.tsx +175 -0
  9. package/src/blocks/Cards/FluorescentCard/index.tsx +180 -0
  10. package/src/blocks/Cards/InfoCard/index.tsx +206 -0
  11. package/src/blocks/Cards/TickerCard/index.tsx +125 -0
  12. package/src/blocks/Carousel/index.tsx +216 -0
  13. package/src/blocks/Checkbox/index.tsx +101 -0
  14. package/src/blocks/Collection/index.tsx +59 -0
  15. package/src/blocks/Container/index.tsx +55 -0
  16. package/src/blocks/Controls/Control.tsx +67 -0
  17. package/src/blocks/Controls/index.tsx +11 -0
  18. package/src/blocks/CyclingNumber/index.tsx +78 -0
  19. package/src/blocks/DisplaySet/index.tsx +42 -0
  20. package/src/blocks/Divider/index.tsx +14 -0
  21. package/src/blocks/Draggable/index.tsx +266 -0
  22. package/src/blocks/Drawer/index.tsx +136 -0
  23. package/src/blocks/DynamicIsland/DynamicIsland.tsx +89 -0
  24. package/src/blocks/DynamicIsland/index.tsx +2 -0
  25. package/src/blocks/Fader/index.tsx +145 -0
  26. package/src/blocks/FamilyDrawer/README.md +116 -0
  27. package/src/blocks/FamilyDrawer/example.tsx +108 -0
  28. package/src/blocks/FamilyDrawer/index.tsx +119 -0
  29. package/src/blocks/FamilyDrawer/views/DefaultView.tsx +93 -0
  30. package/src/blocks/FamilyDrawer/views/KeyView.tsx +129 -0
  31. package/src/blocks/FamilyDrawer/views/PhraseView.tsx +129 -0
  32. package/src/blocks/FamilyDrawer/views/RemoveView.tsx +81 -0
  33. package/src/blocks/FieldSet/index.tsx +173 -0
  34. package/src/blocks/Filesystem/index.tsx +198 -0
  35. package/src/blocks/Gallery/Carousel/index.tsx +257 -0
  36. package/src/blocks/Gallery/Modal/index.tsx +83 -0
  37. package/src/blocks/Gallery/index.tsx +57 -0
  38. package/src/blocks/Gallery/utils/animationVariants.ts +18 -0
  39. package/src/blocks/Gallery/utils/aspectRatio.ts +14 -0
  40. package/src/blocks/Gallery/utils/downloadPhoto.ts +24 -0
  41. package/src/blocks/Gallery/utils/range.ts +11 -0
  42. package/src/blocks/GradientMesh/index.tsx +106 -0
  43. package/src/blocks/Group/index.tsx +152 -0
  44. package/src/blocks/Heading/index.tsx +111 -0
  45. package/src/blocks/HorizontalScroller/index.tsx +135 -0
  46. package/src/blocks/Icon/index.tsx +45 -0
  47. package/src/blocks/Indicator/index.tsx +27 -0
  48. package/src/blocks/InlineEditor/index.tsx +216 -0
  49. package/src/blocks/List/index.tsx +657 -0
  50. package/src/blocks/Main/index.tsx +17 -0
  51. package/src/blocks/Marquee/index.tsx +116 -0
  52. package/src/blocks/MaskedField/index.tsx +199 -0
  53. package/src/blocks/Menu/MenuContent.tsx +246 -0
  54. package/src/blocks/Menu/MenuContext.tsx +34 -0
  55. package/src/blocks/Menu/MenuItem.tsx +104 -0
  56. package/src/blocks/Menu/index.tsx +60 -0
  57. package/src/blocks/Modal/index.tsx +268 -0
  58. package/src/blocks/MorphingPopover/index.tsx +294 -0
  59. package/src/blocks/Overlay/Backdrop.tsx +48 -0
  60. package/src/blocks/Overlay/OverscrollGuard.tsx +36 -0
  61. package/src/blocks/Overlay/index.ts +2 -0
  62. package/src/blocks/Parallax/index.tsx +117 -0
  63. package/src/blocks/ParallaxSection/index.tsx +61 -0
  64. package/src/blocks/Placeholder/index.tsx +48 -0
  65. package/src/blocks/Popover/index.tsx +402 -0
  66. package/src/blocks/Progress/getProgressColor.ts +61 -0
  67. package/src/blocks/Progress/index.tsx +179 -0
  68. package/src/blocks/ProgressiveBlur/index.tsx +75 -0
  69. package/src/blocks/README.md +15 -0
  70. package/src/blocks/RenderAsset/index.tsx +18 -0
  71. package/src/blocks/ScrollContainer/index.tsx +93 -0
  72. package/src/blocks/ShinyText/index.tsx +72 -0
  73. package/src/blocks/Skeleton/index.tsx +71 -0
  74. package/src/blocks/Slider/SliderControls.tsx +119 -0
  75. package/src/blocks/Slider/index.tsx +140 -0
  76. package/src/blocks/Slider/useSlider.ts +126 -0
  77. package/src/blocks/Slideshow/index.tsx +177 -0
  78. package/src/blocks/Spotlight/index.tsx +144 -0
  79. package/src/blocks/Steps/StepIndicator.tsx +149 -0
  80. package/src/blocks/Steps/StepProgress.tsx +164 -0
  81. package/src/blocks/Steps/Steps.tsx +197 -0
  82. package/src/blocks/Steps/StepsNav.tsx +30 -0
  83. package/src/blocks/Steps/StepsTracker.tsx +80 -0
  84. package/src/blocks/Steps/hooks.ts +71 -0
  85. package/src/blocks/Steps/index.tsx +16 -0
  86. package/src/blocks/Steps/types.ts +71 -0
  87. package/src/blocks/StickySectionStack/index.tsx +136 -0
  88. package/src/blocks/Switch/index.tsx +85 -0
  89. package/src/blocks/SystemNotice/index.tsx +81 -0
  90. package/src/blocks/Table/README.md +251 -0
  91. package/src/blocks/Table/Table.tsx +207 -0
  92. package/src/blocks/Table/TablePagination.tsx +189 -0
  93. package/src/blocks/Table/index.ts +33 -0
  94. package/src/blocks/Table/useTableControls.ts +331 -0
  95. package/src/blocks/Tag/index.tsx +27 -0
  96. package/src/blocks/TextBreak/index.tsx +96 -0
  97. package/src/blocks/TextReveal/index.tsx +104 -0
  98. package/src/blocks/Thumbnail/index.tsx +26 -0
  99. package/src/blocks/Ticker/index.tsx +112 -0
  100. package/src/blocks/Toast/index.tsx +77 -0
  101. package/src/blocks/Tooltip/index.tsx +174 -0
  102. package/src/blocks/Underlay/index.tsx +104 -0
  103. package/src/blocks/Upload/Dropzone.tsx +92 -0
  104. package/src/blocks/Upload/UploadBtn.tsx +38 -0
  105. package/src/blocks/Upload/index.tsx +61 -0
  106. package/src/blocks/Upload/types.ts +37 -0
  107. package/src/blocks/VideoMarquee/index.tsx +511 -0
  108. package/src/blocks/index.ts +119 -0
  109. package/src/blocks/pagination/Pagination.tsx +148 -0
  110. package/src/blocks/pagination/PaginationList.tsx +41 -0
  111. package/src/blocks/pagination/index.ts +2 -0
  112. package/src/charts/BarChart.tsx +63 -0
  113. package/src/charts/PieChart.tsx +39 -0
  114. package/src/charts/index.ts +3 -0
  115. package/src/charts/utils.ts +103 -0
  116. package/src/docs/README.md +373 -0
  117. package/src/docs/reference/README.md +299 -0
  118. package/src/elements/box.ts +163 -0
  119. package/src/elements/button.ts +49 -0
  120. package/src/elements/field.ts +129 -0
  121. package/src/elements/index.ts +8 -0
  122. package/src/elements/text.ts +47 -0
  123. package/src/elements/utils.js +97 -0
  124. package/src/hooks/use-copy-to-clipboard.tsx +33 -0
  125. package/src/hooks/use-enter-submit.tsx +23 -0
  126. package/src/hooks/use-local-storage.ts +42 -0
  127. package/src/hooks/use-sidebar.tsx +109 -0
  128. package/src/hooks/useAnimatedText.ts +32 -0
  129. package/src/hooks/useAutosizeTextArea.ts +45 -0
  130. package/src/hooks/useBreakpoint.tsx +123 -0
  131. package/src/hooks/useClickOutside.tsx +38 -0
  132. package/src/hooks/useHover.tsx +33 -0
  133. package/src/hooks/useHoverList.tsx +17 -0
  134. package/src/hooks/useKeyboardShortcuts.ts +91 -0
  135. package/src/hooks/useKeypress.ts +27 -0
  136. package/src/hooks/useOverlay.ts +32 -0
  137. package/src/hooks/useReducedMotion.ts +25 -0
  138. package/src/hooks/useStandaloneMode.ts +35 -0
  139. package/src/hooks/useTouchDevice.ts +34 -0
  140. package/src/icons/index.tsx +129 -0
  141. package/src/index.ts +12 -0
  142. package/src/providers/DesignSystemProvider.tsx +35 -0
  143. package/src/providers/StyledComponentsRegistry.tsx +30 -0
  144. package/src/providers/index.ts +2 -0
  145. package/src/themes/README.md +30 -0
  146. package/src/themes/default/assets/badge-avatar.tsx +45 -0
  147. package/src/themes/default/assets/logo.tsx +42 -0
  148. package/src/themes/default/global.ts +138 -0
  149. package/src/themes/default/modes/dark/config.js +49 -0
  150. package/src/themes/default/modes/dark/skins.js +631 -0
  151. package/src/themes/default/modes/dark/theme.js +87 -0
  152. package/src/themes/default/modes/light/config.js +48 -0
  153. package/src/themes/default/modes/light/skins.js +1026 -0
  154. package/src/themes/default/modes/light/theme.js +74 -0
  155. package/src/themes/default/tokens/controls.js +53 -0
  156. package/src/themes/default/tokens/shadows.js +63 -0
  157. package/src/themes/default/tokens/shapes.js +37 -0
  158. package/src/themes/default/tokens/space.js +143 -0
  159. package/src/themes/default/tokens/spectre.js +16 -0
  160. package/src/themes/default/utils.js +523 -0
  161. package/src/themes/index.ts +11 -0
  162. package/src/types.ts +394 -0
  163. package/src/utils/overlayTheme.ts +61 -0
  164. package/src/utils/pickColor.ts +15 -0
  165. package/tsconfig.json +24 -0
package/src/types.ts ADDED
@@ -0,0 +1,394 @@
1
+ "use client";
2
+
3
+ // Export all component prop types for AI analysis and type checking
4
+ export type { BaseElementProps } from "./elements/box";
5
+ export type { TextProps } from "./elements/text";
6
+ export type { ButtonProps } from "./elements/button";
7
+ export type { FieldProps } from "./elements/field";
8
+
9
+ export type ImageType = {
10
+ id: number;
11
+ url: string;
12
+ };
13
+
14
+ // ============================================================================
15
+ // THEME TOKEN TYPES
16
+ // ============================================================================
17
+
18
+ /**
19
+ * Space tokens for spacing (gap, padding, margin, generic size)
20
+ * Used by: gap, p, m, px, py, pt, pb, pl, pr, mx, my, mt, mb, ml, mr, size (on Box/Text)
21
+ */
22
+ export type SpaceToken =
23
+ | "mini" // 4px
24
+ | "xs" // 8px
25
+ | "s" // 12px
26
+ | "m" // 16px (default)
27
+ | "l" // 24px
28
+ | "xl" // 32px
29
+ | "xxl" // 48px
30
+ | "xxxl"; // 64px
31
+
32
+ /**
33
+ * Font size tokens for typography
34
+ * Used by: fontSize (on Text component)
35
+ */
36
+ export type FontSizeToken =
37
+ | "xs" // 12px - captions, labels
38
+ | "s" // 14px - secondary text
39
+ | "m" // 16px - body text (default)
40
+ | "l" // 20px - large body
41
+ | "xl" // 24px - headings
42
+ | "xxl" // 32px - page titles
43
+ | "xxxl"; // 48px - hero text
44
+
45
+ /**
46
+ * Shape tokens for border radius
47
+ * Used by: shape prop
48
+ */
49
+ export type ShapeToken =
50
+ | "square" // 0px - no rounding
51
+ | "roundedSmall" // 4px - subtle rounding
52
+ | "rounded" // 8px - standard rounding (default)
53
+ | "roundedLarge" // 16px - pronounced rounding
54
+ | "pill" // 32px - pill shape
55
+ | "circle"; // 50% - circular
56
+
57
+ /**
58
+ * Skin tokens for predefined styling combinations
59
+ * Used by: skin prop on Box
60
+ */
61
+ export type SkinToken =
62
+ // Surface skins
63
+ | "base" // Base background
64
+ | "surface" // Secondary surface
65
+ | "card" // Card with elevation
66
+ | "panel" // Panel background
67
+ // Semantic skins
68
+ | "primary" // Primary brand color
69
+ | "secondary" // Secondary color
70
+ | "error" // Error state
71
+ | "success" // Success state
72
+ | "warning" // Warning state
73
+ | "info" // Info state
74
+ // Color-coded skins
75
+ | "blue"
76
+ | "green"
77
+ | "red"
78
+ | "yellow"
79
+ | "purple"
80
+ | "orange";
81
+
82
+ /**
83
+ * Color tokens for text and backgrounds
84
+ * Used by: color, bg props
85
+ */
86
+ export type ColorToken =
87
+ | "primary"
88
+ | "secondary"
89
+ | "tertiary"
90
+ | "error"
91
+ | "success"
92
+ | "warning"
93
+ | "info"
94
+ | string; // Allow custom colors but prefer tokens
95
+
96
+ /**
97
+ * Font weight tokens
98
+ * Used by: fontWeight prop on Text
99
+ */
100
+ export type FontWeightToken =
101
+ | "normal" // 400
102
+ | "medium" // 500
103
+ | "semibold" // 600
104
+ | "bold"; // 700
105
+
106
+ /**
107
+ * Line height tokens
108
+ * Used by: lineHeight prop on Text
109
+ */
110
+ export type LineHeightToken =
111
+ | "tight" // 1.2
112
+ | "normal" // 1.5 (default)
113
+ | "relaxed" // 1.75
114
+ | "loose"; // 2
115
+
116
+ // ============================================================================
117
+ // COMPONENT-SPECIFIC TYPES
118
+ // ============================================================================
119
+
120
+ /**
121
+ * CRITICAL: Component size for Button and Field ONLY
122
+ * Used ONLY by: $size prop on Button and Field components
123
+ *
124
+ * DO NOT confuse with SpaceToken or 'size' prop!
125
+ * - Button/Field use: $size="medium" (ComponentSize)
126
+ * - Box/Text use: size="m" (SpaceToken)
127
+ */
128
+ export type ComponentSize =
129
+ | "small" // Compact sizing
130
+ | "medium" // Default sizing
131
+ | "large"; // Prominent sizing
132
+
133
+ /**
134
+ * Button variants
135
+ * Used by: variant prop on Button
136
+ */
137
+ export type ButtonVariant =
138
+ | "default" // Default styling
139
+ | "primary" // Primary CTA
140
+ | "secondary" // Secondary action
141
+ | "ghost" // Subtle/minimal
142
+ | "outline"; // Outlined
143
+
144
+ /**
145
+ * Field variants
146
+ * Used by: variant prop on Field
147
+ */
148
+ export type FieldVariant =
149
+ | "default" // Default styling
150
+ | "outline" // Outlined input
151
+ | "filled" // Filled background
152
+ | "unstyled"; // No styling
153
+
154
+ /**
155
+ * Semantic HTML tags for Text component
156
+ * Used by: as prop on Text
157
+ */
158
+ export type TextElement =
159
+ | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" // Headings
160
+ | "p" // Paragraph
161
+ | "span" // Inline text
162
+ | "label" // Form label
163
+ | "a" // Link
164
+ | "div"; // Generic container
165
+
166
+ /**
167
+ * Form input elements
168
+ * Used by: as prop on Field
169
+ */
170
+ export type FieldElement =
171
+ | "input"
172
+ | "textarea"
173
+ | "select";
174
+
175
+ /**
176
+ * Input types for Field
177
+ * Used by: type prop on Field when as="input"
178
+ */
179
+ export type InputType =
180
+ | "text"
181
+ | "email"
182
+ | "password"
183
+ | "number"
184
+ | "tel"
185
+ | "url"
186
+ | "search"
187
+ | "date"
188
+ | "time"
189
+ | "datetime-local"
190
+ | "month"
191
+ | "week"
192
+ | "range"
193
+ | "color"
194
+ | "file";
195
+
196
+ // ============================================================================
197
+ // PROP USAGE GUIDE TYPES
198
+ // ============================================================================
199
+
200
+ /**
201
+ * CRITICAL DISTINCTION: size vs $size
202
+ *
203
+ * This is the #1 source of confusion and errors!
204
+ */
205
+ export interface PropUsageGuide {
206
+ /**
207
+ * Box and Text use 'size' for GENERIC spacing
208
+ * Maps to theme space scale (SpaceToken)
209
+ */
210
+ BoxSizeUsage: {
211
+ size?: SpaceToken; // ✅ CORRECT for Box
212
+ // $size?: never; // ❌ WRONG for Box
213
+ };
214
+
215
+ TextSizeUsage: {
216
+ size?: SpaceToken; // ✅ CORRECT for Text (generic size)
217
+ fontSize?: FontSizeToken; // ✅ CORRECT for text sizing
218
+ // $size?: never; // ❌ WRONG for Text
219
+ };
220
+
221
+ /**
222
+ * Button and Field use '$size' for COMPONENT-SPECIFIC sizing
223
+ * Predefined sizes: small, medium, large
224
+ */
225
+ ButtonSizeUsage: {
226
+ $size?: ComponentSize; // ✅ CORRECT for Button
227
+ // size?: never; // ❌ WRONG for Button (component sizing)
228
+ };
229
+
230
+ FieldSizeUsage: {
231
+ $size?: ComponentSize; // ✅ CORRECT for Field
232
+ // size?: never; // ❌ WRONG for Field (component sizing)
233
+ };
234
+ }
235
+
236
+ // ============================================================================
237
+ // RESPONSIVE TYPES
238
+ // ============================================================================
239
+
240
+ /**
241
+ * Responsive array for breakpoint values
242
+ * Format: [mobile, tablet, desktop]
243
+ *
244
+ * @example
245
+ * // Mobile 100%, tablet 50%, desktop 33%
246
+ * <Box width={[1, 1/2, 1/3]} />
247
+ *
248
+ * @example
249
+ * // Responsive padding
250
+ * <Box p={['s', 'm', 'l']} />
251
+ */
252
+ export type ResponsiveValue<T> = T | [T] | [T, T] | [T, T, T];
253
+
254
+ /**
255
+ * Common responsive props
256
+ */
257
+ export interface ResponsiveProps {
258
+ width?: ResponsiveValue<string | number>;
259
+ height?: ResponsiveValue<string | number>;
260
+ padding?: ResponsiveValue<SpaceToken>;
261
+ margin?: ResponsiveValue<SpaceToken>;
262
+ fontSize?: ResponsiveValue<FontSizeToken>;
263
+ display?: ResponsiveValue<string>;
264
+ }
265
+
266
+ // ============================================================================
267
+ // HELPER TYPES
268
+ // ============================================================================
269
+
270
+ /**
271
+ * Common display values
272
+ */
273
+ export type DisplayValue =
274
+ | "flex"
275
+ | "grid"
276
+ | "block"
277
+ | "inline-block"
278
+ | "inline-flex"
279
+ | "inline"
280
+ | "none";
281
+
282
+ /**
283
+ * Common cursor values
284
+ */
285
+ export type CursorValue =
286
+ | "pointer"
287
+ | "default"
288
+ | "not-allowed"
289
+ | "text"
290
+ | "move"
291
+ | "grab"
292
+ | "grabbing";
293
+
294
+ /**
295
+ * Hover states
296
+ */
297
+ export type HoverState =
298
+ | "subtle" // Subtle highlight
299
+ | "lift" // Lift with shadow
300
+ | "glow"; // Glow effect
301
+
302
+ /**
303
+ * Focus states
304
+ */
305
+ export type FocusState =
306
+ | "highlight" // Highlight border
307
+ | "ring" // Focus ring
308
+ | "none"; // No focus style
309
+
310
+ // ============================================================================
311
+ // USAGE EXAMPLES (FOR DOCUMENTATION)
312
+ // ============================================================================
313
+
314
+ /**
315
+ * Example usage interfaces (for documentation purposes)
316
+ */
317
+ export interface UsageExamples {
318
+ // ✅ CORRECT EXAMPLES
319
+ correctBoxUsage: {
320
+ display: "flex";
321
+ gap: SpaceToken; // ✅ Use theme token
322
+ p: SpaceToken; // ✅ Use theme token
323
+ size: SpaceToken; // ✅ Use 'size' not '$size' for Box
324
+ };
325
+
326
+ correctTextUsage: {
327
+ as: TextElement; // ✅ ALWAYS specify for semantic HTML
328
+ fontSize: FontSizeToken; // ✅ Use theme token
329
+ fontWeight: FontWeightToken;
330
+ color: ColorToken;
331
+ };
332
+
333
+ correctButtonUsage: {
334
+ variant: ButtonVariant;
335
+ $size: ComponentSize; // ✅ Use '$size' (with $) for Button
336
+ // NOT 'size' // ❌ Common mistake
337
+ };
338
+
339
+ correctFieldUsage: {
340
+ as: FieldElement;
341
+ type?: InputType;
342
+ variant: FieldVariant;
343
+ $size: ComponentSize; // ✅ Use '$size' (with $) for Field
344
+ // NOT 'size' // ❌ Common mistake
345
+ };
346
+
347
+ // ❌ INCORRECT EXAMPLES (DON'T DO THIS)
348
+ incorrectExamples: {
349
+ // Wrong: Using Box for text
350
+ // <Box as="h1" fontSize="xl">Title</Box>
351
+ // Should be: <Text as="h1" fontSize="xl">Title</Text>
352
+
353
+ // Wrong: Using arbitrary values
354
+ // <Box p="16px" gap={8} />
355
+ // Should be: <Box p="m" gap="xs" />
356
+
357
+ // Wrong: Using 'size' on Button
358
+ // <Button size="medium">Click</Button>
359
+ // Should be: <Button $size="medium">Click</Button>
360
+
361
+ // Wrong: Using '$size' on Box
362
+ // <Box $size="m">Content</Box>
363
+ // Should be: <Box size="m">Content</Box>
364
+
365
+ // Wrong: Missing 'as' on Text
366
+ // <Text fontSize="xl">Heading</Text>
367
+ // Should be: <Text as="h1" fontSize="xl">Heading</Text>
368
+ };
369
+ }
370
+
371
+ // ============================================================================
372
+ // TYPE GUARDS (HELPER FUNCTIONS)
373
+ // ============================================================================
374
+
375
+ /**
376
+ * Check if a value is a valid SpaceToken
377
+ */
378
+ export function isSpaceToken(value: any): value is SpaceToken {
379
+ return ["mini", "xs", "s", "m", "l", "xl", "xxl", "xxxl"].includes(value);
380
+ }
381
+
382
+ /**
383
+ * Check if a value is a valid ComponentSize
384
+ */
385
+ export function isComponentSize(value: any): value is ComponentSize {
386
+ return ["small", "medium", "large"].includes(value);
387
+ }
388
+
389
+ /**
390
+ * Check if a value is a valid FontSizeToken
391
+ */
392
+ export function isFontSizeToken(value: any): value is FontSizeToken {
393
+ return ["xs", "s", "m", "l", "xl", "xxl", "xxxl"].includes(value);
394
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Ref-counted theme-color override for overlay components (Modal, Drawer, etc.).
3
+ *
4
+ * Multiple overlays can be open at once (e.g. a confirmation dialog inside a
5
+ * modal). We only restore the original theme-color when the *last* overlay
6
+ * unmounts.
7
+ */
8
+
9
+ let themeColorRefCount = 0;
10
+ let themeColorOriginal: string | null = null;
11
+ let htmlBgOriginal: string | null = null;
12
+ let bodyOverflowOriginal: string | null = null;
13
+
14
+ const OVERLAY_THEME_COLOR = "#000000";
15
+
16
+ /**
17
+ * Only tint the Safari chrome / html background on touch devices
18
+ * where the address-bar colour is visible. On desktop it's purely jarring.
19
+ */
20
+ const isTouchDevice = () =>
21
+ typeof window !== "undefined" &&
22
+ ("ontouchstart" in window || navigator.maxTouchPoints > 0);
23
+
24
+ export function pushThemeColor() {
25
+ if (themeColorRefCount === 0) {
26
+ if (isTouchDevice()) {
27
+ const meta = document.querySelector('meta[name="theme-color"]');
28
+ if (meta) {
29
+ themeColorOriginal = meta.getAttribute("content");
30
+ meta.setAttribute("content", OVERLAY_THEME_COLOR);
31
+ }
32
+ const html = document.documentElement;
33
+ htmlBgOriginal = html.style.backgroundColor;
34
+ html.style.backgroundColor = OVERLAY_THEME_COLOR;
35
+ }
36
+
37
+ // Lock body scroll while any overlay is open
38
+ bodyOverflowOriginal = document.body.style.overflow;
39
+ document.body.style.overflow = "hidden";
40
+ }
41
+ themeColorRefCount++;
42
+ }
43
+
44
+ export function popThemeColor() {
45
+ themeColorRefCount = Math.max(0, themeColorRefCount - 1);
46
+ if (themeColorRefCount === 0) {
47
+ if (themeColorOriginal !== null) {
48
+ const meta = document.querySelector('meta[name="theme-color"]');
49
+ if (meta) meta.setAttribute("content", themeColorOriginal);
50
+ themeColorOriginal = null;
51
+ }
52
+ if (htmlBgOriginal !== null) {
53
+ document.documentElement.style.backgroundColor = htmlBgOriginal;
54
+ htmlBgOriginal = null;
55
+ }
56
+
57
+ // Restore body scroll
58
+ document.body.style.overflow = bodyOverflowOriginal ?? "";
59
+ bodyOverflowOriginal = null;
60
+ }
61
+ }
@@ -0,0 +1,15 @@
1
+ interface PickColorOptions {
2
+ sat?: string;
3
+ lum?: string;
4
+ }
5
+
6
+ export function pickColor(
7
+ stringInput: string,
8
+ { sat = "42%", lum = "42%" }: PickColorOptions = {},
9
+ ): string {
10
+ const stringUniqueHash = stringInput.split("").reduce((acc, char) => {
11
+ return char.charCodeAt(0) + ((acc << 12) - acc);
12
+ }, 0);
13
+
14
+ return `hsla(${stringUniqueHash % 360}, ${sat}, ${lum}, 1)`;
15
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": ["DOM", "DOM.Iterable", "ESNext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "esModuleInterop": true,
10
+ "module": "ESNext",
11
+ "moduleResolution": "Bundler",
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "jsx": "react-jsx",
15
+ "incremental": true,
16
+ "baseUrl": ".",
17
+ "paths": {
18
+ "@/design-system/*": ["./src/*"],
19
+ "@/*": ["./src/*"]
20
+ }
21
+ },
22
+ "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.js"],
23
+ "exclude": ["node_modules", "dist"]
24
+ }