@astuteo/breakout-grid 5.1.4 → 5.2.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.
@@ -1,6 +1,6 @@
1
1
  (function() {
2
2
  "use strict";
3
- const VERSION = `v${"5.1.4"} lite`;
3
+ const VERSION = `v${"5.2.0"} lite`;
4
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
5
 
6
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.`;
@@ -1,6 +1,6 @@
1
1
  (function() {
2
2
  "use strict";
3
- const VERSION = `v${"5.1.4"}`;
3
+ const VERSION = `v${"5.2.0"}`;
4
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
5
 
6
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.`;
@@ -77,12 +77,42 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
77
77
  restoreInput: "",
78
78
  restoreError: null,
79
79
  sectionCopied: null,
80
+ cssDropdownOpen: false,
81
+ hasConfigOverride: false,
80
82
  showCloseWarningModal: false,
81
83
  gridOpacity: 0.8,
82
84
  backdropOpacity: 0.85
83
85
  };
84
86
  }
85
- const BUILD_VERSION = "5.1.4";
87
+ const BUILD_VERSION = "5.2.0";
88
+ function wrapWithTailwindUtilities(css) {
89
+ return css.replace(
90
+ /^\.(-?[a-zA-Z_][\w-]*)\s*\{/gm,
91
+ "@utility $1 {"
92
+ );
93
+ }
94
+ function generateTailwindCSSExport(c, version = BUILD_VERSION) {
95
+ const css = generateCSSExport(c, version);
96
+ const tailwindCss = wrapWithTailwindUtilities(css);
97
+ return tailwindCss.replace(
98
+ /INTEGRATION \(ITCSS \+ Tailwind v4\)[\s\S]*?QUICK START/,
99
+ `INTEGRATION (Tailwind v4 @utility)
100
+ * ============================================================================
101
+ *
102
+ * This file uses @utility directives — requires Tailwind CSS v4+.
103
+ * All utilities support responsive and state variants:
104
+ *
105
+ * <div class="col-content md:col-feature lg:col-full">...</div>
106
+ *
107
+ * Add this file to your CSS imports:
108
+ *
109
+ * @import 'tailwindcss';
110
+ * @import './_objects.breakout-grid.tw.css';
111
+ *
112
+ * ============================================================================
113
+ * QUICK START`
114
+ );
115
+ }
86
116
  function generateCSSExport(c, version = BUILD_VERSION) {
87
117
  var _a, _b, _c, _d, _e;
88
118
  const VERSION2 = version;
@@ -764,6 +794,7 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
764
794
  if (savedConfig) {
765
795
  try {
766
796
  const config = JSON.parse(savedConfig);
797
+ this.hasConfigOverride = true;
767
798
  this.$nextTick(() => this.applyConfig(config));
768
799
  } catch (e) {
769
800
  }
@@ -977,13 +1008,15 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
977
1008
  setTimeout(() => this.copySuccess = false, 2e3);
978
1009
  });
979
1010
  },
