@developer_tribe/react-builder 1.0.5 → 1.0.7

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 (48) hide show
  1. package/dist/build-components/index.d.ts +1 -2
  2. package/dist/build-components/patterns.generated.d.ts +0 -383
  3. package/dist/components/BottomBar.d.ts +12 -0
  4. package/dist/components/LoadingComponent.d.ts +1 -0
  5. package/dist/index.cjs.js +3 -3
  6. package/dist/index.cjs.js.map +1 -1
  7. package/dist/index.d.ts +2 -1
  8. package/dist/index.esm.js +3 -3
  9. package/dist/index.esm.js.map +1 -1
  10. package/dist/index.native.cjs.js +1 -1
  11. package/dist/index.native.cjs.js.map +1 -1
  12. package/dist/index.native.esm.js +4 -4
  13. package/dist/index.native.esm.js.map +1 -1
  14. package/dist/modals/ScreenColorsModal.d.ts +8 -0
  15. package/dist/modals/index.d.ts +1 -0
  16. package/dist/pages/ProjectPage.d.ts +3 -2
  17. package/dist/styles.css +1 -1
  18. package/dist/utils/nodeTree.d.ts +5 -0
  19. package/package.json +5 -2
  20. package/src/build-components/RenderNode.generated.tsx +0 -7
  21. package/src/build-components/index.ts +0 -5
  22. package/src/build-components/patterns.generated.ts +0 -399
  23. package/src/components/BottomBar.tsx +242 -0
  24. package/src/components/LoadingComponent.tsx +10 -0
  25. package/src/index.ts +2 -1
  26. package/src/modals/ScreenColorsModal.tsx +121 -0
  27. package/src/modals/index.ts +1 -0
  28. package/src/pages/ProjectPage.tsx +72 -163
  29. package/src/styles/base/_global.scss +25 -20
  30. package/src/styles/components/_attributes-editor.scss +26 -24
  31. package/src/styles/components/_bottom-bar.scss +101 -0
  32. package/src/styles/components/_editor-shell.scss +19 -18
  33. package/src/styles/components/_mockos-router.scss +16 -14
  34. package/src/styles/components/_ui-components.scss +14 -15
  35. package/src/styles/foundation/_colors.scss +28 -8
  36. package/src/styles/foundation/_mixins.scss +1 -1
  37. package/src/styles/foundation/_sizes.scss +4 -2
  38. package/src/styles/index.scss +1 -0
  39. package/src/styles/layout/_builder.scss +1 -1
  40. package/src/styles/modals/_add-component.scss +2 -2
  41. package/src/styles/modals/_color-modal.scss +2 -2
  42. package/src/styles/modals/_modal-shell.scss +1 -1
  43. package/src/utils/nodeTree.ts +99 -0
  44. package/dist/build-components/PaywallSubscriButton/PaywallSubscriButton.d.ts +0 -5
  45. package/dist/build-components/PaywallSubscriButton/PaywallSubscriButtonProps.generated.d.ts +0 -50
  46. package/src/build-components/PaywallSubscriButton/PaywallSubscriButton.tsx +0 -10
  47. package/src/build-components/PaywallSubscriButton/PaywallSubscriButtonProps.generated.ts +0 -77
  48. package/src/build-components/PaywallSubscriButton/pattern.json +0 -27
