@walkeros/explorer 0.3.0 → 0.5.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/STYLE.md ADDED
@@ -0,0 +1,993 @@
1
+ # Explorer Styling Guide
2
+
3
+ Complete guide to styling, theming, and customization in walkerOS Explorer.
4
+
5
+ **Quick Links:** [Quick Start](#quick-start) · [Theme Variables](#theme-variables) · [Grid System](#grid-system) · [Monaco Editor](#monaco-editor) · [SCSS Architecture](#scss-architecture)
6
+
7
+ ---
8
+
9
+ ## Quick Start
10
+
11
+ ### Theme Switching
12
+
13
+ Explorer supports two built-in themes via the `data-theme` attribute:
14
+
15
+ ```html
16
+ <!-- Dark theme (default) -->
17
+ <div data-theme="dark">
18
+ <YourExplorerComponents />
19
+ </div>
20
+
21
+ <!-- Light theme -->
22
+ <div data-theme="light">
23
+ <YourExplorerComponents />
24
+ </div>
25
+ ```
26
+
27
+ Theme detection priority:
28
+ 1. Closest ancestor `data-theme` attribute
29
+ 2. Document root `data-theme` attribute
30
+ 3. System preference via `prefers-color-scheme`
31
+
32
+ ### Customizing Colors
33
+
34
+ Override CSS variables in your stylesheet:
35
+
36
+ ```css
37
+ [data-theme="dark"] {
38
+ --color-primary: #your-brand-color;
39
+ --bg-box: #your-background;
40
+ --text-primary: #your-text-color;
41
+ }
42
+ ```
43
+
44
+ ### Required Import
45
+
46
+ ```tsx
47
+ import '@walkeros/explorer/styles.css';
48
+ ```
49
+
50
+ ---
51
+
52
+ ## Theme Variables
53
+
54
+ ### Complete Variable Reference
55
+
56
+ #### Text Colors
57
+
58
+ | Variable | Light | Dark | Usage |
59
+ |----------|-------|------|-------|
60
+ | `--color-text` | `#000` | `#e0e0e0` | Primary text |
61
+ | `--color-text-label` | `#424242` | `#cccccc` | Labels, headers |
62
+ | `--color-text-button` | `#616161` | `#cccccc` | Button text (inactive) |
63
+ | `--color-text-button-hover` | `#424242` | `#ffffff` | Button text on hover |
64
+ | `--color-text-button-active` | `#1f2937` | `#ffffff` | Button text when active |
65
+ | `--color-text-muted` | `#666` | `#999` | Secondary/muted text |
66
+ | `--color-text-toggle` | `#666` | `#999` | Toggle/switch labels |
67
+ | `--color-text-input` | `#000` | `#e0e0e0` | Input field text |
68
+ | `--color-text-placeholder` | `#9ca3af` | `#666` | Input placeholder text |
69
+
70
+ #### Background Colors
71
+
72
+ | Variable | Light | Dark | Usage |
73
+ |----------|-------|------|-------|
74
+ | `--bg-box` | `#ffffff` | `#1e1e1e` | Main container background |
75
+ | `--bg-header` | `#f5f5f5` | `#252526` | Header background |
76
+ | `--bg-footer` | `#f5f5f5` | `#252526` | Footer background |
77
+ | `--bg-button-hover` | `#e8e8e8` | `#2a2d2e` | Button background on hover |
78
+ | `--bg-button-active` | `#ffffff` | `#1e1e1e` | Button background when active |
79
+ | `--bg-button-group` | `#f3f4f6` | `#2a2d2e` | Button group container |
80
+ | `--bg-input` | `#ffffff` | `#252526` | Input field background |
81
+ | `--bg-input-hover` | `#f9f9f9` | `#2a2d2e` | Input field on hover |
82
+ | `--bg-code-inline` | `#f9f9f9` | `rgba(255,255,255,0.05)` | Inline code background |
83
+ | `--bg-dropdown` | `#ffffff` | `#252526` | Dropdown menu background |
84
+ | `--bg-dropdown-option-hover` | `#f0f0f0` | `#2a2d2e` | Dropdown option on hover |
85
+ | `--bg-dropdown-option-highlighted` | `#e3f2fd` | `#1e3a5f` | Highlighted dropdown option |
86
+
87
+ #### Border Colors
88
+
89
+ | Variable | Light | Dark | Usage |
90
+ |----------|-------|------|-------|
91
+ | `--border-box` | `#e0e0e0` | `#3c3c3c` | Main container border |
92
+ | `--border-header` | `#e0e0e0` | `#3c3c3c` | Header border |
93
+ | `--border-footer` | `#e0e0e0` | `#3c3c3c` | Footer border |
94
+ | `--border-button-group` | `#d1d5db` | `#3c3c3c` | Button group borders |
95
+ | `--border-input` | `#d1d5db` | `#3c3c3c` | Input field border |
96
+ | `--border-input-focus` | `#3b82f6` | `#4a90e2` | Input border when focused |
97
+
98
+ #### Button Colors
99
+
100
+ | Variable | Light | Dark | Usage |
101
+ |----------|-------|------|-------|
102
+ | `--color-button-primary` | `#3b82f6` | `#4a90e2` | Primary button background |
103
+ | `--color-button-primary-hover` | `#2563eb` | `#357abd` | Primary button on hover |
104
+ | `--color-button-primary-text` | `#ffffff` | `#ffffff` | Primary button text |
105
+ | `--color-button-danger` | `#ef4444` | `#ef4444` | Danger button background |
106
+ | `--color-button-danger-hover` | `#dc2626` | `#dc2626` | Danger button on hover |
107
+ | `--color-button-danger-text` | `#ffffff` | `#ffffff` | Danger button text |
108
+
109
+ #### Status Colors
110
+
111
+ | Variable | Light | Dark | Usage |
112
+ |----------|-------|------|-------|
113
+ | `--color-status-enabled` | `#22c55e` | `#22c55e` | Enabled/success state |
114
+ | `--color-status-disabled` | `#9ca3af` | `#9ca3af` | Disabled state |
115
+ | `--color-status-warning` | `#f59e0b` | `#f59e0b` | Warning state |
116
+
117
+ #### Highlight Colors
118
+
119
+ Used for code highlighting and data attribute visualization.
120
+
121
+ | Variable | Light | Dark | Usage |
122
+ |----------|-------|------|-------|
123
+ | `--color-highlight-primary` | `#01b5e2` | `#01b5e2` | Primary highlight color |
124
+ | `--highlight-globals` | `#4fc3f7cc` | `#4fc3f7cc` | Global properties highlight |
125
+ | `--highlight-context` | `#ffbd44cc` | `#ffbd44cc` | Context properties highlight |
126
+ | `--highlight-entity` | `#00ca4ecc` | `#00ca4ecc` | Entity name highlight |
127
+ | `--highlight-property` | `#ff605ccc` | `#ff605ccc` | Property name highlight |
128
+ | `--highlight-action` | `#9900ffcc` | `#9900ffcc` | Action name highlight |
129
+ | `--highlight-background` | `#1f2937` | `#1f2937` | Highlight tooltip background |
130
+ | `--highlight-text` | `#9ca3af` | `#9ca3af` | Highlight tooltip text |
131
+ | `--highlight-hover` | `rgba(255,255,255,0.05)` | `rgba(255,255,255,0.05)` | Highlight hover effect |
132
+ | `--highlight-separator` | `rgba(255,255,255,0.05)` | `rgba(255,255,255,0.05)` | Separator in highlights |
133
+
134
+ #### Typography
135
+
136
+ | Variable | Light | Dark | Usage |
137
+ |----------|-------|------|-------|
138
+ | `--font-family-base` | `system-ui, -apple-system, ...` | Same | Base font family |
139
+ | `--font-mono` | `'SF Mono', 'Monaco', ...` | Same | Monospace font for code |
140
+ | `--font-size-base` | `14px` | Same | Base font size |
141
+ | `--font-size-label` | `13px` | Same | Label font size |
142
+ | `--font-size-toggle` | `12px` | Same | Toggle/switch font size |
143
+ | `--font-size-highlight-button` | `0.75rem` | Same | Highlight button font size |
144
+ | `--line-height-base` | `1.5` | Same | Base line height |
145
+ | `--font-weight-normal` | `400` | Same | Normal font weight |
146
+ | `--font-weight-semibold` | `600` | Same | Semibold font weight |
147
+
148
+ #### Spacing & Layout
149
+
150
+ | Variable | Light | Dark | Usage |
151
+ |----------|-------|------|-------|
152
+ | `--spacing-header` | `6px 10px` | Same | Header padding |
153
+ | `--spacing-footer` | `6px 10px` | Same | Footer padding |
154
+ | `--spacing-button` | `4px 8px` | Same | Button padding |
155
+ | `--spacing-button-group` | `1px` | Same | Gap between button group items |
156
+ | `--spacing-grid-gap` | `12px` | Same | Grid gap spacing |
157
+ | `--grid-min-box-width` | `350px` | Same | Minimum box width in grid |
158
+ | `--grid-row-min-height` | `250px` | Same | Minimum grid row height |
159
+ | `--grid-row-max-height` | `450px` | Same | Maximum grid row height |
160
+ | `--grid-box-max-height-mobile` | `500px` | Same | Max box height on mobile |
161
+
162
+ #### Border Radius
163
+
164
+ | Variable | Light | Dark | Usage |
165
+ |----------|-------|------|-------|
166
+ | `--radius-box` | `4px` | Same | Main container border radius |
167
+ | `--radius-button` | `3px` | Same | Button border radius |
168
+ | `--radius-button-group` | `4px` | Same | Button group border radius |
169
+ | `--radius-highlight-button` | `6px` | Same | Highlight button border radius |
170
+
171
+ #### Shadows
172
+
173
+ | Variable | Light | Dark | Usage |
174
+ |----------|-------|------|-------|
175
+ | `--shadow-button-active` | `0 1px 2px rgba(0,0,0,0.1)` | `0 1px 2px rgba(0,0,0,0.3)` | Active button shadow |
176
+ | `--shadow-dropdown` | `0 4px 6px rgba(0,0,0,0.1)` | `0 4px 6px rgba(0,0,0,0.5)` | Dropdown shadow |
177
+
178
+ #### Monaco Editor
179
+
180
+ | Variable | Light | Dark | Usage |
181
+ |----------|-------|------|-------|
182
+ | `--monaco-font-size` | `13px` | Same | Monaco editor font size |
183
+ | `--monaco-line-height` | `1.5` | Same | Monaco editor line height |
184
+
185
+ ### Customization Examples
186
+
187
+ **Custom Brand Colors:**
188
+
189
+ ```css
190
+ .elb-explorer {
191
+ --color-button-primary: #ff6b35;
192
+ --color-button-primary-hover: #ff5722;
193
+ --border-input-focus: #ff6b35;
194
+ }
195
+ ```
196
+
197
+ **Larger Fonts for Accessibility:**
198
+
199
+ ```css
200
+ .elb-explorer {
201
+ --font-size-base: 16px;
202
+ --font-size-label: 15px;
203
+ --monaco-font-size: 15px;
204
+ --line-height-base: 1.6;
205
+ }
206
+ ```
207
+
208
+ **High Contrast Theme:**
209
+
210
+ ```css
211
+ [data-theme='light'] .elb-explorer {
212
+ --color-text: #000000;
213
+ --bg-box: #ffffff;
214
+ --border-box: #000000;
215
+ --color-button-primary: #0000ff;
216
+ }
217
+
218
+ [data-theme='dark'] .elb-explorer {
219
+ --color-text: #ffffff;
220
+ --bg-box: #000000;
221
+ --border-box: #ffffff;
222
+ --color-button-primary: #00ffff;
223
+ }
224
+ ```
225
+
226
+ ---
227
+
228
+ ## Grid System
229
+
230
+ Explorer uses a sophisticated Grid component with three height modes for responsive layouts.
231
+
232
+ ### Height Modes
233
+
234
+ **1. Equal Heights** - All boxes in same row share the tallest content height
235
+ ```tsx
236
+ <Grid columns={3} heightMode="equal">
237
+ <CodeBox code={event} />
238
+ <CodeBox code={mapping} />
239
+ <CodeBox code={output} />
240
+ </Grid>
241
+ ```
242
+
243
+ **2. Auto Heights** - Each box sized independently to content
244
+ ```tsx
245
+ <Grid columns={3} heightMode="auto">
246
+ <CodeBox code={shortEvent} />
247
+ <CodeBox code={longMapping} />
248
+ <CodeBox code={mediumOutput} />
249
+ </Grid>
250
+ ```
251
+
252
+ **3. Synced Heights** - Boxes in same row share height, different rows can differ
253
+ ```tsx
254
+ <Grid columns={3} heightMode="synced">
255
+ <CodeBox code={event} />
256
+ <CodeBox code={mapping} />
257
+ <CodeBox code={output} />
258
+ {/* Next row can have different height */}
259
+ <CodeBox code={shortSnippet} />
260
+ </Grid>
261
+ ```
262
+
263
+ ### Implementation Details
264
+
265
+ **Why Grid Heights Are Complex:**
266
+
267
+ The Grid height synchronization required sophisticated coordination because:
268
+
269
+ 1. **Monaco reports content-only height** - Excludes header (40px) and border (2px)
270
+ 2. **Box needs total height** - Must add header + border for consistent row sizing
271
+ 3. **Height changes cascade** - Content → Monaco → Box → Grid → Row
272
+ 4. **Race conditions during mount** - Components mount asynchronously
273
+ 5. **Automatic layout detection** - Must detect container resize events
274
+
275
+ **Key Files:**
276
+ - [useMonacoHeight.ts](./src/hooks/useMonacoHeight.ts) - Monaco content measurement
277
+ - [GridHeightContext.tsx](./src/contexts/GridHeightContext.tsx) - Cross-component coordination
278
+ - [box.tsx](./src/components/molecules/box.tsx) - Total height calculation
279
+ - [grid.tsx](./src/components/organisms/grid.tsx) - Row height orchestration
280
+
281
+ **Common Pitfalls:**
282
+
283
+ 1. **Forgetting Box overhead** - Always add header (40px) + border (2px) to Monaco height
284
+ 2. **Not handling async layout** - Monaco's layout() is async, use callbacks
285
+ 3. **ResizeObserver loops** - Debounce layout calls with requestAnimationFrame
286
+ 4. **Theme-specific heights** - Test both light and dark themes for consistency
287
+
288
+ **Usage Guidelines:**
289
+
290
+ ```tsx
291
+ // Grid context - Don't use autoHeight (maintains equal row heights)
292
+ <Grid columns={3} heightMode="synced">
293
+ <CodeBox code={event} label="Event" />
294
+ <CodeBox code={mapping} label="Mapping" />
295
+ </Grid>
296
+
297
+ // Standalone context - Use autoHeight to fit content
298
+ <CodeBox
299
+ code={setupExample}
300
+ label="Setup"
301
+ autoHeight={{ min: 100, max: 600 }}
302
+ disabled
303
+ />
304
+
305
+ // Explicit height override
306
+ <CodeBox code={longCode} height="600px" />
307
+ ```
308
+
309
+ **Height Calculation:**
310
+
311
+ ```typescript
312
+ // Monaco provides content-only height
313
+ const contentHeight = editor.getContentHeight(); // e.g., 347px
314
+
315
+ // Box calculates total height
316
+ const totalHeight = contentHeight + 40 + 2; // 389px (includes header + border)
317
+
318
+ // Grid syncs heights across row
319
+ const rowHeight = Math.max(...boxHeightsInRow); // Use tallest box
320
+ ```
321
+
322
+ ---
323
+
324
+ ## Monaco Editor
325
+
326
+ ### Theme Integration
327
+
328
+ Explorer includes two Monaco themes that automatically sync with `data-theme` attribute:
329
+
330
+ - **`elbTheme-dark`** - Dark theme based on Prism Palenight
331
+ - **`elbTheme-light`** - Light theme based on GitHub syntax highlighting
332
+
333
+ **Automatic Theme Switching:**
334
+
335
+ ```typescript
336
+ // Theme detection in code.tsx
337
+ const checkTheme = () => {
338
+ const dataTheme = getDataTheme();
339
+ const isDark = dataTheme === 'dark' ||
340
+ (dataTheme === null && window.matchMedia('(prefers-color-scheme: dark)').matches);
341
+ const newTheme = isDark ? 'elbTheme-dark' : 'elbTheme-light';
342
+ setMonacoTheme(newTheme);
343
+ };
344
+ ```
345
+
346
+ ### Token Color Palette (Dark Theme)
347
+
348
+ Current color scheme matches Prism Palenight:
349
+
350
+ | Token Type | Color | Usage |
351
+ |------------|-------|-------|
352
+ | Comments | `#697098` | Gray, italic |
353
+ | Strings | `#c3e88d` | Green |
354
+ | Numbers | `#f78c6c` | Orange |
355
+ | Functions | `#82aaff` | Blue |
356
+ | Delimiters | `#c792ea` | Purple (braces, brackets) |
357
+ | Operators | `#89ddff` | Cyan |
358
+ | Keywords | `#c084fc` | Bright purple, italic |
359
+ | Types/Classes | `#ffcb6b` | Yellow/gold |
360
+ | Variables | `#bfc7d5` | Light gray |
361
+ | Booleans | `#ff5874` | Red |
362
+ | Tags (HTML) | `#bfc7d5` | Light gray |
363
+ | Attributes (HTML) | `#bfc7d5` | Light gray |
364
+
365
+ ### Language-Specific Token Rules
366
+
367
+ **Critical**: Monaco uses specific token names per language. Always add language-specific rules for proper highlighting:
368
+
369
+ ```typescript
370
+ // Generic rule (may not work)
371
+ { token: 'string', foreground: 'c3e88d' },
372
+
373
+ // Language-specific rules (work reliably)
374
+ { token: 'string.html', foreground: 'c3e88d' },
375
+ { token: 'string.json', foreground: 'c3e88d' },
376
+ { token: 'string.js', foreground: 'c3e88d' },
377
+ { token: 'string.ts', foreground: 'c3e88d' },
378
+ { token: 'string.value.json', foreground: 'c3e88d' },
379
+ ```
380
+
381
+ **Common Language-Specific Tokens:**
382
+
383
+ ```typescript
384
+ // HTML
385
+ 'entity.name.tag.html' // <div>
386
+ 'attribute.name.html' // class=""
387
+ 'attribute.value.html' // ="value"
388
+ 'delimiter.html' // < > / =
389
+ 'comment.html' // <!-- -->
390
+
391
+ // JSON
392
+ 'string.key.json' // "key":
393
+ 'string.value.json' // : "value"
394
+ 'support.type.property-name.json' // Object keys
395
+
396
+ // JavaScript/TypeScript
397
+ 'variable.parameter.ts' // Function parameters
398
+ 'support.type.primitive.ts' // string, number, etc.
399
+ 'entity.name.type.ts' // Type names
400
+ 'keyword.operator.type.ts' // : => |
401
+ ```
402
+
403
+ ### Local Loading (Not CDN)
404
+
405
+ **Problem:** Monaco's default behavior loads from CDN, causing CORS issues.
406
+
407
+ **Solution:** Static synchronous imports in [code.tsx](./src/components/atoms/code.tsx):
408
+
409
+ ```typescript
410
+ // Static imports for Monaco and workers
411
+ import * as monaco from 'monaco-editor';
412
+ import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
413
+ import JsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
414
+ // ... other workers
415
+
416
+ // Configure BEFORE any Editor component mounts
417
+ if (typeof window !== 'undefined') {
418
+ self.MonacoEnvironment = {
419
+ getWorker(_: unknown, label: string) {
420
+ if (label === 'json') return new JsonWorker();
421
+ // ... other worker mappings
422
+ return new EditorWorker();
423
+ },
424
+ };
425
+
426
+ loader.config({ monaco }); // Prevents CDN fallback
427
+ }
428
+ ```
429
+
430
+ **Why It Works:**
431
+ - Static imports execute synchronously at module load time
432
+ - `loader.config()` runs BEFORE any `<Editor>` component mounts
433
+ - No network requests to CDN
434
+
435
+ ### Monaco UI Colors
436
+
437
+ ```typescript
438
+ colors: {
439
+ // Transparent backgrounds let CSS variables control color
440
+ 'editor.background': '#00000000',
441
+ 'editorGutter.background': '#00000000',
442
+ 'editor.lineHighlightBackground': '#00000000',
443
+
444
+ // CRITICAL: Sticky scroll MUST have solid background
445
+ 'editorStickyScroll.background': '#1e1e2e',
446
+
447
+ // Cursor/selection transparent for read-only snippets
448
+ 'editorCursor.foreground': '#00000000',
449
+ 'editor.selectionBackground': '#00000000',
450
+ }
451
+ ```
452
+
453
+ ### Custom Monaco Themes
454
+
455
+ To create a custom Monaco theme:
456
+
457
+ ```typescript
458
+ import { lighthouseTheme } from '@walkeros/explorer';
459
+ import type { editor } from 'monaco-editor';
460
+
461
+ const customTheme: editor.IStandaloneThemeData = {
462
+ ...lighthouseTheme,
463
+ rules: [
464
+ ...lighthouseTheme.rules,
465
+ { token: 'string', foreground: '00ff00' }, // Green strings
466
+ ],
467
+ };
468
+
469
+ // Register in Code component's beforeMount
470
+ <Code
471
+ code={code}
472
+ beforeMount={(monaco) => {
473
+ monaco.editor.defineTheme('my-custom-theme', customTheme);
474
+ monaco.editor.setTheme('my-custom-theme');
475
+ }}
476
+ />
477
+ ```
478
+
479
+ ### Debugging Token Colors
480
+
481
+ **Step 1: Find Token Scope**
482
+
483
+ Open Monaco Editor with F1 → "Developer: Inspect Tokens"
484
+
485
+ ```
486
+ Token: "const"
487
+ Scopes:
488
+ - keyword.const.ts
489
+ - source.ts
490
+ ```
491
+
492
+ **Step 2: Add Specific Rule**
493
+
494
+ ```typescript
495
+ // Before (not working)
496
+ { token: 'keyword', foreground: 'c084fc' }
497
+
498
+ // After (works)
499
+ { token: 'keyword.const.ts', foreground: 'c084fc' }
500
+ ```
501
+
502
+ **Step 3: Verify in Browser DevTools**
503
+
504
+ Inspect rendered Monaco token span:
505
+ ```html
506
+ <span class="mtk5">const</span>
507
+ ```
508
+
509
+ Check computed styles for `.mtk5` - should have your foreground color.
510
+
511
+ ### TypeScript IntelliSense
512
+
513
+ Monaco Editor provides IntelliSense for walkerOS packages through a virtual file system.
514
+
515
+ **How It Works:**
516
+
517
+ ```
518
+ User types code → Monaco TypeScript Service → Virtual File System
519
+ import type { WalkerOS } from '@walkeros/core';
520
+
521
+ file:///node_modules/@walkeros/core/index.d.ts
522
+
523
+ IntelliSense: Autocomplete, type checking, hover docs
524
+ ```
525
+
526
+ **Bundled Types** (included at build time):
527
+ - `@walkeros/core`
528
+ - `@walkeros/collector`
529
+ - `@walkeros/web-source-browser`
530
+
531
+ **Setup** (automatic):
532
+
533
+ The [monaco-types.ts](./src/utils/monaco-types.ts) utility:
534
+ 1. Registers walkerOS type definitions with Monaco
535
+ 2. Creates virtual files in Monaco's file system
536
+ 3. Provides autocomplete and type checking
537
+
538
+ **Usage:**
539
+
540
+ ```typescript
541
+ import { registerWalkerOSTypes } from '../../utils/monaco-types';
542
+
543
+ const handleBeforeMount = (monaco: typeof import('monaco-editor')) => {
544
+ registerWalkerOSTypes(monaco); // Enables IntelliSense
545
+ registerAllThemes(monaco);
546
+ monaco.editor.setTheme('elbTheme-dark');
547
+ };
548
+ ```
549
+
550
+ ---
551
+
552
+ ## SCSS Architecture
553
+
554
+ ### Directory Structure
555
+
556
+ ```
557
+ src/styles/
558
+ ├── index.scss # Main entry (import all components here)
559
+ ├── theme/
560
+ │ ├── _tokens.scss # SCSS tokens ($spacing-md: 12px)
561
+ │ ├── _variables.scss # CSS variables (--bg-input, --color-text)
562
+ │ └── _dark.scss # Dark theme overrides
563
+ ├── foundation/
564
+ │ ├── _reset.scss
565
+ │ ├── _typography.scss
566
+ │ ├── _layout.scss # Grid/flex mixins
567
+ │ ├── _spacing.scss
568
+ │ └── _responsive.scss # Breakpoint mixins
569
+ └── components/
570
+ ├── atoms/ # _button.scss, _toggle.scss, etc.
571
+ ├── molecules/ # _code-panel.scss, _tree-sidebar.scss
572
+ └── organisms/ # _box.scss, _grid.scss, _live-code.scss
573
+ ```
574
+
575
+ ### SCSS Compliance Rules (MANDATORY)
576
+
577
+ **✅ DO:**
578
+
579
+ 1. Use ONLY defined CSS variables from `theme/_variables.scss`
580
+ 2. Follow BEM naming: `.elb-{component}-{element}--{modifier}`
581
+ 3. Use `calc(var(--font-size-base) - 1px)` for font size variations
582
+ 4. Create one SCSS file per component in correct directory
583
+ 5. Import new files alphabetically in `index.scss`
584
+ 6. Use standard gap: `12px` for vertical spacing in flex/grid layouts
585
+ 7. Test in both light and dark themes
586
+
587
+ **❌ DON'T:**
588
+
589
+ 1. Use undefined CSS variables (e.g., `--bg-secondary`, `--font-size-sm`)
590
+ 2. Use `--font-family-mono` (correct: `--font-mono`)
591
+ 3. Hardcode colors, spacing, or font sizes
592
+ 4. Use inline `style` attributes
593
+ 5. Skip wrapper pattern for widgets: `elb-rjsf-widget` → `elb-{name}-widget-wrapper`
594
+
595
+ ### Example Component SCSS
596
+
597
+ ```scss
598
+ // _my-component.scss
599
+ .elb-my-component {
600
+ // Layout
601
+ display: flex;
602
+ flex-direction: column;
603
+ gap: 12px; // Standard gap
604
+
605
+ // Box model (outside to inside)
606
+ margin: var(--spacing-md);
607
+ border: 1px solid var(--border-box);
608
+ padding: var(--spacing-md);
609
+
610
+ // Typography
611
+ font-family: var(--font-family-base);
612
+ font-size: var(--font-size-base);
613
+
614
+ // Visual
615
+ background-color: var(--bg-box);
616
+ color: var(--color-text);
617
+ border-radius: var(--radius-box);
618
+
619
+ // Modifier
620
+ &--primary {
621
+ background-color: var(--color-button-primary);
622
+ color: white;
623
+ }
624
+
625
+ // Element
626
+ &__header {
627
+ font-size: calc(var(--font-size-base) + 2px);
628
+ font-weight: 600;
629
+ }
630
+
631
+ // State
632
+ &.-active {
633
+ background-color: var(--color-button-primary);
634
+ }
635
+ }
636
+ ```
637
+
638
+ ### Component Checklist
639
+
640
+ Before submitting any component:
641
+
642
+ - [ ] Placed in correct atomic layer (atoms/molecules/organisms)
643
+ - [ ] TypeScript types exported from component file
644
+ - [ ] SCSS file created with BEM naming (`elb-{component}-*`)
645
+ - [ ] SCSS imported in `index.scss` (alphabetical order)
646
+ - [ ] All CSS variables exist in `theme/_variables.scss`
647
+ - [ ] No hardcoded values (colors, spacing, fonts)
648
+ - [ ] Uses `calc(var(--font-size-base) - Npx)` for size variations
649
+ - [ ] No inline `style` attributes
650
+ - [ ] Light and dark theme tested
651
+ - [ ] Build succeeds: `npm run build`
652
+
653
+ ---
654
+
655
+ ## Design Rules
656
+
657
+ ### When to Add CSS Variables
658
+
659
+ Add a CSS variable when:
660
+
661
+ 1. **Color appears in 2+ places** - Ensures consistency
662
+ 2. **Value should be theme-aware** - Different light/dark values
663
+ 3. **Users might customize** - Exposed as customization API
664
+ 4. **Component-specific but reused** - Like `--pane-header-height`
665
+
666
+ Don't add CSS variables for:
667
+
668
+ 1. **One-off values** - Use literal values in component SCSS
669
+ 2. **Calculated values** - Use SCSS math instead
670
+ 3. **Values that never change** - Like specific font names
671
+
672
+ ### Color Selection Guidelines
673
+
674
+ **Primary Colors:**
675
+ - Use for interactive elements (buttons, links, focus states)
676
+ - Should have 4.5:1 contrast ratio with background
677
+ - Provide variants (hover, active, disabled)
678
+
679
+ **Text Colors:**
680
+ - Primary text: 7:1 contrast minimum
681
+ - Secondary text: 4.5:1 contrast minimum
682
+ - Always test with [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/)
683
+
684
+ **Syntax Highlighting:**
685
+ - Follow established palette (Prism Palenight for dark, GitHub for light)
686
+ - Ensure readability (4.5:1 minimum for code)
687
+ - Use semantic colors (green for strings, red for errors)
688
+
689
+ **Accessibility:**
690
+ - All interactive elements: 3:1 contrast with background minimum
691
+ - Focus indicators: 3:1 contrast with adjacent colors
692
+ - Test with multiple color vision deficiencies
693
+
694
+ ---
695
+
696
+ ## Common Tasks
697
+
698
+ ### Add a New Component
699
+
700
+ **1. Create Component SCSS** (`src/styles/components/_your-component.scss`):
701
+
702
+ ```scss
703
+ @use '../theme/variables';
704
+
705
+ .elb-your-component {
706
+ background-color: var(--bg-box);
707
+ color: var(--color-text);
708
+ border: 1px solid var(--border-box);
709
+ border-radius: var(--radius-box);
710
+ padding: var(--spacing-md);
711
+
712
+ &__header {
713
+ font-size: calc(var(--font-size-base) + 2px);
714
+ font-weight: 600;
715
+ color: var(--color-text);
716
+ border-bottom: 1px solid var(--border-box);
717
+ padding-bottom: var(--spacing-sm);
718
+ margin-bottom: var(--spacing-md);
719
+ }
720
+
721
+ &--primary {
722
+ background-color: var(--color-button-primary);
723
+ color: white;
724
+ }
725
+ }
726
+ ```
727
+
728
+ **2. Import in Main SCSS** (`src/styles/index.scss`):
729
+
730
+ ```scss
731
+ @use 'components/your-component';
732
+ ```
733
+
734
+ **3. Create Component** (`src/components/molecules/your-component.tsx`):
735
+
736
+ ```typescript
737
+ import React from 'react';
738
+
739
+ interface YourComponentProps {
740
+ title: string;
741
+ variant?: 'default' | 'primary';
742
+ children: React.ReactNode;
743
+ }
744
+
745
+ export function YourComponent({
746
+ title,
747
+ variant = 'default',
748
+ children
749
+ }: YourComponentProps) {
750
+ const className = `elb-your-component ${variant === 'primary' ? 'elb-your-component--primary' : ''}`.trim();
751
+
752
+ return (
753
+ <div className={className}>
754
+ <div className="elb-your-component__header">
755
+ {title}
756
+ </div>
757
+ {children}
758
+ </div>
759
+ );
760
+ }
761
+ ```
762
+
763
+ ### Customize Theme Colors
764
+
765
+ **Option 1: Override CSS Variables** (Recommended)
766
+
767
+ ```css
768
+ /* custom-theme.css */
769
+ [data-theme="dark"] {
770
+ --color-button-primary: #your-brand-color;
771
+ --bg-app: #your-dark-bg;
772
+ --bg-box: #your-dark-box-bg;
773
+ --color-text: #your-dark-text;
774
+ }
775
+
776
+ [data-theme="light"] {
777
+ --color-button-primary: #your-brand-color;
778
+ --bg-app: #your-light-bg;
779
+ --bg-box: #your-light-box-bg;
780
+ --color-text: #your-light-text;
781
+ }
782
+ ```
783
+
784
+ Import after Explorer styles:
785
+
786
+ ```typescript
787
+ import '@walkeros/explorer/dist/index.css';
788
+ import './custom-theme.css'; // Overrides Explorer defaults
789
+ ```
790
+
791
+ **Option 2: Fork and Modify** (Advanced)
792
+
793
+ 1. Clone Explorer package
794
+ 2. Modify `src/styles/_variables.scss`
795
+ 3. Rebuild with `npm run build`
796
+ 4. Use local build instead of npm package
797
+
798
+ ---
799
+
800
+ ## Troubleshooting
801
+
802
+ ### Monaco Editor Issues
803
+
804
+ **Problem:** Monaco shows black/white background instead of theme colors
805
+
806
+ **Cause:** Monaco theme not registered before Editor mounts
807
+
808
+ **Solution:** Ensure `handleBeforeMount` registers themes:
809
+ ```typescript
810
+ const handleBeforeMount = (monaco: typeof import('monaco-editor')) => {
811
+ registerAllThemes(monaco);
812
+ monaco.editor.setTheme('elbTheme-dark');
813
+ };
814
+ ```
815
+
816
+ ---
817
+
818
+ **Problem:** Syntax highlighting wrong colors for specific language
819
+
820
+ **Cause:** Missing language-specific token rules
821
+
822
+ **Solution:** Add language variant rules:
823
+ ```typescript
824
+ { token: 'string', foreground: 'c3e88d' }, // Generic
825
+ { token: 'string.html', foreground: 'c3e88d' }, // HTML-specific
826
+ { token: 'string.json', foreground: 'c3e88d' }, // JSON-specific
827
+ ```
828
+
829
+ ---
830
+
831
+ **Problem:** Monaco loads from CDN despite local imports
832
+
833
+ **Cause:** `loader.config()` called after Editor mounts
834
+
835
+ **Solution:** Use static imports at module level:
836
+ ```typescript
837
+ // Top of file - runs synchronously
838
+ import * as monaco from 'monaco-editor';
839
+ if (typeof window !== 'undefined') {
840
+ loader.config({ monaco });
841
+ }
842
+ ```
843
+
844
+ ---
845
+
846
+ **Problem:** Height not updating when content changes
847
+
848
+ **Cause:** Monaco's `automaticLayout: true` missed resize event
849
+
850
+ **Solution:** Add ResizeObserver to force layout:
851
+ ```typescript
852
+ const resizeObserver = new ResizeObserver(() => {
853
+ requestAnimationFrame(() => editor.layout());
854
+ });
855
+ resizeObserver.observe(container);
856
+ ```
857
+
858
+ ---
859
+
860
+ ### Grid Layout Issues
861
+
862
+ **Problem:** Boxes different heights in same row (synced mode)
863
+
864
+ **Cause:** Box height calculation missing header/border
865
+
866
+ **Solution:** Verify Box adds header (40px) + border (2px):
867
+ ```typescript
868
+ const boxHeight = monacoHeight + 40 + 2;
869
+ ```
870
+
871
+ ---
872
+
873
+ **Problem:** Grid rows collapsing or overflowing
874
+
875
+ **Cause:** Flex container constraints not set
876
+
877
+ **Solution:** Apply flex constraints to Grid:
878
+ ```scss
879
+ .elb-grid {
880
+ display: flex;
881
+ flex-direction: column;
882
+ min-height: 0; // Critical for flex overflow containment
883
+
884
+ &__row {
885
+ display: flex;
886
+ flex: 1;
887
+ min-height: 0; // Also critical
888
+ }
889
+ }
890
+ ```
891
+
892
+ ---
893
+
894
+ **Problem:** Heights "bouncing" during resize
895
+
896
+ **Cause:** Race condition between Monaco layout and Grid calculation
897
+
898
+ **Solution:** Use `requestAnimationFrame` to batch updates:
899
+ ```typescript
900
+ requestAnimationFrame(() => {
901
+ editor.layout();
902
+ updateHeight(editor.getContentHeight());
903
+ });
904
+ ```
905
+
906
+ ---
907
+
908
+ ### Theme Switching Issues
909
+
910
+ **Problem:** Theme changes but Monaco stays same color
911
+
912
+ **Cause:** Monaco theme name doesn't match data-theme value
913
+
914
+ **Solution:** Map data-theme to Monaco theme name:
915
+ ```typescript
916
+ const themeName = dataTheme === 'dark' ? 'elbTheme-dark' : 'elbTheme-light';
917
+ setMonacoTheme(themeName);
918
+ ```
919
+
920
+ ---
921
+
922
+ **Problem:** CSS variables update but colors don't change
923
+
924
+ **Cause:** Components caching old CSS variable values
925
+
926
+ **Solution:** CSS variables update immediately - check for hard-coded colors:
927
+ ```scss
928
+ // Wrong
929
+ .component {
930
+ color: #bfc7d5;
931
+ }
932
+
933
+ // Correct
934
+ .component {
935
+ color: var(--color-text);
936
+ }
937
+ ```
938
+
939
+ ---
940
+
941
+ ## Change Log
942
+
943
+ ### November 2025
944
+
945
+ **HTML Token Standardization**
946
+ - Changed HTML tag colors from red (#ff5572) to light gray (#bfc7d5)
947
+ - Changed HTML attribute colors from green (#c3e88d) to light gray (#bfc7d5)
948
+ - Kept attribute string values green for consistency
949
+ - Added comprehensive HTML-specific token rules
950
+
951
+ **Debug Logging Cleanup**
952
+ - Removed all console.log statements from production code
953
+ - Cleaned up code.tsx, monaco-setup.ts, theme files
954
+
955
+ **Documentation Consolidation**
956
+ - Created unified STYLE.md as single source of truth
957
+ - Archived historical docs (STYLE.md, THEME.md, etc.)
958
+ - Eliminated ~30% duplicate content
959
+
960
+ ### October 2025
961
+
962
+ **Monaco Theme Rename**
963
+ - Renamed `palenight` theme to `elbTheme-dark`
964
+ - Renamed `lighthouse` theme to `elbTheme-light`
965
+ - Updated all references
966
+
967
+ **Local Monaco Loading**
968
+ - Migrated from CDN to local npm package loading
969
+ - Added static imports for Monaco and language workers
970
+ - Configured MonacoEnvironment for Vite workers
971
+
972
+ **Language-Specific Token Rules**
973
+ - Added HTML-specific tokens
974
+ - Added JSON-specific tokens
975
+ - Added JavaScript/TypeScript-specific tokens
976
+ - Improved token matching reliability
977
+
978
+ ### September 2025
979
+
980
+ **Website Color Alignment**
981
+ - Aligned Explorer colors with walkerOS website
982
+ - Updated Prism Palenight colors for dark theme
983
+ - Updated GitHub colors for light theme
984
+
985
+ **Contrast Improvements**
986
+ - Fixed low-contrast text issues (7:1 for primary text)
987
+ - Fixed button contrast issues (4.5:1 minimum)
988
+ - Fixed border contrast issues (3:1 minimum)
989
+ - WCAG AA compliance achieved
990
+
991
+ ---
992
+
993
+ **Last Updated:** 2025-11-06