@kubex/zinc 1.1.67 → 1.1.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/custom-elements.json +1288 -20
  2. package/dist/vscode.html-custom-data.json +167 -11
  3. package/dist/web-types.json +360 -17
  4. package/dist/zn.d.ts +368 -20
  5. package/dist/zn.min.js +764 -645
  6. package/docs/pages/components/collapsible.md +6 -2
  7. package/docs/pages/components/preview-frame-demo.njk +33 -4
  8. package/docs/pages/components/preview-frame.md +20 -0
  9. package/docs/pages/components/theme-editor.md +269 -0
  10. package/docs/superpowers/plans/2026-08-03-theme-editor.md +1536 -0
  11. package/docs/superpowers/specs/2026-08-03-theme-editor-design.md +327 -0
  12. package/package.json +1 -1
  13. package/src/components/collapsible/collapsible.component.ts +21 -23
  14. package/src/components/collapsible/collapsible.scss +10 -0
  15. package/src/components/data-table/data-table.component.ts +49 -44
  16. package/src/components/data-table/data-table.scss +4 -0
  17. package/src/components/data-table/data-table.test.ts +42 -0
  18. package/src/components/defined-label/defined-label.component.ts +118 -95
  19. package/src/components/defined-label/defined-label.scss +40 -28
  20. package/src/components/dropdown/dropdown.component.ts +9 -0
  21. package/src/components/editor/editor.component.ts +20 -21
  22. package/src/components/file/file.component.ts +70 -0
  23. package/src/components/page-builder/page-builder.scss +1 -1
  24. package/src/components/preview-frame/preview-frame.component.ts +126 -16
  25. package/src/components/preview-frame/preview-frame.scss +27 -0
  26. package/src/components/preview-frame/preview-frame.test.ts +253 -0
  27. package/src/components/theme-editor/index.ts +12 -0
  28. package/src/components/theme-editor/theme-editor.component.ts +761 -0
  29. package/src/components/theme-editor/theme-editor.scss +309 -0
  30. package/src/components/theme-editor/theme-editor.test.ts +1584 -0
  31. package/src/events/events.ts +2 -0
  32. package/src/events/zn-theme-change.ts +13 -0
  33. package/src/events/zn-theme-submit.ts +9 -0
  34. package/src/types/web-test-runner-commands.d.ts +6 -0
  35. package/src/zinc.ts +1 -0
  36. package/tsconfig.json +7 -1
  37. package/web-test-runner.config.js +3 -1
@@ -1,15 +1,23 @@
1
- import {type CSSResultGroup, html, type PropertyValues, type TemplateResult, unsafeCSS} from 'lit';
1
+ import {type CSSResultGroup, html, nothing, type PropertyValues, type TemplateResult, unsafeCSS} from 'lit';
2
2
  import {FormControlController} from '../../internal/form';
3
3
  import {ifDefined} from 'lit/directives/if-defined.js';
4
4
  import {property, query} from 'lit/decorators.js';
5
5
  import {watch} from '../../internal/watch';
6
6
  import ZincElement from '../../internal/zinc-element';
7
+ import ZnButton from '../button';
8
+ import ZnDropdown from '../dropdown';
9
+ import ZnInput from '../input';
10
+ import ZnOption from '../option';
11
+ import ZnSelect from '../select';
7
12
  import type {ZincFormControl} from '../../internal/zinc-element';
8
- import type ZnDropdown from '../dropdown';
9
- import type ZnInput from '../input';
10
13
 
11
14
  import styles from './defined-label.scss';
12
15
 
16
+ interface PredefinedLabel {
17
+ name: string;
18
+ options?: string[];
19
+ }
20
+
13
21
  /**
14
22
  * @summary This component provides a labeled input with support for predefined and custom labels,
15
23
  * allowing users to select or enter label-value pairs within a dropdown interface.
@@ -21,31 +29,51 @@ import styles from './defined-label.scss';
21
29
  * @dependency zn-dropdown
22
30
  * @dependency zn-input
23
31
  * @dependency zn-option
24
- * @dependency zn-panel
25
32
  * @dependency zn-select
26
- * @dependency zn-sp
27
33
  *
28
34
  * @csspart input - The component's main input.
29
35
  * @csspart input-value - The label's value inputs.
30
36
  */