@@ -0,0 +1,121 @@
1
+ import React, { useMemo } from 'react';
2
+ import type { AppConfig } from '../types/PreviewConfig';
3
+ import { defaultAppConfig } from '../types/PreviewConfig';
4
+ import Modal from './Modal';
5
+
6
+ type ScreenMode = 'light' | 'dark';
7
+ type ScreenColorKey = keyof AppConfig['screenStyle']['light'];
8
+
9
+ type ScreenColorsModalProps = {
10
+ appConfig: AppConfig;
11
+ onChange: (next: AppConfig) => void;
12
+ onClose: () => void;
13
+ };
14
+
15
+ type ColorField = {
16
+ id: string;
17
+ label: string;
18
+ mode: ScreenMode;
19
+ key: ScreenColorKey;
20
+ };
21
+
22
+ const colorFields: ColorField[] = [
23
+ {
24
+ id: 'light-bg',
25
+ label: 'Light Background',
26
+ mode: 'light',
27
+ key: 'backgroundColor',
28
+ },
29
+ {
30
+ id: 'light-color',
31
+ label: 'Light Text',
32
+ mode: 'light',
33
+ key: 'color',
34
+ },
35
+ {
36
+ id: 'dark-bg',
37
+ label: 'Dark Background',
38
+ mode: 'dark',
39
+ key: 'backgroundColor',
40
+ },
41
+ {
42
+ id: 'dark-color',
43
+ label: 'Dark Text',
44
+ mode: 'dark',
45
+ key: 'color',
46
+ },
47
+ ];
48
+
49
+ export function ScreenColorsModal({
50
+ appConfig,
51
+ onChange,
52
+ onClose,
53
+ }: ScreenColorsModalProps) {
54
+ const defaults = defaultAppConfig.screenStyle;
55
+
56
+ const getValue = (mode: ScreenMode, key: ScreenColorKey) =>
57
+ appConfig.screenStyle?.[mode]?.[key] ?? defaults[mode][key];
58
+
59
+ const handleChange = (mode: ScreenMode, key: ScreenColorKey, value: string) =>
60
+ onChange({
61
+ ...appConfig,
62
+ screenStyle: {
63
+ ...defaults,
64
+ ...appConfig.screenStyle,
65
+ [mode]: {
66
+ ...defaults[mode],
67
+ ...appConfig.screenStyle?.[mode],
68
+ [key]: value,
69
+ },
70
+ },
71
+ });
72
+
73
+ const headerDescription = useMemo(
74
+ () =>
75
+ 'Edit light/dark screen background & text colors used in the preview.',
76
+ [],
77
+ );
78
+
79
+ return (
80
+ <Modal
81
+ onClose={onClose}
82
+ ariaLabelledBy="screen-colors-title"
83
+ contentClassName="localication-modal__content"
84
+ >
85
+ <div className="modal__header localication-modal__header">
86
+ <div className="localication-modal__header-main">
87
+ <h3 id="screen-colors-title" className="modal__title">
88
+ Screen Colors
89
+ </h3>
90
+ <p className="localication-modal__description">{headerDescription}</p>
91
+ </div>
92
+ <button type="button" className="editor-button" onClick={onClose}>
93
+ Close
94
+ </button>
95
+ </div>
96
+
97
+ <div className="localication-modal__body">
98
+ <div
99
+ style={{
100
+ display: 'grid',
101
+ gridTemplateColumns: 'repeat(2, minmax(0, 1fr))',
102
+ gap: 12,
103
+ }}
104
+ >
105
+ {colorFields.map(({ id, label, mode, key }) => (
106
+ <React.Fragment key={id}>
107
+ <div style={{ alignSelf: 'center' }}>{label}</div>
108
+ <input
109
+ id={id}
110
+ type="color"
111
+ className="input input--color"
112
+ value={String(getValue(mode, key))}
113
+ onChange={(e) => handleChange(mode, key, e.target.value)}
114
+ />
115
+ </React.Fragment>
116
+ ))}
117
+ </div>
118
+ </div>
119
+ </Modal>
120
+ );
121
+ }
@@ -4,6 +4,7 @@ export { DeviceSelectorModal } from './DeviceSelectorModal';
4
4
  export { ColorModal } from './ColorModal';
5
5
  export { IconPickerModal } from './IconPickerModal';
6
6
  export { LocalicationModal } from './LocalicationModal';
7
+ export { ScreenColorsModal } from './ScreenColorsModal';
7
8
  export { MockableFeatureModal } from './MockableFeatureModal';
8
9
  export { ProductEditModal } from './ProductEditModal';
9
10
  export { ProductPresetsModal } from './ProductPresetsModal';
@@ -1,12 +1,12 @@
1
1
  import { useCallback, useEffect, useState } from 'react';
2
- import type { Node, NodeData } from '../types/Node';
2
+ import type { Node } from '../types/Node';
3
3
  import type { Project, ProjectColors } from '../types/Project';
4
4
  import { RenderPage } from '../RenderPage';
5
5
  import { EditorHeader } from '../components/EditorHeader';
6
6
  import { AttributesEditorPanel } from '../components/AttributesEditorPanel';
7
7
  import { BuilderProvider } from '../components/BuilderProvider';
8
8
  import { BuilderPanel } from './tabs/BuilderPanel';
9
- import { SideTool } from './tabs/SideTool';
9
+ import { BottomBar } from '../components/BottomBar';
10
10
  import { AppConfig, defaultAppConfig } from '../types/PreviewConfig';
