@astuteo/breakout-grid 5.1.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.
@@ -0,0 +1,659 @@
1
+ (function() {
2
+ "use strict";
3
+ const VERSION = `v${"5.1.0"} lite`;
4
+ const LOREM_CONTENT = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium.
5
+
6
+ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet.`;
7
+ const GRID_AREAS = [
8
+ { name: "full", label: "Full", className: ".col-full", color: "rgba(239, 68, 68, 0.25)", borderColor: "rgb(239, 68, 68)" },
9
+ { name: "full-limit", label: "Full Limit", className: ".col-full-limit", color: "rgba(220, 38, 38, 0.25)", borderColor: "rgb(220, 38, 38)" },
10
+ { name: "feature", label: "Feature", className: ".col-feature", color: "rgba(6, 182, 212, 0.25)", borderColor: "rgb(6, 182, 212)" },
11
+ { name: "popout", label: "Popout", className: ".col-popout", color: "rgba(34, 197, 94, 0.25)", borderColor: "rgb(34, 197, 94)" },
12
+ { name: "content", label: "Content", className: ".col-content", color: "rgba(168, 85, 247, 0.25)", borderColor: "rgb(168, 85, 247)" }
13
+ ];
14
+ function createInitialStateLite() {
15
+ return {
16
+ // UI State
17
+ isVisible: false,
18
+ showLabels: true,
19
+ showClassNames: true,
20
+ showMeasurements: true,
21
+ showPixelWidths: false,
22
+ showGapPadding: false,
23
+ showBreakoutPadding: false,
24
+ showAdvanced: false,
25
+ showLoremIpsum: false,
26
+ viewportWidth: window.innerWidth,
27
+ selectedArea: null,
28
+ hoveredArea: null,
29
+ // Column widths in pixels
30
+ columnWidths: {
31
+ full: 0,
32
+ "full-limit": 0,
33
+ feature: 0,
34
+ popout: 0,
35
+ content: 0,
36
+ center: 0
37
+ },
38
+ // Current breakpoint for gap scale (mobile, lg, xl)
39
+ currentBreakpoint: "mobile",
40
+ // Spacing panel state
41
+ spacingPanelCollapsed: false,
42
+ spacingPanelPos: { x: 16, y: 16 },
43
+ isDraggingSpacing: false,
44
+ dragOffsetSpacing: { x: 0, y: 0 },
45
+ // Control panel state
46
+ controlPanelCollapsed: false
47
+ };
48
+ }
49
+ const methodsLite = {
50
+ // Initialize
51
+ init() {
52
+ const saved = localStorage.getItem("breakoutGridVisualizerVisible");
53
+ if (saved !== null) {
54
+ this.isVisible = saved === "true";
55
+ }
56
+ const spacingPos = localStorage.getItem("breakoutGridSpacingPos");
57
+ if (spacingPos) {
58
+ try {
59
+ this.spacingPanelPos = JSON.parse(spacingPos);
60
+ } catch (e) {
61
+ }
62
+ }
63
+ const spacingCollapsed = localStorage.getItem("breakoutGridSpacingCollapsed");
64
+ if (spacingCollapsed !== null) {
65
+ this.spacingPanelCollapsed = spacingCollapsed === "true";
66
+ }
67
+ window.addEventListener("keydown", (e) => {
68
+ if ((e.ctrlKey || e.metaKey) && e.key === "g") {
69
+ e.preventDefault();
70
+ this.toggle();
71
+ }
72
+ });
73
+ window.addEventListener("resize", () => {
74
+ this.viewportWidth = window.innerWidth;
75
+ this.updateColumnWidths();
76
+ this.updateCurrentBreakpoint();
77
+ });
78
+ this.updateCurrentBreakpoint();
79
+ console.log("Breakout Grid Visualizer (Lite) loaded. Press Ctrl/Cmd + G to toggle.");
80
+ },
81
+ // Toggle visibility
82
+ toggle() {
83
+ this.isVisible = !this.isVisible;
84
+ localStorage.setItem("breakoutGridVisualizerVisible", this.isVisible);
85
+ },
86
+ // Update column widths by querying DOM elements
87
+ updateColumnWidths() {
88
+ this.$nextTick(() => {
89
+ this.gridAreas.forEach((area) => {
90
+ const el = document.querySelector(`.breakout-visualizer-grid .col-${area.name}`);
91
+ if (el) {
92
+ this.columnWidths[area.name] = Math.round(el.getBoundingClientRect().width);
93
+ }
94
+ });
95
+ });
96
+ },
97
+ // Detect current breakpoint based on viewport width
98
+ updateCurrentBreakpoint() {
99
+ const width = window.innerWidth;
100
+ if (width >= 1280) {
101
+ this.currentBreakpoint = "xl";
102
+ } else if (width >= 1024) {
103
+ this.currentBreakpoint = "lg";
104
+ } else {
105
+ this.currentBreakpoint = "mobile";
106
+ }
107
+ },
108
+ // Select a grid area
109
+ selectArea(areaName) {
110
+ this.selectedArea = this.selectedArea === areaName ? null : areaName;
111
+ },
112
+ // Check if area is selected
113
+ isSelected(areaName) {
114
+ return this.selectedArea === areaName;
115
+ },
116
+ // Spacing panel drag handling
117
+ startDragSpacing(e) {
118
+ this.isDraggingSpacing = true;
119
+ this.dragOffsetSpacing = {
120
+ x: e.clientX - this.spacingPanelPos.x,
121
+ y: e.clientY - this.spacingPanelPos.y
122
+ };
123
+ },
124
+ onDragSpacing(e) {
125
+ if (this.isDraggingSpacing) {
126
+ this.spacingPanelPos = {
127
+ x: e.clientX - this.dragOffsetSpacing.x,
128
+ y: e.clientY - this.dragOffsetSpacing.y
129
+ };
130
+ }
131
+ },
132
+ stopDragSpacing() {
133
+ if (this.isDraggingSpacing) {
134
+ localStorage.setItem("breakoutGridSpacingPos", JSON.stringify(this.spacingPanelPos));
135
+ }
136
+ this.isDraggingSpacing = false;
137
+ }
138
+ };
139
+ const templateLite = `
140
+ <div x-show="isVisible"
141
+ x-transition
142
+ class="breakout-grid-visualizer"
143
+ style="position: fixed; inset: 0; pointer-events: none; z-index: 9999;">
144
+
145
+ <!-- Advanced Span Examples Overlay -->
146
+ <div x-show="showAdvanced"
147
+ class="grid-cols-breakout"
148
+ style="position: absolute; inset: 0; height: 100%; pointer-events: auto; z-index: 5;">
149
+
150
+ <!-- Left-anchored: full-start to feature-end -->
151
+ <div x-data="{ hovered: false }"
152
+ @mouseenter="hovered = true"
153
+ @mouseleave="hovered = false"
154
+ :style="{
155
+ gridColumn: 'full-start / feature-end',
156
+ background: hovered ? 'linear-gradient(135deg, rgba(236, 72, 153, 0.6) 0%, rgba(139, 92, 246, 0.6) 100%)' : 'linear-gradient(135deg, rgba(236, 72, 153, 0.25) 0%, rgba(139, 92, 246, 0.25) 100%)',
157
+ border: '3px solid rgb(168, 85, 247)',
158
+ margin: '1rem 0',
159
+ padding: '1rem',
160
+ display: 'flex',
161
+ alignItems: 'center',
162
+ justifyContent: 'flex-start',
163
+ transition: 'background 0.2s ease'
164
+ }">
165
+ <div style="background: rgb(139, 92, 246);
166
+ color: white;
167
+ padding: 0.75rem 1rem;
168
+ font-size: 0.75rem;
169
+ font-weight: 700;
170
+ text-align: left;
171
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);">
172
+ <div style="font-family: monospace; margin-bottom: 0.25rem;">.col-feature-left</div>
173
+ <div style="font-size: 0.625rem; opacity: 0.9; font-weight: 500;">Left edge → feature boundary</div>
174
+ </div>
175
+ </div>
176
+
177
+ <!-- Right-anchored: feature-start to full-end -->
178
+ <div x-data="{ hovered: false }"
179
+ @mouseenter="hovered = true"
180
+ @mouseleave="hovered = false"
181
+ :style="{
182
+ gridColumn: 'feature-start / full-end',
183
+ background: hovered ? 'linear-gradient(135deg, rgba(34, 197, 94, 0.6) 0%, rgba(59, 130, 246, 0.6) 100%)' : 'linear-gradient(135deg, rgba(34, 197, 94, 0.25) 0%, rgba(59, 130, 246, 0.25) 100%)',
184
+ border: '3px solid rgb(34, 197, 94)',
185
+ margin: '1rem 0',
186
+ padding: '1rem',
187
+ display: 'flex',
188
+ alignItems: 'center',
189
+ justifyContent: 'flex-end',
190
+ transition: 'background 0.2s ease'
191
+ }">
192
+ <div style="background: rgb(34, 197, 94);
193
+ color: white;
194
+ padding: 0.75rem 1rem;
195
+ font-size: 0.75rem;
196
+ font-weight: 700;
197
+ text-align: right;
198
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);">
199
+ <div style="font-family: monospace; margin-bottom: 0.25rem;">.col-feature-right</div>
200
+ <div style="font-size: 0.625rem; opacity: 0.9; font-weight: 500;">Feature boundary → right edge</div>
201
+ </div>
202
+ </div>
203
+
204
+ <!-- To center point: full-start to center-end -->
205
+ <div x-data="{ hovered: false }"
206
+ @mouseenter="hovered = true"
207
+ @mouseleave="hovered = false"
208
+ :style="{
209
+ gridColumn: 'full-start / center-end',
210
+ background: hovered ? 'linear-gradient(135deg, rgba(251, 146, 60, 0.6) 0%, rgba(234, 179, 8, 0.6) 100%)' : 'linear-gradient(135deg, rgba(251, 146, 60, 0.25) 0%, rgba(234, 179, 8, 0.25) 100%)',
211
+ border: '3px solid rgb(234, 179, 8)',
212
+ margin: '1rem 0',
213
+ padding: '1rem',
214
+ display: 'flex',
215
+ alignItems: 'center',
216
+ justifyContent: 'flex-end',
217
+ transition: 'background 0.2s ease'
218
+ }">
219
+ <div style="background: rgb(234, 179, 8);
220
+ color: white;
221
+ padding: 0.75rem 1rem;
222
+ font-size: 0.75rem;
223
+ font-weight: 700;
224
+ text-align: right;
225
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);">
226
+ <div style="font-family: monospace; margin-bottom: 0.25rem;">.col-center-left</div>
227
+ <div style="font-size: 0.625rem; opacity: 0.9; font-weight: 500;">Left edge → center point</div>
228
+ </div>
229
+ </div>
230
+
231
+ <!-- Nested grid example: breakout-to-feature inside col-feature -->
232
+ <div x-data="{ hovered: false }"
233
+ @mouseenter="hovered = true"
234
+ @mouseleave="hovered = false"
235
+ :style="{
236
+ gridColumn: 'feature',
237
+ border: '3px dashed rgb(59, 130, 246)',
238
+ margin: '1rem 0',
239
+ background: hovered ? 'rgba(59, 130, 246, 0.3)' : 'rgba(59, 130, 246, 0.05)',
240
+ transition: 'background 0.2s ease',
241
+ padding: '0.5rem'
242
+ }">
243
+ <div style="font-size: 0.625rem; font-family: monospace; color: rgb(30, 64, 175); margin-bottom: 0.5rem; padding: 0.25rem;">
244
+ Parent: .col-feature container
245
+ </div>
246
+ <div class="grid-cols-breakout breakout-to-feature"
247
+ style="background: rgba(59, 130, 246, 0.1);">
248
+ <div style="grid-column: feature;
249
+ background: rgba(59, 130, 246, 0.3);
250
+ padding: 0.5rem;
251
+ font-size: 0.625rem;
252
+ font-family: monospace;
253
+ color: rgb(30, 64, 175);">
254
+ .col-feature → fills container
255
+ </div>
256
+ <div style="grid-column: content;
257
+ background: rgb(59, 130, 246);
258
+ color: white;
259
+ padding: 0.75rem 1rem;
260
+ font-size: 0.75rem;
261
+ font-weight: 700;
262
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);">
263
+ <div style="font-family: monospace; margin-bottom: 0.5rem;">.col-content → has margins</div>
264
+ <div style="font-size: 0.625rem; opacity: 0.9; font-weight: 500; margin-bottom: 0.75rem;">breakout-to-feature collapses outer tracks</div>
265
+ <pre style="font-size: 0.5rem; background: rgba(0,0,0,0.2); padding: 0.5rem; margin: 0; white-space: pre-wrap; text-align: left;">&lt;div class="col-feature"&gt;
266
+ &lt;div class="grid-cols-breakout breakout-to-feature"&gt;
267
+ &lt;div class="col-feature"&gt;Fills container&lt;/div&gt;
268
+ &lt;p class="col-content"&gt;Has margins&lt;/p&gt;
269
+ &lt;/div&gt;
270
+ &lt;/div&gt;</pre>
271
+ </div>
272
+ </div>
273
+ </div>
274
+
275
+ <!-- Subgrid example: child aligns to parent grid tracks -->
276
+ <div x-data="{ hovered: false }"
277
+ @mouseenter="hovered = true"
278
+ @mouseleave="hovered = false"
279
+ :style="{
280
+ gridColumn: 'feature-start / full-end',
281
+ display: 'grid',
282
+ gridTemplateColumns: 'subgrid',
283
+ border: '3px solid rgb(236, 72, 153)',
284
+ margin: '1rem 0',
285
+ background: hovered ? 'rgba(236, 72, 153, 0.15)' : 'rgba(236, 72, 153, 0.05)',
286
+ transition: 'background 0.2s ease'
287
+ }">
288
+ <!-- Parent label -->
289
+ <div style="grid-column: 1 / -1;
290
+ font-size: 0.625rem;
291
+ font-family: monospace;
292
+ color: rgb(157, 23, 77);
293
+ padding: 0.5rem;
294
+ background: rgba(236, 72, 153, 0.1);">
295
+ Parent: .col-feature-right .grid-cols-breakout-subgrid
296
+ </div>
297
+ <!-- Child spanning feature (wider, lighter) -->
298
+ <div style="grid-column: feature;
299
+ background: rgba(236, 72, 153, 0.3);
300
+ padding: 0.5rem;
301
+ margin: 0.5rem 0;
302
+ font-size: 0.625rem;
303
+ font-family: monospace;
304
+ color: rgb(157, 23, 77);">
305
+ Child: .col-feature (aligns to feature area)
306
+ </div>
307
+ <!-- Child using subgrid to align to content (darker) -->
308
+ <div style="grid-column: content;
309
+ background: rgb(236, 72, 153);
310
+ color: white;
311
+ padding: 0.75rem 1rem;
312
+ margin: 0.5rem 0;
313
+ font-size: 0.75rem;
314
+ font-weight: 700;
315
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);">
316
+ <div style="font-family: monospace; margin-bottom: 0.5rem;">Child: .col-content</div>
317
+ <div style="font-size: 0.625rem; opacity: 0.9; font-weight: 500; margin-bottom: 0.75rem;">Subgrid lets children align to parent's named lines</div>
318
+ <pre style="font-size: 0.5rem; background: rgba(0,0,0,0.2); padding: 0.5rem; margin: 0; white-space: pre-wrap; text-align: left;">&lt;div class="col-feature-right grid-cols-breakout-subgrid"&gt;
319
+ &lt;div class="col-feature"&gt;Aligns to feature!&lt;/div&gt;
320
+ &lt;div class="col-content"&gt;Aligns to content!&lt;/div&gt;
321
+ &lt;/div&gt;</pre>
322
+ <div style="margin-top: 0.75rem;">
323
+ <a href="https://caniuse.com/css-subgrid" target="_blank" rel="noopener" style="display: inline-block; background: rgba(255,255,255,0.2); color: white; text-decoration: none; padding: 0.375rem 0.75rem; border-radius: 0.25rem; font-size: 0.625rem; font-weight: 600; border: 1px solid rgba(255,255,255,0.3);">Check browser support</a>
324
+ <span style="font-size: 0.5rem; opacity: 0.7; margin-left: 0.5rem;">(~90% as of Jan 2025)</span>
325
+ </div>
326
+ </div>
327
+ </div>
328
+
329
+ </div>
330
+
331
+ <!-- Spacing Panel (read-only, shows current CSS values) -->
332
+ <div x-show="!showAdvanced"
333
+ x-init="updateCurrentBreakpoint()"
334
+ @mousemove.window="onDragSpacing($event)"
335
+ @mouseup.window="stopDragSpacing()"
336
+ :style="{
337
+ position: 'fixed',
338
+ left: spacingPanelPos.x + 'px',
339
+ top: spacingPanelPos.y + 'px',
340
+ zIndex: 30,
341
+ pointerEvents: 'auto',
342
+ background: '#f7f7f7',
343
+ borderRadius: '8px',
344
+ boxShadow: '0 4px 24px rgba(0, 0, 0, 0.15)',
345
+ width: '200px',
346
+ fontFamily: 'system-ui, -apple-system, sans-serif',
347
+ overflow: 'hidden'
348
+ }">
349
+ <!-- Header -->
350
+ <div @mousedown="startDragSpacing($event)"
351
+ @dblclick="spacingPanelCollapsed = !spacingPanelCollapsed; localStorage.setItem('breakoutGridSpacingCollapsed', spacingPanelCollapsed)"
352
+ style="padding: 8px 12px; background: #1a1a2e; color: white; display: flex; justify-content: space-between; align-items: center; cursor: move; user-select: none;">
353
+ <div style="display: flex; align-items: center; gap: 8px;">
354
+ <span style="font-weight: 600; font-size: 12px;">Spacing</span>
355
+ <span style="font-size: 10px; font-weight: 600; color: white; background: transparent; border: 1.5px solid rgba(255,255,255,0.5); padding: 1px 6px; border-radius: 3px;" x-text="'@' + currentBreakpoint"></span>
356
+ </div>
357
+ <button @click.stop="spacingPanelCollapsed = !spacingPanelCollapsed; localStorage.setItem('breakoutGridSpacingCollapsed', spacingPanelCollapsed)" style="background: transparent; border: none; color: rgba(255,255,255,0.6); cursor: pointer; font-size: 14px; line-height: 1; padding: 0;" x-text="spacingPanelCollapsed ? '+' : '−'"></button>
358
+ </div>
359
+ <!-- Content (read-only display) -->
360
+ <div x-show="!spacingPanelCollapsed" style="padding: 12px;">
361
+ <!-- Gap -->
362
+ <div style="display: flex; flex-direction: column; gap: 8px;">
363
+ <div style="display: flex; align-items: center; gap: 8px;">
364
+ <span style="font-size: 10px; font-weight: 600; color: #6b7280; text-transform: uppercase; letter-spacing: 0.5px;">Gap</span>
365
+ <span style="font-size: 9px; color: #9ca3af;">outer margins</span>
366
+ </div>
367
+ <div style="display: flex; align-items: flex-end; gap: 8px;">
368
+ <div style="width: var(--gap); height: 24px; background: #f97316; min-width: 20px;"></div>
369
+ <div style="width: 24px; height: var(--gap); background: #f97316; min-height: 20px;"></div>
370
+ </div>
371
+ <div style="font-size: 9px; font-family: 'SF Mono', Monaco, monospace; color: #6b7280;">
372
+ var(--gap)
373
+ </div>
374
+ </div>
375
+ <!-- Breakout Padding -->
376
+ <div style="display: flex; flex-direction: column; gap: 8px; padding-top: 12px; margin-top: 12px; border-top: 1px solid #e5e5e5;">
377
+ <div style="display: flex; align-items: center; gap: 8px;">
378
+ <span style="font-size: 10px; font-weight: 600; color: #6b7280; text-transform: uppercase; letter-spacing: 0.5px;">Breakout</span>
379
+ <span style="font-size: 9px; color: #9ca3af;">p-breakout / m-breakout</span>
380
+ </div>
381
+ <div style="display: flex; align-items: flex-end; gap: 8px;">
382
+ <div style="width: var(--breakout-padding); height: 24px; background: #8b5cf6; min-width: 20px;"></div>
383
+ <div style="width: 24px; height: var(--breakout-padding); background: #8b5cf6; min-height: 20px;"></div>
384
+ </div>
385
+ <div style="font-size: 9px; font-family: 'SF Mono', Monaco, monospace; color: #6b7280;">
386
+ var(--breakout-padding)
387
+ </div>
388
+ </div>
389
+ </div>
390
+ </div>
391
+
392
+ <!-- Grid Overlay (hidden in Advanced mode) -->
393
+ <div x-show="!showAdvanced" x-init="$watch('isVisible', v => v && setTimeout(() => updateColumnWidths(), 50)); setTimeout(() => updateColumnWidths(), 100)" class="grid-cols-breakout breakout-visualizer-grid" style="height: 100%; position: relative; z-index: 2;">
394
+ <template x-for="area in gridAreas" :key="area.name">
395
+ <div :class="'col-' + area.name"
396
+ @click="selectArea(area.name)"
397
+ @mouseenter="hoveredArea = area.name"
398
+ @mouseleave="hoveredArea = null"
399
+ :style="{
400
+ backgroundColor: (hoveredArea === area.name || isSelected(area.name)) ? area.color.replace('0.25', '0.6') : area.color,
401
+ borderLeft: '1px solid ' + area.borderColor,
402
+ borderRight: '1px solid ' + area.borderColor,
403
+ position: 'relative',
404
+ height: '100%',
405
+ pointerEvents: 'auto',
406
+ cursor: 'pointer',
407
+ transition: 'background-color 0.2s'
408
+ }">
409
+
410
+ <!-- Label (centered) -->
411
+ <div x-show="showLabels"
412
+ :style="{
413
+ position: 'absolute',
414
+ top: '50%',
415
+ left: '50%',
416
+ transform: 'translate(-50%, -50%)',
417
+ backgroundColor: area.borderColor,
418
+ color: 'white',
419
+ padding: '0.75rem 1rem',
420
+ borderRadius: '0.375rem',
421
+ fontSize: '0.75rem',
422
+ fontWeight: '600',
423
+ textTransform: 'uppercase',
424
+ letterSpacing: '0.05em',
425
+ whiteSpace: 'nowrap',
426
+ boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1)',
427
+ opacity: isSelected(area.name) ? '1' : '0.8',
428
+ textAlign: 'center',
429
+ zIndex: '10'
430
+ }">
431
+ <div x-text="area.label"></div>
432
+ <div x-show="showClassNames"
433
+ :style="{
434
+ fontSize: '0.625rem',
435
+ fontWeight: '500',
436
+ textTransform: 'none',
437
+ marginTop: '0.25rem',
438
+ opacity: '0.9',
439
+ fontFamily: 'monospace'
440
+ }" x-text="area.className"></div>
441
+ <div x-show="showPixelWidths && columnWidths[area.name] > 0"
442
+ :style="{
443
+ fontSize: '0.625rem',
444
+ fontWeight: '600',
445
+ textTransform: 'none',
446
+ marginTop: '0.25rem',
447
+ opacity: '0.75',
448
+ fontFamily: 'monospace',
449
+ backgroundColor: 'rgba(0,0,0,0.2)',
450
+ padding: '0.125rem 0.375rem',
451
+ borderRadius: '0.25rem'
452
+ }" x-text="columnWidths[area.name] + 'px'"></div>
453
+ </div>
454
+
455
+ <!-- Lorem Ipsum Content (behind label) -->
456
+ <div x-show="showLoremIpsum"
457
+ :style="{
458
+ position: 'absolute',
459
+ inset: '0',
460
+ padding: showGapPadding ? 'var(--gap)' : (showBreakoutPadding ? 'var(--breakout-padding)' : '1.5rem 0'),
461
+ boxSizing: 'border-box',
462
+ overflow: 'hidden',
463
+ whiteSpace: 'pre-line',
464
+ fontSize: '1.125rem',
465
+ lineHeight: '1.75',
466
+ color: 'white',
467
+ textShadow: '0 1px 2px rgba(0,0,0,0.3)',
468
+ zIndex: '1'
469
+ }" x-text="loremContent"></div>
470
+
471
+ <!-- p-gap / px-gap Padding Overlay -->
472
+ <div x-show="showGapPadding"
473
+ :style="{
474
+ position: 'absolute',
475
+ inset: 'var(--gap)',
476
+ border: '2px dotted ' + area.borderColor,
477
+ backgroundColor: area.color.replace('0.1', '0.2'),
478
+ pointerEvents: 'none',
479
+ zIndex: '10'
480
+ }">
481
+ <div :style="{
482
+ position: 'absolute',
483
+ top: '0.5rem',
484
+ left: '0.5rem',
485
+ fontSize: '0.625rem',
486
+ fontWeight: '700',
487
+ color: area.borderColor,
488
+ textTransform: 'uppercase',
489
+ letterSpacing: '0.05em',
490
+ backgroundColor: 'white',
491
+ padding: '0.25rem 0.5rem',
492
+ borderRadius: '0.25rem',
493
+ boxShadow: '0 1px 3px rgba(0,0,0,0.1)'
494
+ }">p-gap</div>
495
+ </div>
496
+
497
+ <!-- p-breakout / px-breakout Padding Overlay -->
498
+ <div x-show="showBreakoutPadding"
499
+ :style="{
500
+ position: 'absolute',
501
+ inset: 'var(--breakout-padding)',
502
+ border: '3px dashed ' + area.borderColor,
503
+ backgroundColor: area.color.replace('0.1', '0.25'),
504
+ pointerEvents: 'none',
505
+ zIndex: '10'
506
+ }">
507
+ <div :style="{
508
+ position: 'absolute',
509
+ top: '0.5rem',
510
+ left: '0.5rem',
511
+ fontSize: '0.625rem',
512
+ fontWeight: '700',
513
+ color: area.borderColor,
514
+ textTransform: 'uppercase',
515
+ letterSpacing: '0.05em',
516
+ backgroundColor: 'white',
517
+ padding: '0.25rem 0.5rem',
518
+ borderRadius: '0.25rem',
519
+ boxShadow: '0 1px 3px rgba(0,0,0,0.1)'
520
+ }">p-breakout</div>
521
+ </div>
522
+ </div>
523
+ </template>
524
+ </div>
525
+
526
+
527
+ <!-- Control Panel - Simplified (no Config/Diagram buttons) -->
528
+ <div :style="{
529
+ position: 'fixed',
530
+ bottom: '12px',
531
+ right: '12px',
532
+ pointerEvents: 'auto',
533
+ background: '#f7f7f7',
534
+ borderRadius: '8px',
535
+ boxShadow: '0 4px 24px rgba(0, 0, 0, 0.15)',
536
+ width: '200px',
537
+ fontFamily: 'system-ui, -apple-system, sans-serif',
538
+ zIndex: '10000',
539
+ overflow: 'hidden'
540
+ }">
541
+
542
+ <!-- Header -->
543
+ <div @dblclick="controlPanelCollapsed = !controlPanelCollapsed"
544
+ style="padding: 8px 12px; background: #1a1a2e; color: white; display: flex; justify-content: space-between; align-items: center; cursor: pointer; user-select: none;">
545
+ <div style="display: flex; align-items: center; gap: 8px;">
546
+ <span style="font-weight: 600; font-size: 12px;">Grid</span>
547
+ <span style="font-size: 10px; color: rgba(255,255,255,0.5);" x-text="version"></span>
548
+ <span x-show="controlPanelCollapsed" style="font-size: 10px; color: rgba(255,255,255,0.4);">...</span>
549
+ </div>
550
+ <div style="display: flex; align-items: center; gap: 8px;">
551
+ <span style="font-size: 11px; font-variant-numeric: tabular-nums; color: rgba(255,255,255,0.7);" x-text="viewportWidth + 'px'"></span>
552
+ <button @click.stop="toggle()" style="background: transparent; border: none; color: rgba(255,255,255,0.6); cursor: pointer; font-size: 16px; line-height: 1; padding: 0;">&times;</button>
553
+ </div>
554
+ </div>
555
+
556
+ <!-- Collapsible Content -->
557
+ <div x-show="!controlPanelCollapsed">
558
+ <!-- Display Options -->
559
+ <div style="padding: 8px 12px; background: white; border-bottom: 1px solid #e5e5e5;">
560
+ <div style="font-size: 9px; font-weight: 600; color: #6b7280; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 6px;">Display</div>
561
+ <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 4px 12px;">
562
+ <label style="display: flex; align-items: center; cursor: pointer; font-size: 11px; color: #374151;">
563
+ <input type="checkbox" x-model="showLabels" style="margin-right: 6px; cursor: pointer; accent-color: #1a1a2e;">
564
+ Labels
565
+ </label>
566
+ <label style="display: flex; align-items: center; cursor: pointer; font-size: 11px; color: #374151;">
567
+ <input type="checkbox" x-model="showClassNames" style="margin-right: 6px; cursor: pointer; accent-color: #1a1a2e;">
568
+ Classes
569
+ </label>
570
+ <label style="display: flex; align-items: center; cursor: pointer; font-size: 11px; color: #374151;">
571
+ <input type="checkbox" x-model="showMeasurements" style="margin-right: 6px; cursor: pointer; accent-color: #1a1a2e;">
572
+ Values
573
+ </label>
574
+ <label style="display: flex; align-items: center; cursor: pointer; font-size: 11px; color: #374151;">
575
+ <input type="checkbox" x-model="showLoremIpsum" style="margin-right: 6px; cursor: pointer; accent-color: #1a1a2e;">
576
+ Lorem
577
+ </label>
578
+ <label style="display: flex; align-items: center; cursor: pointer; font-size: 11px; color: #374151;">
579
+ <input type="checkbox" x-model="showPixelWidths" style="margin-right: 6px; cursor: pointer; accent-color: #1a1a2e;">
580
+ Pixels
581
+ </label>
582
+ </div>
583
+ </div>
584
+
585
+ <!-- Padding Options -->
586
+ <div style="padding: 8px 12px; background: white; border-bottom: 1px solid #e5e5e5;">
587
+ <div style="font-size: 9px; font-weight: 600; color: #6b7280; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 6px;">Padding</div>
588
+ <div style="display: flex; gap: 12px;">
589
+ <label style="display: flex; align-items: center; cursor: pointer; font-size: 11px; color: #374151;">
590
+ <input type="checkbox" x-model="showGapPadding" style="margin-right: 6px; cursor: pointer; accent-color: #1a1a2e;">
591
+ p-gap
592
+ </label>
593
+ <label style="display: flex; align-items: center; cursor: pointer; font-size: 11px; color: #374151;">
594
+ <input type="checkbox" x-model="showBreakoutPadding" style="margin-right: 6px; cursor: pointer; accent-color: #1a1a2e;">
595
+ p-breakout
596
+ </label>
597
+ </div>
598
+ </div>
599
+
600
+ <!-- Advanced -->
601
+ <div style="padding: 8px 12px; background: white;">
602
+ <label style="display: flex; align-items: center; cursor: pointer; font-size: 11px; color: #374151;">
603
+ <input type="checkbox" x-model="showAdvanced" style="margin-right: 6px; cursor: pointer; accent-color: #1a1a2e;">
604
+ Advanced Spans
605
+ </label>
606
+ </div>
607
+
608
+ <!-- Footer -->
609
+ <div style="padding: 6px 12px; background: #f7f7f7; border-top: 1px solid #e5e5e5;">
610
+ <div style="font-size: 9px; color: #9ca3af; text-align: center;">
611
+ <kbd style="background: #e5e5e5; padding: 1px 4px; border-radius: 2px; font-size: 9px; font-weight: 600; color: #374151;">⌘G</kbd> toggle
612
+ </div>
613
+ </div>
614
+
615
+ <!-- Selected Area Info -->
616
+ <div x-show="selectedArea" style="padding: 8px 12px; background: #f0f9ff; border-top: 1px solid #e5e5e5;">
617
+ <div style="font-size: 11px; color: #1a1a2e; font-weight: 600; font-family: monospace;" x-text="gridAreas.find(a => a.name === selectedArea)?.className || ''"></div>
618
+ </div>
619
+ </div><!-- End Collapsible Content -->
620
+
621
+ </div>
622
+
623
+ </div>
624
+ `;
625
+ (function() {
626
+ document.addEventListener("alpine:init", () => {
627
+ Alpine.data("breakoutGridVisualizer", () => ({
628
+ // Constants
629
+ version: VERSION,
630
+ loremContent: LOREM_CONTENT,
631
+ // Configuration (read-only)
632
+ gridAreas: GRID_AREAS,
633
+ // State
634
+ ...createInitialStateLite(),
635
+ // Methods
636
+ ...methodsLite,
637
+ // Template
638
+ template: templateLite
639
+ }));
640
+ });
641
+ function injectVisualizer() {
642
+ if (document.getElementById("breakout-grid-visualizer-root")) return;
643
+ const container = document.createElement("div");
644
+ container.id = "breakout-grid-visualizer-root";
645
+ container.setAttribute("x-data", "breakoutGridVisualizer");
646
+ container.setAttribute("x-html", "template");
647
+ document.body.appendChild(container);
648
+ console.log("Breakout Grid Visualizer (Lite) injected. Press Ctrl/Cmd + G to toggle.");
649
+ }
650
+ if (document.readyState === "loading") {
651
+ document.addEventListener("DOMContentLoaded", () => {
652
+ setTimeout(injectVisualizer, 10);
653
+ });
654
+ } else {
655
+ document.addEventListener("alpine:initialized", injectVisualizer);
656
+ setTimeout(injectVisualizer, 100);
657
+ }
658
+ })();
659
+ })();