31
37
  export default class ZnDefinedLabel extends ZincElement implements ZincFormControl {
32
38
  static styles: CSSResultGroup = unsafeCSS(styles);
39
+ static dependencies = {
40
+ 'zn-button': ZnButton,
41
+ 'zn-dropdown': ZnDropdown,
42
+ 'zn-input': ZnInput,
43
+ 'zn-option': ZnOption,
44
+ 'zn-select': ZnSelect
45
+ };
33
46
 
34
47
  private readonly formControlController = new FormControlController(this, {
35
48
  value: (control: this) => control.value + (control.inputValue ? `:${control.inputValue}` : ''),
36
49
  });
37
50
 
38
- @query('.input__control') input: ZnInput;
51
+ @query('.defined-label__input') input: ZnInput;
39
52
  @query('.defined-label__dropdown') dropdown: ZnDropdown;
40
53
 
54
+ /** The selected label key. Also acts as the filter while typing. */
41
55
  @property() value: string = '';
56
+
57
+ /** The value entered for the selected label. Submitted as `label:value` when present. */
42
58
  @property() inputValue: string = '';
59
+
60
+ /** The size of the main input. */
43
61
  @property({attribute: 'input-size', reflect: true}) inputSize: 'x-small' | 'small' | 'medium' | 'large' = 'medium';
62
+
63
+ /** The name of the control. Used for form submission. */
44
64
  @property() name: string = 'label';
65
+
66
+ /** The title of the main input. */
45
67
  @property() title: string;
68
+
69
+ /** Disables the control. */
46
70
  @property({type: Boolean}) disabled: boolean = false;
71
+
72
+ /** Allows labels that aren't in the predefined list. */
47
73
  @property({attribute: 'allow-custom', type: Boolean}) allowCustom: boolean = false;
48
- @property({type: Array, attribute: 'predefined-labels'}) predefinedLabels = [];
74
+
75
+ /** The predefined labels. Entries are either a string or `{name, options}`. */
76
+ @property({type: Array, attribute: 'predefined-labels'}) predefinedLabels: (PredefinedLabel | string)[] = [];
49
77
 
50
78
  get validationMessage() {
51
79
  return this.input.validationMessage;
@@ -83,6 +111,14 @@ export default class ZnDefinedLabel extends ZincElement implements ZincFormContr
83
111
  this.formControlController.updateValidity();
84
112
  }
85
113
 
114
+ private getFilteredLabels(): PredefinedLabel[] {
115
+ const filter = this.value.toLowerCase();
116
+ return this.predefinedLabels
117
+ .map(label => typeof label === 'string' ? {name: label} : label)
118
+ .filter((label): label is PredefinedLabel => Boolean(label?.name))
119
+ .filter(label => !filter || label.name.toLowerCase().includes(filter));
120
+ }
121
+
86
122
  private handleChange() {
87
123
  if (!this.dropdown.open) {
88
124
  this.dropdown.show().then(r => r);
@@ -117,14 +153,18 @@ export default class ZnDefinedLabel extends ZincElement implements ZincFormContr
117
153
  if (target.hasAttribute('data-label')) this.value = target.getAttribute('data-label') ?? "";
118
154
  }
119
155
 
120
- private handleInputValueInput(e: Event) {
121
- const target = e.target as HTMLInputElement | HTMLSelectElement;
122
- this.inputValue = target.value.toLowerCase();
123
-
124
- if (target.hasAttribute('data-label')) this.value = target.getAttribute('data-label') ?? "";
125
- }
156
+ private handleFormSubmit(e: Event, label?: string) {
157
+ // Sync from the submitted row so a partially typed filter never wins over
158
+ // the row's actual label key, and stale values from other rows are discarded
159
+ const row = (e.currentTarget as HTMLElement).closest('.defined-label__row');
160
+ const control = row?.querySelector<ZnInput | ZnSelect>('.defined-label__value');
161
+ if (label) {
162
+ this.value = label;
163
+ }
164
+ if (control) {
165
+ this.inputValue = String(control.value ?? '').toLowerCase();
166
+ }
126
167
 
127
- private handleFormSubmit() {
128
168
  const form = this.formControlController.getForm();
129
169
 
130
170
  if (form && form.reportValidity()) {
@@ -135,106 +175,89 @@ export default class ZnDefinedLabel extends ZincElement implements ZincFormContr
135
175
  }
136
176
  }
137
177
 
138
- render() {
139
- let predefinedLabels = html``;
140
-
141
- if (this.predefinedLabels.length > 0) {
142
- this.predefinedLabels.forEach((label: { [key: string]: any } | string | null) => {
143
- // label = ['name' => 'label', 'options'=>['one', 'two', 'three']]
144
- let options: string[] | undefined;
145
- if (label && typeof label !== 'string') {
146
- options = label.options as string[];
147
- label = label.name as string;
148
- }
149
-
150
- if (this.value && !label?.toLowerCase().includes(this.value.toLowerCase())) {
151
- return;
152
- }
153
-
154
- let selector: TemplateResult<1>;
155
- if (options && options.length > 0) {
156
- selector = html`
157
- <zn-select
158
- part="input-value"
159
- id="input-value input-value-${label}"
160
- class="input__control-value input__control-value--${label}"
161
- data-label="${label}"
162
- @zn-change="${this.handleInputValueChange}"
163
- @zn-input="${this.handleInputValueInput}"
164
- size="small">
165
- <zn-option value="">Select ${label}</zn-option>
166
- ${options.map((option: string) => html`
167
- <zn-option value="${option}">${option}</zn-option>
168
- `)}
169
- </zn-select>`;
170
- } else {
171
- selector = html`
172
- <zn-input
173
- part="input-value"
174
- id="input-value input-value-${label}"
175
- class="input__control-value input__control-value--${label}"
176
- type="text"
177
- data-label="${label}"
178
- @zn-change="${this.handleInputValueChange}"
179
- @zn-input="${this.handleInputValueInput}"
180
- size="small"></zn-input>`;
181
- }
182
-
183
- predefinedLabels = html`
184
- <div class="defined-label__input">
185
- <small>${label}</small>
186
- <div class="defined-label__input-wrap">
187
- ${selector}
188
- <zn-button type="submit" icon="add" @click="${this.handleFormSubmit}"></zn-button>
189
- </div>
190
- </div>`;
191
- });
178
+ private renderValueControl(label: PredefinedLabel): TemplateResult {
179
+ if (label.options && label.options.length > 0) {
180
+ return html`
181
+ <zn-select
182
+ part="input-value"
183
+ class="defined-label__value"
184
+ data-label="${label.name}"
185
+ size="small"
186
+ @zn-change="${this.handleInputValueChange}"
187
+ @zn-input="${this.handleInputValueChange}">
188
+ <zn-option value="">Select ${label.name}</zn-option>
189
+ ${label.options.map(option => html`
190
+ <zn-option value="${option}">${option}</zn-option>`)}
191
+ </zn-select>`;
192
192
  }
193
193
 
194
194
  return html`
195
- <zn-dropdown class="defined-label__dropdown">
195
+ <zn-input
196
+ part="input-value"
197
+ class="defined-label__value"
198
+ type="text"
199
+ placeholder="Label Value"
200
+ data-label="${label.name}"
201
+ size="small"
202
+ @zn-change="${this.handleInputValueChange}"
203
+ @zn-input="${this.handleInputValueChange}"></zn-input>`;
204
+ }
205
+
206
+ private renderRow(name: string, control: TemplateResult, label?: string): TemplateResult {
207
+ return html`
208
+ <div class="defined-label__row">
209
+ <small class="defined-label__row-label">${name}</small>
210
+ <div class="defined-label__row-controls">
211
+ ${control}
212
+ <zn-button
213
+ type="submit"
214
+ icon="add"
215
+ @click="${(e: Event) => this.handleFormSubmit(e, label)}"></zn-button>
216
+ </div>
217
+ </div>`;
218
+ }
219
+
220
+ render() {
221
+ const labels = this.getFilteredLabels();
222
+
223
+ return html`
224
+ <zn-dropdown class="defined-label__dropdown" sync="width">
196
225
  <zn-input
197
226
  part="input"
198
227
  id="input"
199
- class="input__control"
228
+ class="defined-label__input"
200
229
  type="text"
201
- title="${this.title}"
230
+ title="${ifDefined(this.title)}"
202
231
  value="${this.value}"
203
232
  name="${ifDefined(this.name)}"
204
233
  placeholder="Add a Label"
205
234
  maxlength="60"
206
235
  autocomplete="off"
207
236
  size="${this.inputSize}"
237
+ ?disabled="${this.disabled}"
208
238
  @zn-change="${this.handleChange}"
209
239
  @zn-input="${this.handleInput}"
210
240
  @click="${this.handleClick}"
211
241
  slot="trigger"
212
242
  ></zn-input>
213
243
 
214
- <zn-panel class="defined-label__container">
215
- <zn-sp flush divide>
216
- ${predefinedLabels.values.length > 0 ? predefinedLabels : html`
217
- <div class="defined-label__input">
218
- <small>Cannot find any predefined labels</small>
219
- </div>`}
220
-
221
- ${this.allowCustom && this.value !== '' ? html`
222
- <div class="defined-label__input">
223
- <small>${this.value}</small>
224
- <div class="defined-label__input-wrap">
225
- <zn-input part="input-value"
226
- placeholder="Label Value"
227
- type="text"
228
- size="small"
229
- maxlength="60"
230
- @zn-change="${this.handleInputValueChange}"
231
- @zn-input="${this.handleInputValueInput}"></zn-input>
232
- <zn-button type="submit" icon="add" @click="${this.handleFormSubmit}"></zn-button>
233
- </div>
234
- </div>` : ''}
235
-
236
- </zn-sp>
237
- </zn-panel>
244
+ <div class="defined-label__panel">
245
+ ${labels.length > 0
246
+ ? labels.map(label => this.renderRow(label.name, this.renderValueControl(label), label.name))
247
+ : html`
248
+ <div class="defined-label__empty">Cannot find any predefined labels</div>`}
249
+
250
+ ${this.allowCustom && this.value !== '' ? this.renderRow(this.value, html`
251
+ <zn-input
252
+ part="input-value"
253
+ class="defined-label__value"
254
+ placeholder="Label Value"
255
+ type="text"
256
+ size="small"
257
+ maxlength="60"
258
+ @zn-change="${this.handleInputValueChange}"
259
+ @zn-input="${this.handleInputValueChange}"></zn-input>`) : nothing}
260
+ </div>
238
261
  </zn-dropdown>
239
262
  `;
240
263
  }
@@ -4,40 +4,52 @@
4
4
  display: contents;
5
5
  }
6
6
 
7
- .defined-label {
8
- .input__control, .input__control-value {
9
- background-color: rgb(var(--zn-panel));
10
- }
7
+ .defined-label__dropdown {
8
+ width: 100%;
9
+ }
11
10
 
12
- &__dropdown {
13
- width: 100%;
14
- }
11
+ .defined-label__panel {
12
+ display: flex;
13
+ flex-direction: column;
14
+ min-width: 260px;
15
+ max-height: 275px;
16
+ overflow-y: auto;
17
+ overscroll-behavior: none;
18
+ background-color: rgb(var(--zn-panel));
19
+ border: 1px solid rgb(var(--zn-border-color));
20
+ border-radius: var(--zn-border-radius);
21
+ box-shadow: 0 4px 8px -4px rgba(33, 33, 33, 0.1);
22
+ }
15
23
 
16
- &__container {
17
- display: flex;
18
- flex-direction: row;
19
- align-items: center;
20
- gap: var(--zn-spacing-small);
21
- width: 100%;
24
+ .defined-label__row {
25
+ display: flex;
26
+ flex-direction: column;
27
+ gap: var(--zn-spacing-x-small);
28
+ padding: var(--zn-spacing-small);
22
29
 
23
- zn-sp {
24
- max-height: 275px;
25
- }
30
+ & + & {
31
+ border-top: 1px solid rgb(var(--zn-border-color));
26
32
  }
33
+ }
27
34
 
28
- &__input {
29
- display: flex;
30
- flex-direction: column;
31
- gap: 8px;
35
+ .defined-label__row-label {
36
+ font-size: var(--zn-font-size-small);
37
+ color: rgb(var(--zn-text-muted));
38
+ }
32
39
 
33
- &-wrap {
34
- display: flex;
35
- align-items: center;
36
- gap: 8px;
37
- }
40
+ .defined-label__row-controls {
41
+ display: flex;
42
+ align-items: center;
43
+ gap: var(--zn-spacing-x-small);
38
44
 
39
- zn-input, zn-select {
40
- flex-grow: 1;
41
- }
45
+ .defined-label__value {
46
+ flex-grow: 1;
42
47
  }
43
48
  }
49
+
50
+ .defined-label__empty {
51
+ padding: var(--zn-spacing-small);
52
+ font-size: var(--zn-font-size-small);
53
+ color: rgb(var(--zn-text-muted));
54
+ text-align: center;
55
+ }
@@ -335,6 +335,15 @@ export default class ZnDropdown extends ZincElement {
335
335
  this.emit('zn-hide');
336
336
  topLayerManager.unregisterDropdown(this);
337
337
  this.removeOpenListeners();
338
+
339
+ // Move focus out of the panel before it becomes aria-hidden/inert,
340
+ // otherwise the browser blocks aria-hidden on the focused item and warns
341
+ const active = (this.getRootNode() as Document | ShadowRoot).activeElement as HTMLElement | null;
342
+ if (active && this.panel.assignedElements({flatten: true}).some(el => el.contains(active))) {
343
+ this.focusOnTrigger();
344
+ active.blur();
345
+ }
346
+
338
347
  this.panel.blur();
339
348
  this.panel.hidden = true;
340
349
  this.popup.active = false;
@@ -247,28 +247,27 @@ export default class ZnEditor extends ZincElement implements ZincFormControl {
247
247
  onFileUploaded: () => {
248
248
  window.onbeforeunload = () => null;
249
249
  },
250
- upload: (file: File) => {
250
+ upload: async (file: File) => {
251
251
  window.onbeforeunload = () => 'You have unsaved changes. Are you sure you want to leave?';
252
- return new Promise((resolve) => {
253
- const fd = new FormData();
254
- fd.append('filename', file.name);
255
- fd.append('size', file.size.toString());
256
- fd.append('mimeType', file.type);
257
-
258
- const xhr = new XMLHttpRequest();
259
- xhr.open('POST', this.uploadAttachmentUrl, true);
260
- xhr.onload = () => {
261
- if (xhr.status === 200) {
262
- const response = JSON.parse(xhr.responseText) as {
263
- uploadPath: string;
264
- uploadUrl: string;
265
- originalFilename: string;
266
- };
267
- resolve({path: response.uploadPath, url: response.uploadUrl, filename: response.originalFilename});
268
- }
269
- };
270
- xhr.send(fd);
271
- });
252
+
253
+ const fd = new FormData();
254
+ fd.append('filename', file.name);
255
+ fd.append('size', file.size.toString());
256
+ fd.append('mimeType', file.type);
257
+
258
+ const res = await fetch(this.uploadAttachmentUrl, {method: 'POST', body: fd});
259
+ if (!res.ok) throw new Error(`Upload request failed: ${res.status}`);
260
+ const response = await res.json() as {
261
+ uploadPath: string;
262
+ uploadUrl: string;
263
+ originalFilename: string;
264
+ };
265
+
266
+ // resolve relative upload targets against the attachment url so both keep
267
+ // the same base path when the host page rewrites upload-attachment-url (e.g. the
268
+ // kubex console proxying apps under an app base); absolute urls pass through
269
+ const url = new URL(response.uploadUrl, new URL(this.uploadAttachmentUrl, window.location.href)).toString();
270
+ return {path: response.uploadPath, url, filename: response.originalFilename};
272
271
  },
273
272
  },
274
273
  }),
@@ -78,7 +78,13 @@ export default class ZnFile extends ZincElement implements ZincFormControl {
78
78
  assumeInteractionOn: ['zn-change'],
79
79
  // An explicitly cleared control submits an empty value so the server can
80
80
  // remove the stored upload; an untouched empty control submits nothing.
81
+ // In upload-url mode the control submits the uploaded file's URL string
82
+ // instead of the file bytes.
81
83
  value: (el: ZnFile) => {
84
+ if (el.uploadUrl) {
85
+ if (el.src) return el.src;
86
+ return el.clearedByUser ? '' : undefined;
87
+ }
82
88
  if (el.files?.length) return el.files;
83
89
  return el.clearedByUser ? '' : undefined;
84
90
  }
@@ -148,12 +154,21 @@ export default class ZnFile extends ZincElement implements ZincFormControl {
148
154
  */
149
155
  @property({type: String})
150
156
  set value(v: string) {
157
+ // In upload-url mode the value is the uploaded file's URL, held in `src`
158
+ // (a native file input only accepts an empty string as value).
159
+ if (this.uploadUrl) {
160
+ this.src = v ?? '';
161
+ return;
162
+ }
151
163
  if (this.input) {
152
164
  this.input.value = v;
153
165
  }
154
166
  }
155
167
 
156
168
  get value() {
169
+ if (this.uploadUrl) {
170
+ return this.src ?? '';
171
+ }
157
172
  return this.input?.value;
158
173
  }
159
174
 
@@ -254,6 +269,16 @@ export default class ZnFile extends ZincElement implements ZincFormControl {
254
269
  */
255
270
  @property({type: Boolean, attribute: 'show-link'}) showLink = false;
256
271
 
272
+ /**
273
+ * When set, a chosen file is immediately POSTed (multipart, field `file`) to this URL.
274
+ * The endpoint must respond with JSON `{"url": "..."}`; that URL becomes the control's
275
+ * value (and `src` preview) in place of the file bytes, so the control works inside
276
+ * JSON-serialising hosts such as `zn-page-builder`. Emits `zn-error` when the upload fails.
277
+ */
278
+ @property({attribute: 'upload-url'}) uploadUrl = '';
279
+
280
+ @state() private uploading = false;
281
+
257
282
  /** Gets the validity state object */
258
283
  get validity() {
259
284
  return this.input?.validity;
@@ -387,6 +412,14 @@ export default class ZnFile extends ZincElement implements ZincFormControl {
387
412
  e.stopPropagation();
388
413
 
389
414
  this.updatePreview();
415
+
416
+ const file = this.files?.[0];
417
+ if (this.uploadUrl && file) {
418
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
419
+ this.uploadFile(file);
420
+ return;
421
+ }
422
+
390
423
  this.emit('zn-input');
391
424
  this.emit('zn-change');
392
425
 
@@ -395,6 +428,42 @@ export default class ZnFile extends ZincElement implements ZincFormControl {
395
428
  }
396
429
  }
397
430
 
431
+ /**
432
+ * Uploads the chosen file to `uploadUrl` and adopts the returned URL as the
433
+ * control's value. The local file preview stays visible while the upload runs.
434
+ */
435
+ private async uploadFile(file: File) {
436
+ this.uploading = true;
437
+ try {
438
+ const body = new FormData();
439
+ body.append('file', file, file.name);
440
+ const response = await fetch(this.uploadUrl, {method: 'POST', body, credentials: 'same-origin'});
441
+ if (!response.ok) {
442
+ throw new Error(`upload failed with status ${response.status}`);
443
+ }
444
+ const data = await response.json() as { url?: string };
445
+ if (!data.url) {
446
+ throw new Error('upload response missing url');
447
+ }
448
+ this.src = data.url;
449
+ this.input.value = '';
450
+ this.files = null;
451
+ this.clearedByUser = false;
452
+ this.emit('zn-input');
453
+ this.emit('zn-change');
454
+ if (this.triggerSubmit) {
455
+ this.formControlController.submit();
456
+ }
457
+ } catch (error) {
458
+ console.warn('<zn-file> upload failed', error);
459
+ this.input.value = '';
460
+ this.files = null;
461
+ this.emit('zn-error');
462
+ } finally {
463
+ this.uploading = false;
464
+ }
465
+ }
466
+
398
467
  private handleDragOver = (e: DragEvent) => {
399
468
  e.preventDefault();
400
469
  e.stopPropagation();
@@ -698,6 +767,7 @@ export default class ZnFile extends ZincElement implements ZincFormControl {
698
767
  'form-control--large': this.size === 'large',
699
768
  'form-control--medium': this.size === 'medium',
700
769
  'form-control--small': this.size === 'small',
770
+ 'form-control--uploading': this.uploading,
701
771
  'form-control--user-dragging': this.userIsDragging,
702
772
  })}"
703
773
  @dragenter="${this.handleDragOver}"
@@ -5,7 +5,7 @@
5
5
  // Canonical canvas card/container width, inherited by the section-card
6
6
  // component's shadow styles (whose 560px var() fallback only applies when
7
7
  // the card is used outside a builder).
8
- --pb-card-width: 560px;
8
+ --pb-card-width: 1024px;
9
9
 
10
10
  display: block;
11
11
  width: 100%;