11
11
  import { useRenderStore } from '../store';
12
12
  import { logger } from '../utils/logger';
@@ -15,15 +15,21 @@ import type { LogLevel } from '../types/Project';
15
15
  import { analyseAndProccess } from '../utils/analyseNode';
16
16
  import backgroundImage from '../assets/images/background.jpg';
17
17
  import type { PaywallBenefits } from '../paywall/types/benefits';
18
- import Lottie from 'lottie-react';
19
- import loadingAnimation from '../assets/loading_animation.json';
18
+ import { LoadingComponent } from '../components/LoadingComponent';
19
+ import {
20
+ deleteNodeFromTree,
21
+ findNodeByKey,
22
+ isNodeRecord,
23
+ nodeHasChild,
24
+ } from '../utils/nodeTree';
20
25
  export type ProjectPageProps = {
21
26
  project: Project;
22
27
  onSaveProject: (project: Project) => void;
23
28
  appConfig?: AppConfig;
24
29
  logLevel?: LogLevel;
25
30
  projectColors?: ProjectColors;
26
- name: string;
31
+ onSaveProjectColors?: (colors: ProjectColors) => void;
32
+ name?: string;
27
33
  };
28
34
 
29
35
  const MOBILE_BREAKPOINT = 1000;
@@ -34,9 +40,11 @@ export function ProjectPage({
34
40
  onSaveProject,
35
41
  logLevel,
36
42
  projectColors,
43
+ onSaveProjectColors,
37
44
  name,
38
45
  }: ProjectPageProps) {
39
46
  useLogRender('ProjectPage');
47
+ const resolvedName = name ?? project.name;
40
48
  const resolvedProjectColors = projectColors ?? project.projectColors;
41
49
  const {
42
50
  current,
@@ -45,7 +53,7 @@ export function ProjectPage({
45
53
  setProjectName,
46
54
  products,
47
55
  benefits,
48
- } = useRenderStore((s) => ({
56
+ } = useRenderStore(s => ({
49
57
  current: s.current,
50
58
  setCurrent: s.setCurrent,
51
59
  setProjectColors: s.setProjectColors,
@@ -69,7 +77,7 @@ export function ProjectPage({
69
77
  // Extra warning for deleting the root node (editorData)
70
78
  if (nodeToDelete === editorData) {
71
79
  const shouldDeleteRoot = window.confirm(
72
- 'You are about to delete the root component. This will clear the entire screen. Continue?',
80
+ 'You are about to delete the root component. This will clear the entire screen. Continue?'
73
81
  );
74
82
  if (!shouldDeleteRoot) return;
75
83
  setEditorData(null);
@@ -95,7 +103,7 @@ export function ProjectPage({
95
103
  }
96
104
  }
97
105
  },
98
- [editorData, current],
106
+ [editorData, current]
99
107
  );
100
108
 
101
109
  useEffect(() => {
@@ -108,8 +116,8 @@ export function ProjectPage({
108
116
  }, [appConfig, project.name]);
109
117
 
110
118
  useEffect(() => {
111
- setProjectName(name);
112
- }, [name, setProjectName]);
119
+ setProjectName(resolvedName);
120
+ }, [resolvedName, setProjectName]);
113
121
 
114
122
  useEffect(() => {
115
123
  setProjectColors(resolvedProjectColors);
@@ -136,7 +144,7 @@ export function ProjectPage({
136
144
  }, [isMobile]);
137
145
 
138
146
  const toggleMobilePanel = (panel: 'builder' | 'attributes') => {
139
- setMobilePanel((prev) => (prev === panel ? null : panel));
147
+ setMobilePanel(prev => (prev === panel ? null : panel));
140
148
  };
141
149
 
142
150
  const closeMobilePanels = () => {
@@ -176,10 +184,13 @@ export function ProjectPage({
176
184
  const showLoading = editorData === null || !minLoadingDelayDone;
177
185
 
178
186
  return (
179
- <div className="container-full">
187
+ <div className='container-full'>
180
188
  <EditorHeader
181
189
  onSaveProject={() => {
182
190
  logger.info('ProjectPage', 'save project', { name: project.name });
191
+ if (onSaveProjectColors && resolvedProjectColors) {
192
+ onSaveProjectColors(resolvedProjectColors);
193
+ }
183
194
  onSaveProject({
184
195
  ...project,
185
196
  data: editorData,
@@ -196,65 +207,65 @@ export function ProjectPage({
196
207
  />
197
208
  {isMobile && (
198
209
  <div
199
- className="mobile-panel-toggle"
200
- role="group"
201
- aria-label="Editor panels"
210
+ className='mobile-panel-toggle'
211
+ role='group'
212
+ aria-label='Editor panels'
202
213
  >
203
214
  <button
204
- type="button"
215
+ type='button'
205
216
  className={`mobile-panel-toggle__button${mobilePanel === 'builder' ? ' mobile-panel-toggle__button--active' : ''}`}
206
- aria-label="Toggle builder panel"
217
+ aria-label='Toggle builder panel'
207
218
  aria-expanded={mobilePanel === 'builder'}
208
- aria-controls="split-left-panel"
219
+ aria-controls='split-left-panel'
209
220
  onClick={() => toggleMobilePanel('builder')}
210
221
  >
211
- <span className="mobile-panel-toggle__icon" aria-hidden="true">
212
- <svg viewBox="0 0 16 12" role="presentation" focusable="false">
222
+ <span className='mobile-panel-toggle__icon' aria-hidden='true'>
223
+ <svg viewBox='0 0 16 12' role='presentation' focusable='false'>
213
224
  <path
214
- d="M1 1h14M1 6h14M1 11h14"
215
- stroke="currentColor"
216
- strokeWidth="2"
217
- strokeLinecap="round"
218
- fill="none"
225
+ d='M1 1h14M1 6h14M1 11h14'
226
+ stroke='currentColor'
227
+ strokeWidth='2'
228
+ strokeLinecap='round'
229
+ fill='none'
219
230
  />
220
231
  </svg>
221
232
  </span>
222
- <span className="mobile-panel-toggle__label">Builder</span>
233
+ <span className='mobile-panel-toggle__label'>Builder</span>
223
234
  </button>
224
235
  <button
225
- type="button"
236
+ type='button'
226
237
  className={`mobile-panel-toggle__button${mobilePanel === 'attributes' ? ' mobile-panel-toggle__button--active' : ''}`}
227
- aria-label="Toggle attributes panel"
238
+ aria-label='Toggle attributes panel'
228
239
  aria-expanded={mobilePanel === 'attributes'}
229
- aria-controls="split-attributes-panel"
240
+ aria-controls='split-attributes-panel'
230
241
  onClick={() => toggleMobilePanel('attributes')}
231
242
  >
232
- <span className="mobile-panel-toggle__icon" aria-hidden="true">
233
- <svg viewBox="0 0 16 12" role="presentation" focusable="false">
243
+ <span className='mobile-panel-toggle__icon' aria-hidden='true'>
244
+ <svg viewBox='0 0 16 12' role='presentation' focusable='false'>
234
245
  <path
235
- d="M1 1h14M1 6h14M1 11h14"
236
- stroke="currentColor"
237
- strokeWidth="2"
238
- strokeLinecap="round"
239
- fill="none"
246
+ d='M1 1h14M1 6h14M1 11h14'
247
+ stroke='currentColor'
248
+ strokeWidth='2'
249
+ strokeLinecap='round'
250
+ fill='none'
240
251
  />
241
252
  </svg>
242
253
  </span>
243
- <span className="mobile-panel-toggle__label">Attributes</span>
254
+ <span className='mobile-panel-toggle__label'>Attributes</span>
244
255
  </button>
245
256
  </div>
246
257
  )}
247
- <div className="editor-container">
258
+ <div className='editor-container'>
248
259
  <div
249
- id="split-left-panel"
260
+ id='split-left-panel'
250
261
  className={`split-left${leftPanelIsOpen ? ' is-open' : ''}`}
251
262
  aria-hidden={isMobile && !leftPanelIsOpen}
252
263
  >
253
264
  {isMobile && (
254
265
  <button
255
- type="button"
256
- className="split-panel__close"
257
- aria-label="Close builder panel"
266
+ type='button'
267
+ className='split-panel__close'
268
+ aria-label='Close builder panel'
258
269
  onClick={closeMobilePanels}
259
270
  >
260
271
  Close
@@ -269,13 +280,15 @@ export function ProjectPage({
269
280
  </div>
270
281
  </div>
271
282
  <div
272
- style={{ backgroundImage: `url(${backgroundImage})` }}
273
- className="split-right"
283
+ style={{
284
+ // Set as a CSS variable so `.dark .split-right` can override it.
285
+ // eslint-disable-next-line @typescript-eslint/naming-convention
286
+ ['--rb-canvas-bg' as any]: `url(${backgroundImage})`,
287
+ }}
288
+ className='split-right'
274
289
  >
275
- <SideTool data={editorData} setData={setEditorData} />
276
-
277
290
  {showLoading && (
278
- <div className="rb-loading-overlay" aria-busy="true">
291
+ <div className='rb-loading-overlay' aria-busy='true'>
279
292
  <LoadingComponent />
280
293
  </div>
281
294
  )}
@@ -290,20 +303,22 @@ export function ProjectPage({
290
303
  : {},
291
304
  }}
292
305
  >
293
- <RenderPage data={editorData} name={project.name} />
306
+ <RenderPage data={editorData} name={resolvedName} />
294
307
  </BuilderProvider>
295
308
  )}
296
309
  </div>
310
+ {/* BOTOM BAR */}
311
+ <BottomBar data={editorData} setData={setEditorData} />
297
312
  <div
298
- id="split-attributes-panel"
313
+ id='split-attributes-panel'
299
314
  className={`split-third${attributesPanelIsOpen ? ' is-open' : ''}`}
300
315
  aria-hidden={isMobile && !attributesPanelIsOpen}
301
316
  >
302
317
  {isMobile && (
303
318
  <button
304
- type="button"
305
- className="split-panel__close"
306
- aria-label="Close attributes panel"
319
+ type='button'
320
+ className='split-panel__close'
321
+ aria-label='Close attributes panel'
307
322
  onClick={closeMobilePanels}
308
323
  >
309
324
  Close
@@ -312,7 +327,7 @@ export function ProjectPage({
312
327
  <AttributesEditorPanel
313
328
  attributes={editorData}
314
329
  projectColors={resolvedProjectColors}
315
- onChange={(data) => {
330
+ onChange={data => {
316
331
  setEditorData(data);
317
332
  let nodeKey: string | undefined = undefined;
318
333
  if (
@@ -326,16 +341,16 @@ export function ProjectPage({
326
341
  logger.verbose(
327
342
  'ProjectPage',
328
343
  'attributes change',
329
- nodeKey ? { nodeKey } : undefined,
344
+ nodeKey ? { nodeKey } : undefined
330
345
  );
331
346
  }}
332
347
  />
333
348
  </div>
334
349
  {isMobile && mobilePanel && (
335
350
  <button
336
- type="button"
337
- className="editor-container__overlay"
338
- aria-label="Close active panel"
351
+ type='button'
352
+ className='editor-container__overlay'
353
+ aria-label='Close active panel'
339
354
  onClick={closeMobilePanels}
340
355
  />
341
356
  )}
@@ -343,109 +358,3 @@ export function ProjectPage({
343
358
  </div>
344
359
  );
345
360
  }
346
-
347
- function LoadingComponent() {
348
- return (
349
- <div className="rb-loading">
350
- <Lottie animationData={loadingAnimation as any} loop autoplay />
351
- </div>
352
- );
353
- }
354
-
355
- function deleteNodeFromTree(root: Node, target: Node): Node {
356
- if (root === null || root === undefined) return root;
357
- if (typeof root === 'string') return root;
358
-
359
- if (Array.isArray(root)) {
360
- let changed = false;
361
- const nextChildren: Node[] = [];
362
- for (const child of root) {
363
- if (child === target) {
364
- changed = true;
365
- continue;
366
- }
367
- const nextChild = deleteNodeFromTree(child, target);
368
- if (nextChild !== child) changed = true;
369
- nextChildren.push(nextChild);
370
- }
371
- return changed ? nextChildren : root;
372
- }
373
-
374
- const data = root as any;
375
- if ('children' in data) {
376
- const prev = data.children as Node;
377
- if (!prev) return root;
378
-
379
- if (Array.isArray(prev)) {
380
- let changed = false;
381
- const nextChildren: Node[] = [];
382
- for (const child of prev) {
383
- if (child === target) {
384
- changed = true;
385
- continue;
386
- }
387
- const nextChild = deleteNodeFromTree(child, target);
388
- if (nextChild !== child) changed = true;
389
- nextChildren.push(nextChild);
390
- }
391
- if (changed) {
392
- return { ...data, children: nextChildren } as Node;
393
- }
394
- return root;
395
- }
396
-
397
- if (prev === target) {
398
- return { ...data, children: '' } as Node;
399
- }
400
-
401
- const nextChild = deleteNodeFromTree(prev, target);
402
- if (nextChild !== prev) {
403
- return { ...data, children: nextChild } as Node;
404
- }
405
- }
406
-
407
- return root;
408
- }
409
-
410
- function isNodeRecord(node: Node): node is NodeData {
411
- return (
412
- node !== null &&
413
- node !== undefined &&
414
- typeof node === 'object' &&
415
- !Array.isArray(node)
416
- );
417
- }
418
-
419
- function nodeHasChild(parent: NodeData, potentialChild: Node): boolean {
420
- const { children } = parent;
421
- if (!children) return false;
422
- if (Array.isArray(children)) {
423
- return children.some((child) => child === potentialChild);
424
- }
425
- return children === potentialChild;
426
- }
427
-
428
- function findNodeByKey(root: Node, key?: string): Node | null {
429
- if (!key) return null;
430
- if (root === null || root === undefined) return null;
431
- if (typeof root === 'string') return null;
432
-
433
- if (Array.isArray(root)) {
434
- for (const child of root) {
435
- const found = findNodeByKey(child, key);
436
- if (found) return found;
437
- }
438
- return null;
439
- }
440
-
441
- const nodeData = root as NodeData;
442
- if (nodeData.key === key) {
443
- return nodeData;
444
- }
445
-
446
- if (nodeData.children) {
447
- return findNodeByKey(nodeData.children as Node, key);
448
- }
449
-
450
- return null;
451
- }
@@ -2,7 +2,6 @@
2
2
  @use '../foundation/sizes' as sizes;
3
3
  @use '../foundation/typography' as type;
4
4
  @use '../foundation/mixins' as *;
5
- @use 'sass:color';
6
5
 
7
6
  /* Global utility classes */
8
7
  html,
@@ -37,7 +36,7 @@ body,
37
36
  height: 120px;
38
37
  border: 2px dashed colors.$mutedTextColor;
39
38
  border-radius: sizes.$radiusRounded;
40
- background: #fff;
39
+ background: colors.$surfaceColor;
41
40
  cursor: pointer;
42
41
  font-weight: 600;
43
42
  color: colors.$textColor;
@@ -45,12 +44,12 @@ body,
45
44
  border-color 0.2s ease,
46
45
  transform 0.1s ease,
47
46
  box-shadow 0.2s ease;
48
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
47
+ box-shadow: 0 1px 2px hsl(var(--foreground, 220.9 39.3% 11%) / 0.04);
49
48
 
50
49
  &:hover {
51
- border-color: color.adjust(colors.$mutedTextColor, $lightness: -10%);
50
+ border-color: colors.$mutedTextColor;
52
51
  transform: translateY(-1px);
53
- box-shadow: 0 6px 16px rgba(0, 0, 0, 0.1);
52
+ box-shadow: 0 6px 16px hsl(var(--foreground, 220.9 39.3% 11%) / 0.1);
54
53
  }
55
54
  }
56
55
 
@@ -72,6 +71,7 @@ body,
72
71
  max-height: calc(100vh - 120px);
73
72
  overflow: auto;
74
73
  padding: sizes.$spaceComfy;
74
+ background-image: var(--rb-canvas-bg, none);
75
75
  background-size: cover;
76
76
  background-position: center;
77
77
  background-repeat: repeat;
@@ -82,6 +82,11 @@ body,
82
82
  }
83
83
  }
84
84
 
85
+ /* Hide the canvas grid background in dark theme (Tailwind/shadcn convention). */
86
+ .dark .split-right {
87
+ --rb-canvas-bg: none !important;
88
+ }
89
+
85
90
  .split-third {
86
91
  flex: 1 1 25%;
87
92
  border-right: 1px solid colors.$borderColor;
@@ -94,10 +99,10 @@ body,
94
99
  .stage {
95
100
  @include card;
96
101
  align-self: center;
97
- border: 1px solid color.adjust(colors.$borderColor, $lightness: -10%);
102
+ border: 1px solid colors.$borderColor;
98
103
  box-shadow:
99
- 0 18px 50px rgba(0, 0, 0, 0.16),
100
- 0 6px 16px rgba(0, 0, 0, 0.1);
104
+ 0 18px 50px hsl(var(--foreground, 220.9 39.3% 11%) / 0.16),
105
+ 0 6px 16px hsl(var(--foreground, 220.9 39.3% 11%) / 0.1);
101
106
  overflow: hidden;
102
107
  .scroll-container {
103
108
  /* Mobile-like scrollbar styling */
@@ -111,19 +116,19 @@ body,
111
116
  }
112
117
 
113
118
  &::-webkit-scrollbar-thumb {
114
- background: rgba(0, 0, 0, 0.25);
119
+ background: hsl(var(--foreground, 220.9 39.3% 11%) / 0.25);
115
120
  border-radius: 8px;
116
121
  }
117
122
 
118
123
  /* For Firefox */
119
124
  scrollbar-width: thin;
120
- scrollbar-color: rgba(0, 0, 0, 0.25) transparent;
125
+ scrollbar-color: hsl(var(--foreground, 220.9 39.3% 11%) / 0.25) transparent;
121
126
  }
122
127
  }
123
128
 
124
129
  /* Builder preview selection helpers */
125
130
  .rb-node-selected {
126
- border: 1px solid #2684ff;
131
+ border: 1px solid colors.$accentColor;
127
132
  }
128
133
 
129
134
  /* Header */
@@ -131,7 +136,7 @@ body,
131
136
  position: sticky;
132
137
  top: 0;
133
138
  z-index: 10;
134
- background: #fff;
139
+ background: colors.$surfaceColor;
135
140
  border-bottom: 1px solid colors.$borderColor;
136
141
  height: 60px;
137
142
  padding: 0 sizes.$spaceComfy;
@@ -251,7 +256,7 @@ body,
251
256
  padding: sizes.$spaceCozy;
252
257
  border-radius: sizes.$radiusRounded;
253
258
  border: 1px solid colors.$borderColor;
254
- background: #fff;
259
+ background: colors.$surfaceColor;
255
260
  color: colors.$textColor;
256
261
  font-weight: 600;
257
262
  cursor: pointer;
@@ -269,9 +274,9 @@ body,
269
274
 
270
275
  .mobile-panel-toggle__button--active {
271
276
  background: colors.$textColor;
272
- color: #fff;
277
+ color: colors.$surfaceColor;
273
278
  border-color: colors.$textColor;
274
- box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15);
279
+ box-shadow: 0 6px 16px hsl(var(--foreground, 220.9 39.3% 11%) / 0.15);
275
280
  }
276
281
 
277
282
  .mobile-panel-toggle__icon {
@@ -296,8 +301,8 @@ body,
296
301
  right: sizes.$spaceCozy;
297
302
  border: none;
298
303
  border-radius: sizes.$radiusRounded;
299
- background: rgba(0, 0, 0, 0.7);
300
- color: #fff;
304
+ background: hsl(var(--foreground, 220.9 39.3% 11%) / 0.7);
305
+ color: colors.$surfaceColor;
301
306
  padding: sizes.$spaceSnug sizes.$spaceComfy;
302
307
  font-weight: 600;
303
308
  cursor: pointer;
@@ -325,7 +330,7 @@ body,
325
330
  width: 100%;
326
331
  height: 100%;
327
332
  background: colors.$canvasColor;
328
- box-shadow: 0 25px 60px rgba(0, 0, 0, 0.15);
333
+ box-shadow: 0 25px 60px hsl(var(--foreground, 220.9 39.3% 11%) / 0.15);
329
334
  transition:
330
335
  transform 0.3s ease,
331
336
  opacity 0.3s ease;
@@ -370,7 +375,7 @@ body,
370
375
  display: block;
371
376
  position: absolute;
372
377
  inset: 0;
373
- background: rgba(17, 24, 39, 0.35);
378
+ background: hsl(var(--foreground, 220.9 39.3% 11%) / 0.35);
374
379
  border: none;
375
380
  padding: 0;
376
381
  margin: 0;
@@ -404,7 +409,7 @@ body,
404
409
  align-items: center;
405
410
  justify-content: center;
406
411
  pointer-events: none;
407
- background: rgba(255, 255, 255, 0.55);
412
+ background: hsl(var(--card, 0 0% 100%) / 0.55);
408
413
  backdrop-filter: blur(1px);
409
414
  }
410
415