980
- downloadCSS() {
981
- const css = this.generateCSSExport(this.generateConfigExport());
1011
+ downloadCSS(format = "plain") {
1012
+ const config = this.generateConfigExport();
1013
+ const css = format === "tailwind" ? this.generateTailwindCSSExport(config) : this.generateCSSExport(config);
1014
+ const filename = format === "tailwind" ? "_objects.breakout-grid.tw.css" : "_objects.breakout-grid.css";
982
1015
  const blob = new Blob([css], { type: "text/css" });
983
1016
  const url = URL.createObjectURL(blob);
984
1017
  const a = document.createElement("a");
985
1018
  a.href = url;
986
- a.download = `_objects.breakout-grid.css`;
1019
+ a.download = filename;
987
1020
  a.click();
988
1021
  URL.revokeObjectURL(url);
989
1022
  },
@@ -1060,6 +1093,7 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1060
1093
  saveConfigToStorage() {
1061
1094
  const config = this.generateConfigExport();
1062
1095
  localStorage.setItem("breakoutGridConfig", JSON.stringify(config));
1096
+ this.hasConfigOverride = true;
1063
1097
  },
1064
1098
  applyConfig(config) {
1065
1099
  this.editMode = true;
@@ -1112,6 +1146,7 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1112
1146
  this.restoreCSSVariables();
1113
1147
  this.loadCurrentValues();
1114
1148
  this.configCopied = false;
1149
+ this.hasConfigOverride = false;
1115
1150
  },
1116
1151
  updateConfigValue(key, value) {
1117
1152
  this.editValues[key] = value;
@@ -1334,6 +1369,20 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1334
1369
  this.restoreInput = "";
1335
1370
  this.restoreError = null;
1336
1371
  },
1372
+ hasStoredOverrides() {
1373
+ return ["breakoutGridConfig", "breakoutGridEditorOpen", "breakoutGridEditorPos", "breakoutGridSpacingPos", "breakoutGridSpacingCollapsed"].some((key) => localStorage.getItem(key) !== null);
1374
+ },
1375
+ resetAllStorage() {
1376
+ ["breakoutGridVisualizerVisible", "breakoutGridEditorOpen", "breakoutGridConfig", "breakoutGridEditorPos", "breakoutGridSpacingPos", "breakoutGridSpacingCollapsed"].forEach((key) => localStorage.removeItem(key));
1377
+ this.restoreCSSVariables();
1378
+ this.showEditor = false;
1379
+ this.editMode = false;
1380
+ this.editorPos = { x: 20, y: 100 };
1381
+ this.spacingPanelPos = { x: 16, y: 16 };
1382
+ this.spacingPanelCollapsed = false;
1383
+ this.hasConfigOverride = false;
1384
+ this.configCopied = false;
1385
+ },
1337
1386
  restoreConfig() {
1338
1387
  this.restoreError = null;
1339
1388
  try {
@@ -2005,6 +2054,14 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
2005
2054
  </div>
2006
2055
  </div>
2007
2056
 
2057
+ <!-- Config override warning -->
2058
+ <div x-show="hasConfigOverride"
2059
+ style="padding: 6px 10px; background: #fef3c7; border-bottom: 1px solid #fcd34d; display: flex; align-items: center; justify-content: space-between; gap: 6px;">
2060
+ <span style="font-size: 9px; font-weight: 600; color: #92400e;">localStorage override active</span>
2061
+ <button @click="resetAllStorage()"
2062
+ style="background: none; border: 1px solid #f59e0b; border-radius: 3px; font-size: 9px; font-weight: 600; color: #92400e; cursor: pointer; padding: 1px 5px; white-space: nowrap;">Clear</button>
2063
+ </div>
2064
+
2008
2065
  <!-- Collapsible Content -->
2009
2066
  <div x-show="!controlPanelCollapsed">
2010
2067
  <!-- Action Buttons -->
@@ -2106,10 +2163,16 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
2106
2163
  </div>
2107
2164
 
2108
2165
  <!-- Footer -->
2109
- <div style="padding: 6px 12px; background: #f7f7f7; border-top: 1px solid #e5e5e5;">
2110
- <div style="font-size: 9px; color: #9ca3af; text-align: center;">
2166
+ <div style="padding: 6px 12px; background: #f7f7f7; border-top: 1px solid #e5e5e5; display: flex; justify-content: center; align-items: center; gap: 8px;">
2167
+ <div style="font-size: 9px; color: #9ca3af;">
2111
2168
  <kbd style="background: #e5e5e5; padding: 1px 4px; border-radius: 2px; font-size: 9px; font-weight: 600; color: #374151;">⌘G</kbd> toggle
2112
2169
  </div>
2170
+ <button x-show="hasStoredOverrides()"
2171
+ @click="resetAllStorage()"
2172
+ style="background: none; border: none; font-size: 9px; color: #d1d5db; cursor: pointer; padding: 0; text-decoration: underline; text-underline-offset: 2px;"
2173
+ onmouseenter="this.style.color='#9ca3af'"
2174
+ onmouseleave="this.style.color='#d1d5db'"
2175
+ title="Clear all saved visualizer state from localStorage">reset</button>
2113
2176
  </div>
2114
2177
 
2115
2178
  <!-- Selected Area Info -->
@@ -2364,9 +2427,24 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
2364
2427
  Reset
2365
2428
  </button>
2366
2429
  </div>
2367
- <button @click="downloadCSS()" style="width: 100%; padding: 10px 12px; font-size: 12px; font-weight: 600; border: none; border-radius: 4px; cursor: pointer; background: #1a1a2e; color: white;">
2368
- Download CSS
2369
- </button>
2430
+ <div style="position: relative; width: 100%;">
2431
+ <button @click="cssDropdownOpen = !cssDropdownOpen" style="width: 100%; padding: 10px 12px; font-size: 12px; font-weight: 600; border: none; border-radius: 4px; cursor: pointer; background: #1a1a2e; color: white; display: flex; align-items: center; justify-content: center; gap: 6px;">
2432
+ Download CSS <span style="font-size: 9px;">&#9662;</span>
2433
+ </button>
2434
+ <div x-show="cssDropdownOpen" @click.away="cssDropdownOpen = false" x-transition
2435
+ style="position: absolute; bottom: 100%; left: 0; right: 0; margin-bottom: 4px; background: white; border: 1px solid #e5e5e5; border-radius: 4px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); overflow: hidden; z-index: 10;">
2436
+ <button @click="downloadCSS('plain'); cssDropdownOpen = false"
2437
+ style="display: block; width: 100%; padding: 10px 16px; font-size: 11px; font-weight: 600; border: none; background: white; color: #374151; cursor: pointer; text-align: left;"
2438
+ @mouseenter="$el.style.background='#f3f4f6'" @mouseleave="$el.style.background='white'">
2439
+ Plain CSS
2440
+ </button>
2441
+ <button @click="downloadCSS('tailwind'); cssDropdownOpen = false"
2442
+ style="display: block; width: 100%; padding: 10px 16px; font-size: 11px; font-weight: 600; border: none; background: white; color: #374151; cursor: pointer; text-align: left; border-top: 1px solid #f3f4f6;"
2443
+ @mouseenter="$el.style.background='#f3f4f6'" @mouseleave="$el.style.background='white'">
2444
+ Tailwind v4
2445
+ </button>
2446
+ </div>
2447
+ </div>
2370
2448
  </div>
2371
2449
  </div>
2372
2450
  </div>
@@ -2522,6 +2600,17 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
2522
2600
  </div>
2523
2601
 
2524
2602
  </div>
2603
+
2604
+ <!-- Persistent override indicator - visible even when visualizer is hidden -->
2605
+ <div x-show="hasConfigOverride && !isVisible"
2606
+ x-transition
2607
+ style="position: fixed; bottom: 12px; right: 12px; z-index: 9998; pointer-events: auto; font-family: system-ui, -apple-system, sans-serif;">
2608
+ <div style="display: flex; align-items: center; gap: 8px; background: #fef3c7; border: 1px solid #fcd34d; border-radius: 6px; padding: 6px 10px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
2609
+ <span style="font-size: 10px; font-weight: 600; color: #92400e;">Grid overridden by visualizer</span>
2610
+ <button @click="resetAllStorage()"
2611
+ style="background: none; border: 1px solid #f59e0b; border-radius: 3px; font-size: 9px; font-weight: 600; color: #92400e; cursor: pointer; padding: 2px 6px;">Clear</button>
2612
+ </div>
2613
+ </div>
2525
2614
  `;
2526
2615
  (function() {
2527
2616
  document.addEventListener("alpine:init", () => {
@@ -2541,6 +2630,7 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
2541
2630
  ...methods,
2542
2631
  // CSS export
2543
2632
  generateCSSExport,
2633
+ generateTailwindCSSExport,
2544
2634
  cssExportVersion: BUILD_VERSION,
2545
2635
  // Template
2546
2636
  template
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Breakout Grid - Objects Layer (ITCSS)
3
- * Version: 5.1.4
3
+ * Version: 5.2.0
4
4
  *
5
5
  * Documentation: https://github.com/astuteo-llc/breakout-grid
6
6
  *
@@ -0,0 +1,653 @@
1
+ /**
2
+ * Breakout Grid - Objects Layer (ITCSS)
3
+ * Version: 5.2.0
4
+ *
5
+ * Documentation: https://github.com/astuteo-llc/breakout-grid
6
+ *
7
+ * ============================================================================
8
+ * TABLE OF CONTENTS
9
+ * ============================================================================
10
+ *
11
+ * CONFIGURATION
12
+ * - Configuration Variables ........... Customizable :root variables
13
+ * - Computed Values ................... Auto-calculated (do not edit)
14
+ *
15
+ * GRID CONTAINERS
16
+ * - Grid Container - Main ............. .grid-cols-breakout
17
+ * - Subgrid ........................... .grid-cols-breakout-subgrid
18
+ * - Left/Right Aligned Variants ....... .grid-cols-{area}-{left|right}
19
+ * - Breakout Modifiers ................ .breakout-to-{content|popout|feature}
20
+ * - Breakout None ..................... .breakout-none, .breakout-none-flex
21
+ *
22
+ * COLUMN UTILITIES
23
+ * - Basic ............................. .col-{full|feature|popout|content|center}
24
+ * - Start/End ......................... .col-start-*, .col-end-*
25
+ * - Left/Right Spans .................. .col-*-left, .col-*-right
26
+ * - Advanced Spans .................... .col-*-to-*
27
+ * - Full Limit ........................ .col-full-limit
28
+ *
29
+ * SPACING UTILITIES
30
+ * - Padding ........................... .p-breakout, .p-gap, .p-*-to-content
31
+ * - Margins ........................... .m-breakout, .m-gap, .-m-*
32
+ *
33
+ * ============================================================================
34
+ * INTEGRATION (Tailwind v4 @utility)
35
+ * ============================================================================
36
+ *
37
+ * This file uses @utility directives — requires Tailwind CSS v4+.
38
+ * All utilities support responsive and state variants:
39
+ *
40
+ * <div class="col-content md:col-feature lg:col-full">...</div>
41
+ *
42
+ * Add this file to your CSS imports:
43
+ *
44
+ * @import 'tailwindcss';
45
+ * @import './_objects.breakout-grid.tw.css';
46
+ *
47
+ * ============================================================================
48
+ * QUICK START
49
+ * ============================================================================
50
+ *
51
+ * <main class="grid-cols-breakout">
52
+ * <article class="col-content">Reading width</article>
53
+ * <figure class="col-feature">Wider for images</figure>
54
+ * <div class="col-full">Edge to edge</div>
55
+ * </main>
56
+ *
57
+ */
58
+
59
+ /* ============================================================================
60
+ CONFIGURATION VARIABLES
61
+ ============================================================================
62
+ To restore this grid in the visualizer, copy from here to END CONFIGURATION.
63
+ Paste into the "Restore" dialog at:
64
+ https://github.com/astuteo-llc/breakout-grid
65
+ ============================================================================ */
66
+
67
+ :root {
68
+ /* Content (text width) */
69
+ --content-min: 53rem;
70
+ --content-base: 75vw;
71
+ --content-max: 61rem;
72
+
73
+ /* Default column for children without col-* class */
74
+ --default-col: content;
75
+
76
+ /* Track widths */
77
+ --popout-width: 5rem;
78
+ --full-limit: 115rem;
79
+
80
+ /* Feature track */
81
+ --feature-min: 0rem;
82
+ --feature-scale: 12vw;
83
+ --feature-max: 12rem;
84
+
85
+ /* Outer margins */
86
+ --base-gap: 1rem;
87
+ --max-gap: 15rem;
88
+
89
+ /* Responsive scale */
90
+ --gap-scale-default: 4vw;
91
+ --gap-scale-lg: 5vw;
92
+ --gap-scale-xl: 6vw;
93
+
94
+ /* Breakout padding */
95
+ --breakout-min: 1rem;
96
+ --breakout-scale: 5vw;
97
+
98
+ /* Breakpoints (used in media queries below) */
99
+ /* --breakpoint-lg: 1024pxpx; */
100
+ /* --breakpoint-xl: 1280pxpx; */
101
+ }
102
+
103
+ /* ============================================================================
104
+ END CONFIGURATION
105
+ ============================================================================ */
106
+
107
+
108
+ /* ============================================================================
109
+ COMPUTED VALUES - DO NOT EDIT
110
+ ============================================================================
111
+ These are calculated from the customizable variables above.
112
+ Editing these directly will break the grid calculations.
113
+ ============================================================================ */
114
+
115
+ :root {
116
+ /* Responsive gap: scales between base and max based on viewport */
117
+ --gap: clamp(var(--base-gap), var(--gap-scale-default), var(--max-gap));
118
+
119
+ /* Computed gap: larger value for full-width spacing */
120
+ --computed-gap: max(var(--gap), calc((100vw - var(--content)) / 10));
121
+
122
+ /* Content width: fluid between min/max, respects gap on both sides */
123
+ --content: min(clamp(var(--content-min), var(--content-base), var(--content-max)), 100% - var(--gap) * 2);
124
+
125
+ /* Content inset: for left/right aligned grids (single gap) */
126
+ --content-inset: min(clamp(var(--content-min), var(--content-base), var(--content-max)), calc(100% - var(--gap)));
127
+
128
+ /* Half content: used for center alignment */
129
+ --content-half: calc(var(--content) / 2);
130
+
131
+ /* Track definitions for grid-template-columns */
132
+ --full: minmax(var(--gap), 1fr);
133
+ --feature: minmax(0, clamp(var(--feature-min), var(--feature-scale), var(--feature-max)));
134
+ --popout: minmax(0, var(--popout-width));
135
+
136
+ /* Alignment padding: for aligning content inside wider columns */
137
+ --breakout-padding: clamp(var(--breakout-min), var(--breakout-scale), var(--popout-width));
138
+ --popout-to-content: clamp(var(--breakout-min), var(--breakout-scale), var(--popout-width));
139
+ --feature-to-content: calc(clamp(var(--feature-min), var(--feature-scale), var(--feature-max)) + var(--popout-width));
140
+ }
141
+
142
+ /* Responsive gap scaling */
143
+ @media (min-width: 1024pxpx) {
144
+ :root {
145
+ --gap: clamp(var(--base-gap), var(--gap-scale-lg), var(--max-gap));
146
+ }
147
+ }
148
+
149
+ @media (min-width: 1280pxpx) {
150
+ :root {
151
+ --gap: clamp(var(--base-gap), var(--gap-scale-xl), var(--max-gap));
152
+ }
153
+ }
154
+
155
+ /* ========================================
156
+ Grid Container - Main
157
+ ========================================
158
+
159
+ The primary grid container. Apply to any element that should use
160
+ the breakout grid system. All direct children default to the
161
+ content column unless given a col-* class.
162
+
163
+ Basic usage:
164
+ <main class="grid-cols-breakout">
165
+ <article>Default content width</article>
166
+ <figure class="col-feature">Wider for images</figure>
167
+ <div class="col-full">Edge to edge</div>
168
+ </main>
169
+ */
170
+ @utility grid-cols-breakout {
171
+ display: grid;
172
+ grid-template-columns:
173
+ [full-start] var(--full)
174
+ [feature-start] var(--feature)
175
+ [popout-start] var(--popout)
176
+ [content-start] var(--content-half) [center-start center-end] var(--content-half) [content-end]
177
+ var(--popout) [popout-end]
178
+ var(--feature) [feature-end]
179
+ var(--full) [full-end];
180
+ }
181
+
182
+ /* Default column for direct children without explicit col-* class */
183
+ [class*='grid-cols-breakout'] > *:not([class*='col-']),
184
+ [class*='grid-cols-feature'] > *:not([class*='col-']),
185
+ [class*='grid-cols-popout'] > *:not([class*='col-']),
186
+ [class*='grid-cols-content'] > *:not([class*='col-']) {
187
+ grid-column: var(--default-col, content);
188
+ }
189
+
190
+ /* ----------------------------------------
191
+ Subgrid - Nested Alignment
192
+ ----------------------------------------
193
+
194
+ Use subgrid when you need children of a spanning element to align
195
+ with the parent grid's tracks. The child inherits the parent's
196
+ column lines.
197
+
198
+ Example - Card grid inside a feature-width container:
199
+ <div class="col-feature grid-cols-breakout-subgrid">
200
+ <h2 class="col-content">Title aligns with content</h2>
201
+ <div class="col-feature">Full width of parent</div>
202
+ </div>
203
+
204
+ Browser support: ~93% (check caniuse.com/css-subgrid)
205
+ */
206
+ @utility grid-cols-breakout-subgrid {
207
+ display: grid;
208
+ grid-template-columns: subgrid;
209
+ }
210
+
211
+ /* ========================================
212
+ Grid Container - Left/Right Aligned Variants
213
+ ========================================
214
+
215
+ Use these when content should anchor to one side instead of centering.
216
+ Common for asymmetric layouts, sidebars, or split-screen designs.
217
+
218
+ Left variants: Content anchors to left edge, right side has outer tracks
219
+ Right variants: Content anchors to right edge, left side has outer tracks
220
+
221
+ Example - Image left, text right:
222
+ <section class="grid-cols-feature-left">
223
+ <figure class="col-feature">Image anchored left</figure>
224
+ <div class="col-content">Text in content area</div>
225
+ </section>
226
+
227
+ Example - Sidebar layout:
228
+ <div class="grid-cols-content-right">
229
+ <aside class="col-full">Sidebar fills left</aside>
230
+ <main class="col-content">Main content right-aligned</main>
231
+ </div>
232
+ */
233
+ @utility grid-cols-feature-left {
234
+ display: grid;
235
+ grid-template-columns:
236
+ [full-start] var(--full)
237
+ [feature-start] var(--feature)
238
+ [popout-start] var(--popout)
239
+ [content-start] var(--content-inset) [content-end]
240
+ var(--popout) [popout-end]
241
+ var(--feature) [feature-end full-end];
242
+ }
243
+
244
+ @utility grid-cols-popout-left {
245
+ display: grid;
246
+ grid-template-columns:
247
+ [full-start] var(--full)
248
+ [feature-start] var(--feature)
249
+ [popout-start] var(--popout)
250
+ [content-start] var(--content-inset) [content-end]
251
+ var(--popout) [popout-end full-end];
252
+ }
253
+
254
+ @utility grid-cols-content-left {
255
+ display: grid;
256
+ grid-template-columns:
257
+ [full-start] var(--full)
258
+ [feature-start] var(--feature)
259
+ [popout-start] var(--popout)
260
+ [content-start] var(--content-inset) [content-end full-end];
261
+ }
262
+
263
+ /* ========================================
264
+ Grid Container - Right Aligned Variants
265
+ ======================================== */
266
+ @utility grid-cols-feature-right {
267
+ display: grid;
268
+ grid-template-columns:
269
+ [full-start feature-start] var(--feature)
270
+ [popout-start] var(--popout)
271
+ [content-start] var(--content-inset) [content-end]
272
+ var(--popout) [popout-end]
273
+ var(--feature) [feature-end]
274
+ var(--full) [full-end];
275
+ }
276
+
277
+ @utility grid-cols-popout-right {
278
+ display: grid;
279
+ grid-template-columns:
280
+ [full-start popout-start] var(--popout)
281
+ [content-start] var(--content-inset) [content-end]
282
+ var(--popout) [popout-end]
283
+ var(--feature) [feature-end]
284
+ var(--full) [full-end];
285
+ }
286
+
287
+ @utility grid-cols-content-right {
288
+ display: grid;
289
+ grid-template-columns:
290
+ [full-start content-start] var(--content-inset) [content-end]
291
+ var(--popout) [popout-end]
292
+ var(--feature) [feature-end]
293
+ var(--full) [full-end];
294
+ }
295
+
296
+ /* ========================================
297
+ Breakout Modifiers (for nested grids)
298
+ ========================================
299
+
300
+ When you nest a grid inside another element (like inside col-feature),
301
+ the nested grid doesn't know about the parent's constraints. Use these
302
+ modifiers to "reset" the grid to fit its container.
303
+
304
+ breakout-to-content: Collapses all tracks - nested grid fills container
305
+ breakout-to-popout: Keeps popout tracks, collapses feature/full
306
+ breakout-to-feature: Keeps feature+popout tracks, collapses full
307
+
308
+ Example - Full-width hero with nested content grid:
309
+ <div class="col-full bg-blue-500">
310
+ <div class="grid-cols-breakout breakout-to-content">
311
+ <h1>This h1 fills the blue container</h1>
312
+ <p class="col-content">But content still works!</p>
313
+ </div>
314
+ </div>
315
+
316
+ Example - Feature-width card with internal grid:
317
+ <article class="col-feature">
318
+ <div class="grid-cols-breakout breakout-to-feature">
319
+ <img class="col-feature">Full width of card</img>
320
+ <p class="col-content">Padded text inside</p>
321
+ </div>
322
+ </article>
323
+ */
324
+ .grid-cols-breakout.breakout-to-content {
325
+ grid-template-columns: [full-start feature-start popout-start content-start center-start] minmax(0, 1fr) [center-end content-end popout-end feature-end full-end];
326
+ }
327
+
328
+ .grid-cols-breakout.breakout-to-popout {
329
+ grid-template-columns: [full-start feature-start popout-start] var(--popout) [content-start center-start] minmax(0, 1fr) [center-end content-end] var(--popout) [popout-end feature-end full-end];
330
+ }
331
+
332
+ .grid-cols-breakout.breakout-to-feature {
333
+ grid-template-columns: [full-start feature-start] var(--feature) [popout-start] var(--popout) [content-start center-start] minmax(0, 1fr) [center-end content-end] var(--popout) [popout-end] var(--feature) [feature-end full-end];
334
+ }
335
+
336
+ /* ----------------------------------------
337
+ Breakout None - Disable Grid
338
+ ----------------------------------------
339
+
340
+ Use when you need to escape the grid entirely. Useful for:
341
+ - Sidebar layouts where one column shouldn't use grid
342
+ - Components that manage their own layout
343
+ - CMS blocks that shouldn't inherit grid behavior
344
+
345
+ Example - Two-column layout with sidebar:
346
+ <div class="grid grid-cols-[300px_1fr]">
347
+ <aside class="breakout-none">Sidebar - no grid here</aside>
348
+ <main class="grid-cols-breakout">Main content uses grid</main>
349
+ </div>
350
+ */
351
+ @utility breakout-none { display: block; }
352
+ @utility breakout-none-flex { display: flex; }
353
+ @utility breakout-none-grid { display: grid; }
354
+
355
+ /* ========================================
356
+ Column Utilities - Basic
357
+ ========================================
358
+
359
+ Place elements in specific grid tracks. These are the core utilities
360
+ you'll use most often.
361
+
362
+ col-full: Edge to edge (viewport width minus gap)
363
+ col-feature: Wide content (images, videos, heroes)
364
+ col-popout: Slightly wider than content (pull quotes, callouts)
365
+ col-content: Standard reading width (articles, text)
366
+ col-center: Centered within content (rare, for precise centering)
367
+ */
368
+ @utility col-full { grid-column: full; }
369
+ @utility col-feature { grid-column: feature; }
370
+ @utility col-popout { grid-column: popout; }
371
+ @utility col-content { grid-column: content; }
372
+ @utility col-center { grid-column: center; }
373
+
374
+ /* Backward compatibility: col-narrow maps to content */
375
+ @utility col-narrow { grid-column: content; }
376
+
377
+ /* ========================================
378
+ Column Utilities - Start/End
379
+ ========================================
380
+
381
+ Fine-grained control for custom spans. Combine start and end
382
+ utilities to create any span you need.
383
+
384
+ Example - Span from popout to feature on right:
385
+ <div class="col-start-popout col-end-feature">
386
+ Custom span
387
+ </div>
388
+ */
389
+ @utility col-start-full { grid-column-start: full-start; }
390
+ @utility col-start-feature { grid-column-start: feature-start; }
391
+ @utility col-start-popout { grid-column-start: popout-start; }
392
+ @utility col-start-content { grid-column-start: content-start; }
393
+ @utility col-start-center { grid-column-start: center-start; }
394
+
395
+ /* Backward compatibility */
396
+ @utility col-start-narrow { grid-column-start: content-start; }
397
+
398
+ @utility col-end-full { grid-column-end: full-end; }
399
+ @utility col-end-feature { grid-column-end: feature-end; }
400
+ @utility col-end-popout { grid-column-end: popout-end; }
401
+ @utility col-end-content { grid-column-end: content-end; }
402
+ @utility col-end-center { grid-column-end: center-end; }
403
+
404
+ /* Backward compatibility */
405
+ @utility col-end-narrow { grid-column-end: content-end; }
406
+
407
+ /* ========================================
408
+ Column Utilities - Left/Right Spans
409
+ ========================================
410
+
411
+ Asymmetric spans that anchor to one edge. Perfect for:
412
+ - Split layouts (image left, text right)
413
+ - Overlapping elements
414
+ - Pull quotes that bleed to one edge
415
+
416
+ Pattern: col-{track}-left = full-start → {track}-end
417
+ col-{track}-right = {track}-start → full-end
418
+
419
+ Example - Image bleeds left, caption stays in content:
420
+ <figure class="col-content-left">
421
+ <img class="w-full">Spans from left edge to content</img>
422
+ </figure>
423
+
424
+ Example - Quote pulls right:
425
+ <blockquote class="col-popout-right">
426
+ Spans from popout through to right edge
427
+ </blockquote>
428
+ */
429
+ @utility col-feature-left { grid-column: full-start / feature-end; }
430
+ @utility col-feature-right { grid-column: feature-start / full-end; }
431
+ @utility col-popout-left { grid-column: full-start / popout-end; }
432
+ @utility col-popout-right { grid-column: popout-start / full-end; }
433
+ @utility col-content-left { grid-column: full-start / content-end; }
434
+ @utility col-content-right { grid-column: content-start / full-end; }
435
+ @utility col-center-left { grid-column: full-start / center-end; }
436
+ @utility col-center-right { grid-column: center-start / full-end; }
437
+
438
+ /* Backward compatibility */
439
+ @utility col-narrow-left { grid-column: full-start / content-end; }
440
+ @utility col-narrow-right { grid-column: content-start / full-end; }
441
+
442
+ /* ========================================
443
+ Column Utilities - Advanced Spans
444
+ ========================================
445
+
446
+ Partial spans between non-adjacent tracks. Use when you need
447
+ elements that span from an inner track outward but not all
448
+ the way to the edge.
449
+
450
+ Example - Card that spans feature to content (not to edge):
451
+ <div class="col-feature-to-content">
452
+ Wide but doesn't bleed to viewport edge
453
+ </div>
454
+ */
455
+ /* Feature to other columns */
456
+ @utility col-feature-to-popout { grid-column: feature-start / popout-end; }
457
+ @utility col-feature-to-content { grid-column: feature-start / content-end; }
458
+ @utility col-feature-to-center { grid-column: feature-start / center-end; }
459
+
460
+ /* Popout to other columns */
461
+ @utility col-popout-to-content { grid-column: popout-start / content-end; }
462
+ @utility col-popout-to-center { grid-column: popout-start / center-end; }
463
+ @utility col-popout-to-feature { grid-column: popout-start / feature-end; }
464
+
465
+ /* Content to other columns */
466
+ @utility col-content-to-center { grid-column: content-start / center-end; }
467
+ @utility col-content-to-popout { grid-column: content-start / popout-end; }
468
+ @utility col-content-to-feature { grid-column: content-start / feature-end; }
469
+
470
+ /* ----------------------------------------
471
+ Full Limit - Capped Full Width
472
+ ----------------------------------------
473
+
474
+ Goes edge-to-edge like col-full, but caps at --full-limit on
475
+ ultra-wide screens. Prevents content from becoming too wide
476
+ on large monitors while still being full-width on normal screens.
477
+
478
+ Example - Hero that doesn't get absurdly wide:
479
+ <section class="col-full-limit">
480
+ Full width up to 115rem, then centered
481
+ </section>
482
+ */
483
+ @utility col-full-limit {
484
+ grid-column: full;
485
+ width: 100%;
486
+ max-width: var(--full-limit);
487
+ margin-left: auto;
488
+ margin-right: auto;
489
+ box-sizing: border-box;
490
+ }
491
+
492
+ /* ========================================
493
+ Padding Utilities
494
+ ========================================
495
+
496
+ Match padding to grid measurements for alignment. These utilities
497
+ help content inside non-grid elements align with the grid.
498
+
499
+ --breakout-padding: Fluid padding that matches popout track behavior
500
+ --gap: Matches the outer grid gap
501
+ --computed-gap: Larger gap for full-width elements
502
+ --popout-to-content: Align edges with content track from popout
503
+ --feature-to-content: Align edges with content track from feature
504
+
505
+ Example - Full-width section with content-aligned padding:
506
+ <section class="col-full bg-gray-100 px-feature-to-content">
507
+ <p>This text aligns with content column above/below</p>
508
+ </section>
509
+
510
+ Example - Card with consistent internal spacing:
511
+ <div class="col-popout p-breakout">
512
+ Padding scales with the grid
513
+ </div>
514
+ */
515
+ @utility p-breakout { padding: var(--breakout-padding); }
516
+ @utility px-breakout { padding-left: var(--breakout-padding); padding-right: var(--breakout-padding); }
517
+ @utility py-breakout { padding-top: var(--breakout-padding); padding-bottom: var(--breakout-padding); }
518
+ @utility pl-breakout { padding-left: var(--breakout-padding); }
519
+ @utility pr-breakout { padding-right: var(--breakout-padding); }
520
+ @utility pt-breakout { padding-top: var(--breakout-padding); }
521
+ @utility pb-breakout { padding-bottom: var(--breakout-padding); }
522
+
523
+ /* Gap-based padding */
524
+ @utility p-gap { padding: var(--gap); }
525
+ @utility px-gap { padding-left: var(--gap); padding-right: var(--gap); }
526
+ @utility py-gap { padding-top: var(--gap); padding-bottom: var(--gap); }
527
+ @utility pl-gap { padding-left: var(--gap); }
528
+ @utility pr-gap { padding-right: var(--gap); }
529
+ @utility pt-gap { padding-top: var(--gap); }
530
+ @utility pb-gap { padding-bottom: var(--gap); }
531
+
532
+ /* Full-gap padding (computed, for full-width elements) */
533
+ @utility p-full-gap { padding: var(--computed-gap); }
534
+ @utility px-full-gap { padding-left: var(--computed-gap); padding-right: var(--computed-gap); }
535
+ @utility py-full-gap { padding-top: var(--computed-gap); padding-bottom: var(--computed-gap); }
536
+ @utility pl-full-gap { padding-left: var(--computed-gap); }
537
+ @utility pr-full-gap { padding-right: var(--computed-gap); }
538
+ @utility pt-full-gap { padding-top: var(--computed-gap); }
539
+ @utility pb-full-gap { padding-bottom: var(--computed-gap); }
540
+
541
+ /* Popout-width padding */
542
+ @utility p-popout { padding: var(--popout); }
543
+ @utility px-popout { padding-left: var(--popout); padding-right: var(--popout); }
544
+ @utility py-popout { padding-top: var(--popout); padding-bottom: var(--popout); }
545
+ @utility pl-popout { padding-left: var(--popout); }
546
+ @utility pr-popout { padding-right: var(--popout); }
547
+ @utility pt-popout { padding-top: var(--popout); }
548
+ @utility pb-popout { padding-bottom: var(--popout); }
549
+
550
+ /* Alignment padding - align content inside wider columns */
551
+ @utility p-popout-to-content { padding: var(--popout-to-content); }
552
+ @utility px-popout-to-content { padding-left: var(--popout-to-content); padding-right: var(--popout-to-content); }
553
+ @utility py-popout-to-content { padding-top: var(--popout-to-content); padding-bottom: var(--popout-to-content); }
554
+ @utility pt-popout-to-content { padding-top: var(--popout-to-content); }
555
+ @utility pr-popout-to-content { padding-right: var(--popout-to-content); }
556
+ @utility pb-popout-to-content { padding-bottom: var(--popout-to-content); }
557
+ @utility pl-popout-to-content { padding-left: var(--popout-to-content); }
558
+
559
+ @utility p-feature-to-content { padding: var(--feature-to-content); }
560
+ @utility px-feature-to-content { padding-left: var(--feature-to-content); padding-right: var(--feature-to-content); }
561
+ @utility py-feature-to-content { padding-top: var(--feature-to-content); padding-bottom: var(--feature-to-content); }
562
+ @utility pt-feature-to-content { padding-top: var(--feature-to-content); }
563
+ @utility pr-feature-to-content { padding-right: var(--feature-to-content); }
564
+ @utility pb-feature-to-content { padding-bottom: var(--feature-to-content); }
565
+ @utility pl-feature-to-content { padding-left: var(--feature-to-content); }
566
+
567
+ /* ========================================
568
+ Margin Utilities
569
+ ========================================
570
+
571
+ Same values as padding utilities, but for margins. Includes
572
+ negative variants for pulling elements outside their container.
573
+
574
+ Example - Pull image outside its container:
575
+ <div class="col-content">
576
+ <img class="-mx-breakout">Bleeds into popout area</img>
577
+ </div>
578
+
579
+ Example - Overlap previous section:
580
+ <section class="-mt-gap">
581
+ Pulls up into the section above
582
+ </section>
583
+ */
584
+ @utility m-breakout { margin: var(--breakout-padding); }
585
+ @utility mx-breakout { margin-left: var(--breakout-padding); margin-right: var(--breakout-padding); }
586
+ @utility my-breakout { margin-top: var(--breakout-padding); margin-bottom: var(--breakout-padding); }
587
+ @utility ml-breakout { margin-left: var(--breakout-padding); }
588
+ @utility mr-breakout { margin-right: var(--breakout-padding); }
589
+ @utility mt-breakout { margin-top: var(--breakout-padding); }
590
+ @utility mb-breakout { margin-bottom: var(--breakout-padding); }
591
+
592
+ /* Negative margins */
593
+ @utility -m-breakout { margin: calc(var(--breakout-padding) * -1); }
594
+ @utility -mx-breakout { margin-left: calc(var(--breakout-padding) * -1); margin-right: calc(var(--breakout-padding) * -1); }
595
+ @utility -my-breakout { margin-top: calc(var(--breakout-padding) * -1); margin-bottom: calc(var(--breakout-padding) * -1); }
596
+ @utility -ml-breakout { margin-left: calc(var(--breakout-padding) * -1); }
597
+ @utility -mr-breakout { margin-right: calc(var(--breakout-padding) * -1); }
598
+ @utility -mt-breakout { margin-top: calc(var(--breakout-padding) * -1); }
599
+ @utility -mb-breakout { margin-bottom: calc(var(--breakout-padding) * -1); }
600
+
601
+ /* Gap-based margins */
602
+ @utility m-gap { margin: var(--gap); }
603
+ @utility mx-gap { margin-left: var(--gap); margin-right: var(--gap); }
604
+ @utility my-gap { margin-top: var(--gap); margin-bottom: var(--gap); }
605
+ @utility ml-gap { margin-left: var(--gap); }
606
+ @utility mr-gap { margin-right: var(--gap); }
607
+ @utility mt-gap { margin-top: var(--gap); }
608
+ @utility mb-gap { margin-bottom: var(--gap); }
609
+
610
+ /* Negative margins */
611
+ @utility -m-gap { margin: calc(var(--gap) * -1); }
612
+ @utility -mx-gap { margin-left: calc(var(--gap) * -1); margin-right: calc(var(--gap) * -1); }
613
+ @utility -my-gap { margin-top: calc(var(--gap) * -1); margin-bottom: calc(var(--gap) * -1); }
614
+ @utility -ml-gap { margin-left: calc(var(--gap) * -1); }
615
+ @utility -mr-gap { margin-right: calc(var(--gap) * -1); }
616
+ @utility -mt-gap { margin-top: calc(var(--gap) * -1); }
617
+ @utility -mb-gap { margin-bottom: calc(var(--gap) * -1); }
618
+
619
+ /* Full-gap margins */
620
+ @utility m-full-gap { margin: var(--computed-gap); }
621
+ @utility mx-full-gap { margin-left: var(--computed-gap); margin-right: var(--computed-gap); }
622
+ @utility my-full-gap { margin-top: var(--computed-gap); margin-bottom: var(--computed-gap); }
623
+ @utility ml-full-gap { margin-left: var(--computed-gap); }
624
+ @utility mr-full-gap { margin-right: var(--computed-gap); }
625
+ @utility mt-full-gap { margin-top: var(--computed-gap); }
626
+ @utility mb-full-gap { margin-bottom: var(--computed-gap); }
627
+
628
+ /* Negative margins */
629
+ @utility -m-full-gap { margin: calc(var(--computed-gap) * -1); }
630
+ @utility -mx-full-gap { margin-left: calc(var(--computed-gap) * -1); margin-right: calc(var(--computed-gap) * -1); }
631
+ @utility -my-full-gap { margin-top: calc(var(--computed-gap) * -1); margin-bottom: calc(var(--computed-gap) * -1); }
632
+ @utility -ml-full-gap { margin-left: calc(var(--computed-gap) * -1); }
633
+ @utility -mr-full-gap { margin-right: calc(var(--computed-gap) * -1); }
634
+ @utility -mt-full-gap { margin-top: calc(var(--computed-gap) * -1); }
635
+ @utility -mb-full-gap { margin-bottom: calc(var(--computed-gap) * -1); }
636
+
637
+ /* Popout-width margins */
638
+ @utility m-popout { margin: var(--popout); }
639
+ @utility mx-popout { margin-left: var(--popout); margin-right: var(--popout); }
640
+ @utility my-popout { margin-top: var(--popout); margin-bottom: var(--popout); }
641
+ @utility ml-popout { margin-left: var(--popout); }
642
+ @utility mr-popout { margin-right: var(--popout); }
643
+ @utility mt-popout { margin-top: var(--popout); }
644
+ @utility mb-popout { margin-bottom: var(--popout); }
645
+
646
+ /* Negative margins */
647
+ @utility -m-popout { margin: calc(var(--popout) * -1); }
648
+ @utility -mx-popout { margin-left: calc(var(--popout) * -1); margin-right: calc(var(--popout) * -1); }
649
+ @utility -my-popout { margin-top: calc(var(--popout) * -1); margin-bottom: calc(var(--popout) * -1); }
650
+ @utility -ml-popout { margin-left: calc(var(--popout) * -1); }
651
+ @utility -mr-popout { margin-right: calc(var(--popout) * -1); }
652
+ @utility -mt-popout { margin-top: calc(var(--popout) * -1); }
653
+ @utility -mb-popout { margin-bottom: calc(var(--popout) * -1); }
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@astuteo/breakout-grid",
3
- "version": "5.1.4",
3
+ "version": "5.2.0",
4
4
  "type": "module",
5
5
  "description": "CSS Grid breakout layout system with visual configurator",
6
6
  "main": "dist/_objects.breakout-grid.css",
7
7
  "style": "dist/_objects.breakout-grid.css",
8
8
  "files": [
9
9
  "dist/_objects.breakout-grid.css",
10
+ "dist/_objects.breakout-grid.tw.css",
10
11
  "breakout-grid-visualizer.js",
11
12
  "breakout-grid-visualizer-lite.js",
12
13
  "craft-integration.twig",
@@ -16,6 +17,7 @@
16
17
  "exports": {
17
18
  ".": "./dist/_objects.breakout-grid.css",
18
19
  "./css": "./dist/_objects.breakout-grid.css",
20
+ "./tailwind": "./dist/_objects.breakout-grid.tw.css",
19
21
  "./visualizer": "./breakout-grid-visualizer.js",
20
22
  "./visualizer-lite": "./breakout-grid-visualizer-lite.js"
21
23
  },