editor-shell 0.23.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-EZKBKK3E.js → chunk-VEXTYAMU.js} +3 -3
- package/dist/chunk-VEXTYAMU.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/panel/index.d.ts +122 -1
- package/dist/panel/index.js +10 -1
- package/dist/panel/index.js.map +1 -1
- package/dist/panel/panel.css +94 -22
- package/dist/shell/index.d.ts +1 -1
- package/dist/shell/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-EZKBKK3E.js.map +0 -1
|
@@ -22,7 +22,7 @@ var shellTokens = {
|
|
|
22
22
|
previewBg: "#f3f4f6",
|
|
23
23
|
/** `--es-panel` — raised cards / panels. */
|
|
24
24
|
panel: "#ffffff",
|
|
25
|
-
/** `--es-raised` — active
|
|
25
|
+
/** `--es-raised` — a lifted surface. NOT the active chip since card 66302. */
|
|
26
26
|
raised: "#ffffff",
|
|
27
27
|
/** `--es-topbar-bg`. */
|
|
28
28
|
topbarBg: "#ffffff",
|
|
@@ -205,5 +205,5 @@ var puckAzureRamp = {
|
|
|
205
205
|
};
|
|
206
206
|
|
|
207
207
|
export { puckAzureRamp, shellCssVars, shellTokens };
|
|
208
|
-
//# sourceMappingURL=chunk-
|
|
209
|
-
//# sourceMappingURL=chunk-
|
|
208
|
+
//# sourceMappingURL=chunk-VEXTYAMU.js.map
|
|
209
|
+
//# sourceMappingURL=chunk-VEXTYAMU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shell/tokens.ts"],"names":[],"mappings":"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","file":"chunk-VEXTYAMU.js","sourcesContent":["/**\n * Editor SHELL design tokens — the ONE source both editors' chrome converge on.\n *\n * Where `rail/tokens.ts` owns the slim icon-rail, this owns the rest of the\n * editor chrome theme: accent, surfaces, fields, text, lines, chips, named\n * chrome literals, danger/warning/success, radii, gap, shadows and font. Every\n * value bar one is taken verbatim from the storefront editor's `--sf-*` block\n * (`efficient-shop/components/storefront-editor.css`), the visual reference the\n * campaign designer (`--cd-*`) and the storefront's own TS mirror (`tk`) had\n * each re-copied. After card 66024 they all resolve to these values instead.\n * The one exception is `success`, which that block has no counterpart for; see\n * its own note below.\n *\n * Exposed BOTH ways, per the same value (mirrors the rail):\n * - `shellTokens` — this JS object, for code that needs the numbers/colours\n * (e.g. the storefront's `floating-editor/tokens.ts` inline-style mirror);\n * - `shell.css` — the stylesheet, which declares the identical values as CSS\n * custom properties on `.es-editor` (names listed in `shellCssVars`).\n *\n * Numeric fields are pixel magnitudes (unitless) so they compose in code; the\n * CSS applies the `px`. Both editing surfaces are LIGHT only (no dark mode), so\n * there is a single palette here — no dark counterpart.\n */\nexport const shellTokens = {\n // ── Accent (react-os-shell blue) ──────────────────────────────────────────\n /** `--es-accent` — blue-600. */\n accent: '#2563eb',\n /** `--es-accent-tint` — blue-50. */\n accentTint: '#eff6ff',\n /** `--es-accent-tint-strong` — blue-100. */\n accentTintStrong: '#dbeafe',\n /** `--es-accent-text` — blue-700. */\n accentText: '#1d4ed8',\n /** `--es-accent-ring` — focus ring wash. */\n accentRing: 'rgba(37, 99, 235, 0.25)',\n /** `--es-accent-shadow` — accent button shadow. */\n accentShadow: 'rgba(37, 99, 235, 0.35)',\n /** `--es-on-accent` — text/icon over the solid accent. */\n onAccent: '#ffffff',\n\n // ── Surfaces (cool gray ramp) ─────────────────────────────────────────────\n /** `--es-app-bg` — gray-50, sunken canvas. */\n appBg: '#f9fafb',\n /** `--es-preview-bg` — gray-100, preview well. */\n previewBg: '#f3f4f6',\n /** `--es-panel` — raised cards / panels. */\n panel: '#ffffff',\n /** `--es-raised` — a lifted surface. NOT the active chip since card 66302. */\n raised: '#ffffff',\n /** `--es-topbar-bg`. */\n topbarBg: '#ffffff',\n /** `--es-frame-bg` — device \"paper\" behind the preview iframe. */\n frameBg: '#ffffff',\n /** `--es-hover` — gray-100 row / icon hover. */\n hover: '#f3f4f6',\n /** `--es-hatch` — 45° upload-slot texture. */\n hatch: 'rgba(0, 0, 0, 0.018)',\n /** `--es-addcard-veil` — add-section card hover wash. */\n addcardVeil: 'rgba(248, 249, 250, 0.66)',\n\n // ── Fields ────────────────────────────────────────────────────────────────\n /** `--es-field-bg`. */\n fieldBg: '#ffffff',\n /** `--es-track-bg` — gray-100 segmented / search track. */\n trackBg: '#f3f4f6',\n /** `--es-input-border` — gray-300. */\n inputBorder: '#d1d5db',\n /** `--es-field-text` — gray-800. */\n fieldText: '#1f2937',\n /** `--es-field-placeholder` — gray-400. */\n fieldPlaceholder: '#9ca3af',\n /** `--es-knob` — toggle knob. */\n knob: '#ffffff',\n\n // ── Text ────────────────────────────────────────────────────────────────\n /** `--es-text` — gray-900. */\n text: '#111827',\n /** `--es-text-strong` — gray-950 active labels. */\n textStrong: '#030712',\n /** `--es-text-secondary` — gray-700. */\n textSecondary: '#374151',\n /** `--es-muted` — gray-500. */\n muted: '#6b7280',\n /** `--es-icon` — gray-500 icon buttons. */\n icon: '#6b7280',\n /** `--es-disabled` — gray-400 disabled / faint glyphs. */\n disabled: '#9ca3af',\n\n // ── Lines / chips / buttons ──────────────────────────────────────────────\n /** `--es-divider` — gray-200. */\n divider: '#e5e7eb',\n /** `--es-line` — gray-200 hairlines. */\n line: '#e5e7eb',\n /** `--es-btn-border` — gray-300. */\n btnBorder: '#d1d5db',\n /** `--es-btn-text` — gray-700. */\n btnText: '#374151',\n /** `--es-chip-bg` — gray-100. */\n chipBg: '#f3f4f6',\n /** `--es-chip-fg` — gray-500. */\n chipFg: '#6b7280',\n\n // ── Named chrome literals ─────────────────────────────────────────────────\n /** `--es-brand-square` — gray-900 wordmark square. */\n brandSquare: '#111827',\n /** `--es-device-border` — gray-200 tablet/mobile frame. */\n deviceBorder: '#e5e7eb',\n /** `--es-toast-bg` — gray-900 dark chip / seam pill. */\n toastBg: '#111827',\n /** `--es-toggle-off` — gray-300 toggle track. */\n toggleOff: '#d1d5db',\n /** `--es-scrollbar` — gray-300. */\n scrollbar: '#d1d5db',\n /** `--es-scrollbar-hover` — gray-400. */\n scrollbarHover: '#9ca3af',\n\n // ── Danger / warning / success (shell red / amber / green) ────────────────\n /** `--es-danger` — red-600. */\n danger: '#dc2626',\n /** `--es-danger-strong` — red-700. */\n dangerStrong: '#b91c1c',\n /** `--es-danger-tint` — red-50. */\n dangerTint: '#fef2f2',\n /** `--es-warning` — amber-700. */\n warning: '#b45309',\n /** `--es-preview-tint` — amber-100 \"Preview\" pill. */\n previewTint: '#fef3c7',\n /**\n * `--es-success` — green-700. THE ONE VALUE HERE WITH NO `--sf-*` BEHIND IT:\n * the storefront editor's block has a red and an amber and no green, and card\n * 8802.g needs one for a green tick. So it is picked to sit with the block\n * rather than invented next to it — the same Tailwind ramp every other colour\n * in this file comes from, at the 700 step `--es-warning` already sits on.\n * 700 and not 600 for the reason amber went there: it is the darkest step\n * still plainly the hue, and it clears WCAG AA (5.0:1) on the shell's white\n * panels, so a tick may carry a label without needing a second token.\n */\n success: '#15803d',\n\n // ── Radii / shape / gap ───────────────────────────────────────────────────\n /** `--es-panel-radius`. */\n panelRadius: 12,\n /** `--es-row-radius`. */\n rowRadius: 8,\n /** `--es-field-radius`. */\n fieldRadius: 6,\n /** `--es-panel-gap` — inter-pane gutter + outer frame padding, ONE token. */\n panelGap: 10,\n\n // ── Shadows ───────────────────────────────────────────────────────────────\n /** `--es-panel-shadow`. */\n panelShadow: '0 1px 2px rgba(0, 0, 0, 0.05), 0 10px 30px rgba(0, 0, 0, 0.06)',\n /** `--es-topbar-shadow`. */\n topbarShadow: '0 1px 3px rgba(0, 0, 0, 0.06)',\n /**\n * `--es-raised-shadow` — what a small surface sitting ON another one casts:\n * the chip in the settings tab strip's track (card 66138). Tighter and darker\n * than `panelShadow`, which is a whole panel floating over the app; this one\n * has to read across 3px of track without looking like a second panel.\n */\n raisedShadow: '0 1px 2px rgba(16, 24, 40, 0.1)',\n\n // ── Type ──────────────────────────────────────────────────────────────────\n /** `--es-font` — system sans stack (also fed to `--puck-font-family`). */\n font: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, sans-serif',\n} as const;\n\nexport type ShellTokens = typeof shellTokens;\n\n/**\n * The CSS custom-property name behind each `shellTokens` key. `shell.css` sets\n * these on `.es-editor`; override any of them on (or above) the editor root to\n * re-theme without shipping new CSS. The `shell-tokens.test.ts` parity gate\n * proves this map, `shellTokens` and `shell.css` never drift apart.\n */\nexport const shellCssVars: Record<keyof ShellTokens, string> = {\n accent: '--es-accent',\n accentTint: '--es-accent-tint',\n accentTintStrong: '--es-accent-tint-strong',\n accentText: '--es-accent-text',\n accentRing: '--es-accent-ring',\n accentShadow: '--es-accent-shadow',\n onAccent: '--es-on-accent',\n appBg: '--es-app-bg',\n previewBg: '--es-preview-bg',\n panel: '--es-panel',\n raised: '--es-raised',\n topbarBg: '--es-topbar-bg',\n frameBg: '--es-frame-bg',\n hover: '--es-hover',\n hatch: '--es-hatch',\n addcardVeil: '--es-addcard-veil',\n fieldBg: '--es-field-bg',\n trackBg: '--es-track-bg',\n inputBorder: '--es-input-border',\n fieldText: '--es-field-text',\n fieldPlaceholder: '--es-field-placeholder',\n knob: '--es-knob',\n text: '--es-text',\n textStrong: '--es-text-strong',\n textSecondary: '--es-text-secondary',\n muted: '--es-muted',\n icon: '--es-icon',\n disabled: '--es-disabled',\n divider: '--es-divider',\n line: '--es-line',\n btnBorder: '--es-btn-border',\n btnText: '--es-btn-text',\n chipBg: '--es-chip-bg',\n chipFg: '--es-chip-fg',\n brandSquare: '--es-brand-square',\n deviceBorder: '--es-device-border',\n toastBg: '--es-toast-bg',\n toggleOff: '--es-toggle-off',\n scrollbar: '--es-scrollbar',\n scrollbarHover: '--es-scrollbar-hover',\n danger: '--es-danger',\n dangerStrong: '--es-danger-strong',\n dangerTint: '--es-danger-tint',\n warning: '--es-warning',\n previewTint: '--es-preview-tint',\n success: '--es-success',\n panelRadius: '--es-panel-radius',\n rowRadius: '--es-row-radius',\n fieldRadius: '--es-field-radius',\n panelGap: '--es-panel-gap',\n panelShadow: '--es-panel-shadow',\n topbarShadow: '--es-topbar-shadow',\n raisedShadow: '--es-raised-shadow',\n font: '--es-font',\n};\n\n/**\n * Puck's own accent ramp + font, re-tinted to the shell blue. Puck reads these\n * fixed variable names (`--puck-color-azure-NN`, `--puck-font-family`) directly,\n * so they are NOT part of the `--es-*` set — but they had been copied verbatim\n * into BOTH editors' CSS, so `shell.css` now declares them once on `.es-editor`.\n * Kept here as the JS record so the parity gate can prove `shell.css` matches.\n */\nexport const puckAzureRamp: Record<string, string> = {\n '--puck-color-azure-01': '#172554',\n '--puck-color-azure-02': '#1e3a8a',\n '--puck-color-azure-03': '#1d4ed8',\n '--puck-color-azure-04': '#2563eb',\n '--puck-color-azure-05': '#3b82f6',\n '--puck-color-azure-06': '#60a5fa',\n '--puck-color-azure-07': '#93c5fd',\n '--puck-color-azure-08': '#bfdbfe',\n '--puck-color-azure-09': '#dbeafe',\n '--puck-color-azure-10': '#eff6ff',\n '--puck-color-azure-11': '#f5f9ff',\n '--puck-color-azure-12': '#fafcff',\n};\n"]}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { EditorRail, EditorRailButton, railCssVars, railTokens } from './chunk-HYOACICE.js';
|
|
2
2
|
export { AddIcon, LayersIcon, MediaIcon, PageIcon as PagesIcon, SitemapIcon, StylesIcon } from './chunk-V7JPZX2L.js';
|
|
3
|
-
export { puckAzureRamp, shellCssVars, shellTokens } from './chunk-
|
|
3
|
+
export { puckAzureRamp, shellCssVars, shellTokens } from './chunk-VEXTYAMU.js';
|
|
4
4
|
//# sourceMappingURL=index.js.map
|
|
5
5
|
//# sourceMappingURL=index.js.map
|
package/dist/panel/index.d.ts
CHANGED
|
@@ -122,6 +122,71 @@ interface SettingsSegmentedFit {
|
|
|
122
122
|
* not the words Normal / Uppercase / Lowercase that name them. Passing `title`
|
|
123
123
|
* beside a WORDED label is allowed and overrides the word, which is what a
|
|
124
124
|
* strip whose words are abbreviations wants.
|
|
125
|
+
*
|
|
126
|
+
* ── NO CONSUMER TODAY, AND CARRIED ON PURPOSE ────────────────────────────────
|
|
127
|
+
*
|
|
128
|
+
* Written down so the next reader does not find this unused and tidy it away.
|
|
129
|
+
* The rows above stopped needing it while this was being built: card 66246
|
|
130
|
+
* (`efficient-shop` 151afa212, merged 2026-08-31) moved the part panel's three
|
|
131
|
+
* tables onto `icon` + a real word — `{ id: "left", label: "Left", icon:
|
|
132
|
+
* <AlignLeftIcon/> }` — on the owner's ruling that an option says its name AND
|
|
133
|
+
* draws its glyph. Every `label` in the shop is now a string, and this arm has
|
|
134
|
+
* no caller anywhere. That is measured on merged main, not on a pending PR.
|
|
135
|
+
*
|
|
136
|
+
* It stays because it is the shape THE PACKAGE NEXT DOOR ALREADY USES.
|
|
137
|
+
* `react-os-shell` ships its own `Segmented`, and its `SegmentedOption` has
|
|
138
|
+
* carried `label: ReactNode` all along. Identical at the two copies installed on
|
|
139
|
+
* this desk — 4.63.0 under `editor-shell`, 4.67.0 under `efficient-shop`:
|
|
140
|
+
*
|
|
141
|
+
* interface SegmentedOption<T extends string = string> {
|
|
142
|
+
* value: T; label: ReactNode; icon?: ReactNode; disabled?: boolean;
|
|
143
|
+
* }
|
|
144
|
+
*
|
|
145
|
+
* A shared control that refuses a shape its sibling accepts is a fork one level
|
|
146
|
+
* down, which is the fault this whole family of cards exists to stop. Matching
|
|
147
|
+
* it costs nothing while nothing calls it.
|
|
148
|
+
*
|
|
149
|
+
* ── WHERE THE TWO SHELLS CONVERGE, AND WHERE THEY DO NOT ─────────────────────
|
|
150
|
+
*
|
|
151
|
+
* Read this before repeating "the two shells are converged", because it is only
|
|
152
|
+
* true of three fields out of four, and the fourth is the one a caller touches
|
|
153
|
+
* most:
|
|
154
|
+
*
|
|
155
|
+
* react-os-shell editor-shell
|
|
156
|
+
* the label label: ReactNode label: ReactNode converged
|
|
157
|
+
* the glyph icon?: ReactNode icon?: ReactNode converged
|
|
158
|
+
* out of force disabled?: boolean disabled?: boolean converged
|
|
159
|
+
* ─────────────────────────────────────────────────────────────────────────
|
|
160
|
+
* the VALUE value: T id: V NOT converged
|
|
161
|
+
* T extends string V unconstrained
|
|
162
|
+
*
|
|
163
|
+
* BOTH halves of that last row are deliberate.
|
|
164
|
+
*
|
|
165
|
+
* `id` and not `value`, because this field has been called `id` since the type
|
|
166
|
+
* existed and renaming it would move something for every caller already using
|
|
167
|
+
* it — which is the one thing card 66303 was not allowed to do.
|
|
168
|
+
*
|
|
169
|
+
* `V` UNCONSTRAINED and not `T extends string`, because the shop stores a
|
|
170
|
+
* choice as a boolean: `reverse` at `efficient-shop/lib/puck/config.tsx:6747` is
|
|
171
|
+
* `false` = image left and `true` = image right on every published page across
|
|
172
|
+
* every tenant. `T extends string` cannot hold it, and giving that row the house
|
|
173
|
+
* strip by storing "left"/"right" would be a migration of live customer data
|
|
174
|
+
* rather than a control change. So on the value type this package is the WIDER
|
|
175
|
+
* of the two, not the poorer — the opposite of the direction the rest of this
|
|
176
|
+
* note runs.
|
|
177
|
+
*
|
|
178
|
+
* ── AND WHERE WE DELIBERATELY GO FURTHER ─────────────────────────────────────
|
|
179
|
+
*
|
|
180
|
+
* That sibling declaration has NO name field beside its node label. A drawing
|
|
181
|
+
* that cannot name itself is exactly the defect the 2026-08-06 audit closed on
|
|
182
|
+
* the shop's strip, so the `title` here is REQUIRED — a guarantee, not a
|
|
183
|
+
* feature. The union makes a nameless drawing unconstructable rather than a
|
|
184
|
+
* documented risk; the sibling's shape permits one.
|
|
185
|
+
*
|
|
186
|
+
* One more thing that declaration settles, recorded because it was argued: its
|
|
187
|
+
* `disabled` is PER OPTION and it has no strip-level one at all. That is the
|
|
188
|
+
* independent support for the per-option field on
|
|
189
|
+
* `SettingsSegmentedOptionBase` — the sibling has shipped exactly that.
|
|
125
190
|
*/
|
|
126
191
|
type SettingsSegmentedLabel = {
|
|
127
192
|
label: string;
|
|
@@ -283,6 +348,19 @@ interface SettingsGroupHeadProps {
|
|
|
283
348
|
id?: string;
|
|
284
349
|
className?: string;
|
|
285
350
|
}
|
|
351
|
+
interface SettingsGroupSubProps {
|
|
352
|
+
/** The heading's name — "Content", "Background", "Image". */
|
|
353
|
+
title: string;
|
|
354
|
+
/**
|
|
355
|
+
* Which heading level this is in the HOST's outline. Defaults to 4 — one under
|
|
356
|
+
* `SettingsGroupHead`'s 3, because this tier sits under that pill. Only the
|
|
357
|
+
* host knows what is actually above it.
|
|
358
|
+
*/
|
|
359
|
+
level?: 2 | 3 | 4 | 5 | 6;
|
|
360
|
+
/** A DOM id, so the rows it heads can point `aria-labelledby` at it. */
|
|
361
|
+
id?: string;
|
|
362
|
+
className?: string;
|
|
363
|
+
}
|
|
286
364
|
interface SettingsFieldProps {
|
|
287
365
|
label: string;
|
|
288
366
|
/**
|
|
@@ -563,6 +641,49 @@ declare function SettingsGroup({ title, open, onToggle, count, id, children, cla
|
|
|
563
641
|
*/
|
|
564
642
|
declare function SettingsGroupHead({ title, level, id, className, }: SettingsGroupHeadProps): react.JSX.Element;
|
|
565
643
|
|
|
644
|
+
/**
|
|
645
|
+
* The QUIET second tier — a heading one rung under `SettingsGroupHead`.
|
|
646
|
+
*
|
|
647
|
+
* It exists because a panel sometimes has two levels of heading and this package
|
|
648
|
+
* only ever drew one. `SettingsGroupHead` is a filled pill, and a pill cannot
|
|
649
|
+
* serve a tier AND its subordinate: two runs of settings under one pill each
|
|
650
|
+
* need a name, and giving them the same pill says they are siblings of the thing
|
|
651
|
+
* above them rather than parts of it.
|
|
652
|
+
*
|
|
653
|
+
* THE SHOP ALREADY DREW THIS LOOK, TWICE, WHICH IS WHY IT BELONGS HERE.
|
|
654
|
+
* `efficient-shop` hand-rolled `.sf-field-group` in the settings panel and
|
|
655
|
+
* `.sf-group-label` in the Layers tree, and they are the same type to the
|
|
656
|
+
* decimal — 10.5px / 600 / .07em / muted / uppercase. One look, hand-rolled in
|
|
657
|
+
* two panels of one editor, is the definition of a thing the shell should own.
|
|
658
|
+
*
|
|
659
|
+
* ⚠️ NOT THE PILL WITH ITS FILL SWITCHED OFF, and that is the whole reason this
|
|
660
|
+
* is a component and not a `variant` flag on the other one. The shop's own
|
|
661
|
+
* `WHY NOT SettingsGroupHead` docblock refused that shape in advance: a named
|
|
662
|
+
* look the shell draws, "not a boolean that switches the pill off. A flag whose
|
|
663
|
+
* job is to turn the design off moves the fork inside the shell instead of
|
|
664
|
+
* removing it." So there is no path from here to the pill's look and none back —
|
|
665
|
+
* two tiers, two looks, each one this package's own.
|
|
666
|
+
*
|
|
667
|
+
* WHY IT IS QUIET BY TYPE AND THE PILL IS LOUD BY GROUND. The pill separates
|
|
668
|
+
* itself from the settings around it with a fill; this separates itself with
|
|
669
|
+
* CASE and COLOUR and takes no ground at all. That is what lets the two sit in
|
|
670
|
+
* one panel without competing, and it is also why this tier can carry no rule:
|
|
671
|
+
* a hairline under a heading separates it from the rows it heads, which is the
|
|
672
|
+
* opposite of what a heading is for. Removing those rules is the change the
|
|
673
|
+
* owner asked for.
|
|
674
|
+
*
|
|
675
|
+
* A REAL HEADING, defaulting to `h4` — one under the pill's default `h3`,
|
|
676
|
+
* because that is where a subordinate heading sits when the pill above it took
|
|
677
|
+
* the 3. The host may say otherwise; only the host knows the outline around it.
|
|
678
|
+
* Puck lays an inspector out as a FLAT sibling list, so the heading list is the
|
|
679
|
+
* only structure a screen reader has left to navigate by.
|
|
680
|
+
*
|
|
681
|
+
* NO fold, no count, no sign, no body — the same discipline as
|
|
682
|
+
* {@link SettingsGroupHead}, and for the same reason: a component that folds and
|
|
683
|
+
* a component that titles are two different things, and this is one that titles.
|
|
684
|
+
*/
|
|
685
|
+
declare function SettingsGroupSub({ title, level, id, className, }: SettingsGroupSubProps): react.JSX.Element;
|
|
686
|
+
|
|
566
687
|
/**
|
|
567
688
|
* One setting: a label row, then its control.
|
|
568
689
|
*
|
|
@@ -644,4 +765,4 @@ declare function SettingsSwitch({ checked, onChange, disabled, id, ariaLabel, ar
|
|
|
644
765
|
*/
|
|
645
766
|
declare function SettingsDrill({ name, preview, offPage, status, onOpen, id, className, }: SettingsDrillProps): react.JSX.Element;
|
|
646
767
|
|
|
647
|
-
export { SettingsDrill, type SettingsDrillProps, SettingsField, type SettingsFieldProps, SettingsGroup, SettingsGroupHead, type SettingsGroupHeadProps, type SettingsGroupProps, SettingsSegmented, type SettingsSegmentedFit, type SettingsSegmentedOption, type SettingsSegmentedProps, SettingsSwitch, type SettingsSwitchBaseProps, type SettingsSwitchLabel, type SettingsSwitchProps, type SettingsTabItem, SettingsTabs, type SettingsTabsProps };
|
|
768
|
+
export { SettingsDrill, type SettingsDrillProps, SettingsField, type SettingsFieldProps, SettingsGroup, SettingsGroupHead, type SettingsGroupHeadProps, type SettingsGroupProps, SettingsGroupSub, type SettingsGroupSubProps, SettingsSegmented, type SettingsSegmentedFit, type SettingsSegmentedOption, type SettingsSegmentedProps, SettingsSwitch, type SettingsSwitchBaseProps, type SettingsSwitchLabel, type SettingsSwitchProps, type SettingsTabItem, SettingsTabs, type SettingsTabsProps };
|
package/dist/panel/index.js
CHANGED
|
@@ -138,6 +138,15 @@ function SettingsGroupHead({
|
|
|
138
138
|
const Tag = `h${level}`;
|
|
139
139
|
return /* @__PURE__ */ jsx(Tag, { id, className: className ? `es-group-head ${className}` : "es-group-head", children: title });
|
|
140
140
|
}
|
|
141
|
+
function SettingsGroupSub({
|
|
142
|
+
title,
|
|
143
|
+
level = 4,
|
|
144
|
+
id,
|
|
145
|
+
className
|
|
146
|
+
}) {
|
|
147
|
+
const Tag = `h${level}`;
|
|
148
|
+
return /* @__PURE__ */ jsx(Tag, { id, className: className ? `es-group-sub ${className}` : "es-group-sub", children: title });
|
|
149
|
+
}
|
|
141
150
|
function SettingsSwitch({
|
|
142
151
|
checked,
|
|
143
152
|
onChange,
|
|
@@ -237,6 +246,6 @@ function SettingsDrill({
|
|
|
237
246
|
);
|
|
238
247
|
}
|
|
239
248
|
|
|
240
|
-
export { SettingsDrill, SettingsField, SettingsGroup, SettingsGroupHead, SettingsSegmented, SettingsSwitch, SettingsTabs };
|
|
249
|
+
export { SettingsDrill, SettingsField, SettingsGroup, SettingsGroupHead, SettingsGroupSub, SettingsSegmented, SettingsSwitch, SettingsTabs };
|
|
241
250
|
//# sourceMappingURL=index.js.map
|
|
242
251
|
//# sourceMappingURL=index.js.map
|
package/dist/panel/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/panel/SettingsTabs.tsx","../../src/panel/SettingsSegmented.tsx","../../src/panel/SettingsGroup.tsx","../../src/panel/SettingsGroupHead.tsx","../../src/panel/SettingsSwitch.tsx","../../src/panel/SettingsField.tsx","../../src/panel/SettingsDrill.tsx"],"names":["jsx","jsxs"],"mappings":"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","file":"index.js","sourcesContent":["import type { SettingsTabsProps } from './types';\n\n/**\n * The tab strip under the panel head.\n *\n * It replaces the old stack of fold headers a merchant had to open and hunt\n * through: the buckets a section actually has become tabs, in one fixed order,\n * so the panel keeps the same shape on every section. A section shows only the\n * tabs it has — pass three items and three tabs render.\n *\n * Controlled: the host decides `activeId`. No hooks, no browser globals.\n *\n * A tab can be WIRED to what it shows (`panelId` / `htmlId` on the item). That\n * is not decoration: `role=\"tab\"` promises a panel, and a tab strip that names\n * none announces \"tab, 1 of 4, selected\" over nothing. The shell cannot supply\n * those ids — only the host knows the element the settings land in — but until\n * card 8802.d it could not ACCEPT them either, so no host could fix it. A tab\n * with nothing to point at renders no attribute at all; an empty\n * `aria-controls` is worse than a missing one, because it names an element\n * that does not exist instead of admitting there is none.\n *\n * A SEGMENTED CONTROL (card 66138). The strip is a grey track and the chosen\n * bucket is a raised white chip on it. Lewis, looking at a real panel: the tab\n * he is NOT on does not invite a click — it does not even look like a button.\n * The track is the answer to that, because it makes every segment look\n * pressable, where a mark under the chosen tab only ever made the chosen tab\n * louder. It replaces the underline strip and card 8811's tint ground; see the\n * long note on `.es-tab.is-active` in `panel.css`.\n *\n * A segment takes the width its OWN label needs and shares what is left over,\n * rather than every segment taking the width of the widest label. Equal\n * segments were measured and cost too much: three of them need a 267px track\n * and four need 355px, against the 281px the shop's default panel gives.\n *\n * THE WORD DROPS WHERE IT STOPS FITTING. Natural widths fit two and three\n * buckets across the whole panel-drag band; they do not fit four, where the\n * last segment ends 39px past the panel edge. So given an `icon`, a segment can\n * spend less width instead of more — the word goes and the glyph stays.\n *\n * Where that happens depends on HOW MANY buckets a section has, because with\n * natural widths the question is about the sum of the labels:\n *\n * two buckets both words, at every width the panel drags to.\n * three buckets words down to 240px, then glyph plus the chosen word.\n * four buckets glyph plus the chosen word under 320px; glyphs under 200px.\n *\n * Which state is a CSS decision, taken against the PANEL's width — see the\n * `@container` blocks in `panel.css`, and the wrapper below that gives them\n * something to measure. React cannot know the answer at render time, and it\n * does not need to: BOTH parts are always in the markup, and CSS shows the one\n * that fits. The word is only ever hidden, never dropped, and the label rides\n * along as `title` and `aria-label` on every tab in every state, so the glyph\n * never has to speak for the tab.\n *\n * A tab with no icon is untouched at every width — it has nothing to fall back\n * to, so it keeps its word.\n */\nexport function SettingsTabs({\n items,\n activeId,\n onSelect,\n ariaLabel = 'Settings',\n className,\n}: SettingsTabsProps) {\n return (\n // The container context is the SHELL's, not a host's to remember. A panel\n // that forgot to declare one would silently pin the strip to one tier.\n <div className=\"es-tabs-fit\">\n <div\n role=\"tablist\"\n aria-label={ariaLabel}\n aria-orientation=\"horizontal\"\n className={className ? `es-tabs ${className}` : 'es-tabs'}\n >\n {items.map((item) => {\n const active = item.id === activeId;\n const cls = `es-tab${item.icon ? ' has-icon' : ''}${active ? ' is-active' : ''}`;\n return (\n <button\n key={item.id}\n type=\"button\"\n role=\"tab\"\n id={item.htmlId}\n title={item.label}\n aria-label={item.label}\n aria-selected={active}\n aria-controls={item.panelId}\n className={cls}\n onClick={() => onSelect(item.id)}\n >\n {item.icon ? (\n <span className=\"es-tab-icon\" aria-hidden=\"true\">\n {item.icon}\n </span>\n ) : null}\n <span className=\"es-tab-word\">{item.label}</span>\n {typeof item.badge === 'number' && item.badge > 0 ? (\n <span className=\"es-tab-badge\">{item.badge}</span>\n ) : null}\n </button>\n );\n })}\n </div>\n </div>\n );\n}\n","import type { CSSProperties } from 'react';\nimport type { SettingsSegmentedProps } from './types';\n\n/**\n * A segmented control: one row of chips, one of them pressed.\n *\n * THE SAME CHIP AS `SettingsTabs`, AND A DIFFERENT PROMISE. Everything a\n * merchant sees is shared — the grey track, the raised white chip for the one\n * he is on, the glyph drawn at every width, the word that drops where it stops\n * fitting. `panel.css` dresses both from one rule, so there is no second copy\n * of the chip grammar to drift.\n *\n * What differs is what the control SAYS it does. `SettingsTabs` is a tablist\n * and `role=\"tab\"` promises that pressing a chip switches a panel. These chips\n * switch nothing: they write a value and the panel stays where it is. So this\n * is a group of pressed buttons, and its marker is `aria-pressed` — which is\n * also what the storefront's own `Segmented` has carried since the 2026-08-06\n * audit, so the two editors keep saying the same thing about the same control.\n *\n * That distinction is not decoration. Reach for `SettingsTabs` on a Weight row\n * and a control that is correct today starts announcing \"tab, 1 of 4, selected\"\n * over a panel that is not there — three times in one part panel. It is the\n * same split the package already made between `SettingsGroup` and\n * `SettingsGroupHead`: same pill, different promise, two components.\n *\n * Controlled: the host owns `value`. No hooks, no browser globals.\n *\n * ── WHERE THE WORD DROPS, AND WHY THE STRIP HAS TO SAY ───────────────────────\n *\n * The tab strip's tiers are keyed on how many tabs there are, which works\n * because the shell knows those four words. It cannot work here. Measured\n * (`tests/fixtures/segmented-fit.html`), Case — Normal / Uppercase / Lowercase\n * — needs 296px for its words and Alignment — Left / Centre / Right — needs\n * 221px. Both are three-option rows, and the panel drags between 181px and\n * 281px, so a single three-option threshold is visibly wrong for one of them.\n * Whether the words fit is a question about the SUM of the labels; the count is\n * only a stand-in, and it stops standing in the moment a second strip arrives.\n *\n * So the strip carries its own measurement. `fit` is the width of its WORDS,\n * nothing else — the chip's padding, glyph and gaps are the shell's, named on\n * the panel root, and `panel.css` adds them. Those three custom properties\n * below are that measurement and the count, handed to the stylesheet, which\n * compares them against the panel's live width in `calc()`. A container query\n * could not: its condition takes a literal length, so it cannot ask a question\n * whose answer is different for every strip.\n *\n * Without `fit` nothing drops — an unmeasured strip keeps every word at every\n * width, which is exactly what a strip with no glyphs already does.\n *\n * ── WHAT CARD 66303 WIDENED, AND WHY IT HAD TO BE HERE ───────────────────────\n *\n * `efficient-shop` draws its own `Segmented` beside this one and could not stop:\n * four things twelve live call sites need were missing here. Generic `V`\n * (weights are numbers, \"image side\" is a boolean on every published page), a\n * DRAWN label with its own `title`, and `disabled` — on one option or on the\n * strip.\n *\n * The last two could not have been bridged by a wrapper the shop writes, which\n * is the reason they are props and not a consumer's problem. This component\n * renders the `<button>`s, so nothing outside it can disable them; dimming a\n * parent instead leaves them focusable and announcing as ENABLED, which is the\n * regression the 2026-08-06 audit and card 66034 closed. And `label` is spent as\n * `title` and `aria-label` as well as the visible mark, so a node in it reaches\n * both attributes as `[object Object]` — while splitting it into `icon` +\n * `label` renders BOTH spans, and Alignment would draw the rule and the word\n * \"Left\" beside it.\n */\nexport function SettingsSegmented<V = string>({\n options,\n value,\n onChange,\n ariaLabel,\n fit,\n disabled = false,\n className,\n}: SettingsSegmentedProps<V>) {\n // The measurement, handed to the stylesheet. `is-measured` is what the sheet\n // scopes the threshold arithmetic to, so a strip that declared nothing falls\n // back to no threshold at all rather than to a computed one over zeroes.\n const measured: CSSProperties | undefined = fit && {\n ['--es-seg-count' as string]: options.length,\n ['--es-seg-labels' as string]: `${fit.labelsPx}px`,\n ['--es-seg-widest' as string]: `${fit.widestLabelPx}px`,\n };\n\n return (\n // The container context is the SHELL's, not a host's to remember — a panel\n // that forgot to declare one would silently pin the strip to one state.\n <div className=\"es-segs-fit\">\n <div\n role=\"group\"\n aria-label={ariaLabel}\n // A state ATTRIBUTE rather than a second class, so the class string the\n // strip has always rendered is untouched and the sheet's one statement\n // about a whole control out of force has somewhere of its own to hang.\n data-disabled={disabled || undefined}\n className={`es-segs${measured ? ' is-measured' : ''}${className ? ` ${className}` : ''}`}\n style={measured}\n >\n {options.map((option) => {\n // AN EQUALITY, never a truthiness test. `false` is a real answer —\n // \"image left\" is stored as `false` on every published page across\n // every tenant — and a value matching nothing presses nothing, which\n // is the mixed selection the part panel hands in as `\"\"`.\n const pressed = option.id === value;\n // THE NAME, and every option has one. A drawn label has no word to be\n // read aloud, so its `title` is required and is the name; a worded one\n // is named by its word unless it says otherwise. The narrowing is what\n // keeps a node out of the two attributes it would land in as\n // `[object Object]`.\n const name = option.title ?? (typeof option.label === 'string' ? option.label : undefined);\n const off = disabled || option.disabled === true;\n const cls = `es-seg${option.icon ? ' has-icon' : ''}${pressed ? ' is-active' : ''}`;\n return (\n <button\n // Stringified because `V` is whatever the options declare — a\n // weight is a number. For the string strips that shipped before\n // this, the key is the one they already had.\n key={String(option.id)}\n type=\"button\"\n title={name}\n aria-label={name}\n aria-pressed={pressed}\n // The button's OWN disabled, so it leaves the tab order and\n // announces the state itself. React renders nothing for `false`,\n // so a strip in force is unchanged.\n disabled={off}\n className={cls}\n onClick={() => onChange(option.id)}\n >\n {option.icon ? (\n <span className=\"es-seg-icon\" aria-hidden=\"true\">\n {option.icon}\n </span>\n ) : null}\n {/* The label, drawn as given. A worded one is the word; a drawn\n one is the mark itself, in the SAME slot rather than beside it\n — which is why a mark belongs in `label` and not in `icon`. */}\n <span className=\"es-seg-word\">{option.label}</span>\n </button>\n );\n })}\n </div>\n </div>\n );\n}\n","import type { SettingsGroupProps } from './types';\n\n/**\n * A group of settings under a soft pill row that folds with − / +.\n *\n * The sign is drawn by `panel.css` from `aria-expanded`, so the open state has\n * exactly one home (the attribute assistive tech already reads) instead of a\n * second copy in the markup.\n *\n * `count` is the reason a fold here is safe: a folded group that holds changes\n * still says so on its pill, so nothing a merchant changed can hide.\n */\nexport function SettingsGroup({\n title,\n open,\n onToggle,\n count,\n id,\n children,\n className,\n}: SettingsGroupProps) {\n const bodyId = id ? `${id}-body` : undefined;\n return (\n <div className={className ? `es-group ${className}` : 'es-group'}>\n <button\n type=\"button\"\n id={id}\n aria-expanded={open}\n aria-controls={bodyId}\n className=\"es-group-head\"\n onClick={() => onToggle(!open)}\n >\n {title}\n {typeof count === 'number' && count > 0 ? (\n <span className=\"es-group-count\">{count}</span>\n ) : null}\n <span className=\"es-group-sign\" aria-hidden=\"true\" />\n </button>\n <div id={bodyId} className=\"es-group-body\" hidden={!open}>\n {children}\n </div>\n </div>\n );\n}\n","import type { SettingsGroupHeadProps } from './types';\n\n/**\n * A group heading with NO group under it — the pill on its own.\n *\n * It exists because Puck lays the inspector out as a FLAT sibling list: a field\n * renders next to the heading above it, never inside it, so nothing here can\n * wrap the rows that follow. Heading a run of settings is therefore a job for a\n * component that heads and nothing else.\n *\n * `SettingsGroup` cannot do that job. It always renders its body div, and its\n * pill is always a <button> carrying `aria-expanded`, `aria-controls` and the\n * − / + sign. Used header-only it would draw a control that claims to expand\n * something, does nothing when clicked, and tells a screen reader the same lie.\n * A component that folds and a component that titles are two different things,\n * and this is the one that titles.\n *\n * So it is a HEADING, not a control: a real <h3> (the level is the host's, since\n * only the host knows the page outline around it) with no role bolted on, no\n * expanded state, no sign, no body. In a flat list the heading list is the only\n * structure a screen reader has left to navigate by, which is the whole reason\n * this renders a heading element rather than a styled <div>.\n *\n * It wears `.es-group-head` — the same pill as a real group's — so a section\n * that folds and a section that does not read as the same panel. `panel.css`\n * scopes the cursor and the hover tint to `button.es-group-head`, so the\n * heading does not offer a press it cannot honour.\n *\n * NO `count`. A folded group needs one because a fold can hide a change; a\n * heading folds nothing, so nothing can hide behind it.\n */\nexport function SettingsGroupHead({\n title,\n level = 3,\n id,\n className,\n}: SettingsGroupHeadProps) {\n const Tag = `h${level}` as const;\n return (\n <Tag id={id} className={className ? `es-group-head ${className}` : 'es-group-head'}>\n {title}\n </Tag>\n );\n}\n","import type { SettingsSwitchProps } from './types';\n\n/**\n * The yes-or-no control — ONE shape, both editors (card 8801).\n *\n * Point 5 of the approved settings design says one switch style everywhere.\n * Before this, the storefront editor drew a tick box and the campaign designer\n * drew a green switch, so a merchant met two shapes for the same question and\n * had to learn the control twice. Both now import this file, which is the only\n * thing that stops them drifting apart again.\n *\n * It is a real <button>, and that is the whole keyboard story: a button is\n * activated by Space and by Enter in every browser, and that activation IS a\n * click — so the one `onClick` below serves mouse and keyboard alike. A div\n * wearing `role=\"switch\"` would need a key handler of our own. Do not add one.\n * `type=\"button\"` keeps it from submitting a form it happens to sit in.\n *\n * It cannot exist without a NAME. `SettingsSwitchProps` demands `ariaLabel` or\n * `ariaLabelledBy` and forbids both at once, so an unnamed switch — which a\n * screen reader reads out as \"switch, on\" and nothing more — is a compile error\n * instead of something a docstring asks you to remember.\n *\n * ONE-WAY DEPENDENCY. `SettingsField` imports this module so it can recognise a\n * switch in its `action` slot and hand it the field's label as a name. This\n * module must NEVER import `SettingsField` back — that would close a cycle\n * between two siblings in the same folder. It imports `./types` and nothing\n * else, and `tests/settings-switch.test.tsx` (o) keeps it that way.\n *\n * CONTROLLED and hook-free like the other three: `checked` comes from the host\n * and the switch only ever ASKS to be flipped. That is what keeps this module a\n * directive-free leaf a Next-16 server component can import.\n */\nexport function SettingsSwitch({\n checked,\n onChange,\n disabled,\n id,\n ariaLabel,\n ariaLabelledBy,\n className,\n}: SettingsSwitchProps) {\n return (\n <button\n type=\"button\"\n role=\"switch\"\n id={id}\n aria-checked={checked}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n disabled={disabled}\n className={className ? `es-switch ${className}` : 'es-switch'}\n onClick={() => onChange(!checked)}\n />\n );\n}\n","import { cloneElement, isValidElement } from 'react';\nimport type { ReactElement, ReactNode } from 'react';\n\nimport { SettingsSwitch } from './SettingsSwitch';\nimport type { SettingsFieldProps } from './types';\n\n/**\n * Does this slot hold a control?\n *\n * The rule is ELEMENT-OR-NOTHING, deliberately not a list of falsy values: a\n * list rots, and the next odd value nobody thought of walks straight through.\n * `{flag && <Slider/>}` yields `false`, `{count && <Slider/>}` yields `0` when\n * the count is zero — and React RENDERS that zero, a stray 0 sitting in the\n * panel where a control belongs. A bare string or number in a control slot is a\n * caller mistake in every case any of us can name.\n *\n * Arrays are walked rather than trusted: `Array.isArray([])` is true, so an\n * empty array — or one holding only `false` and `null`, which is what a list of\n * conditional controls collapses to — would otherwise draw the empty row this\n * check exists to prevent.\n */\nfunction hasControlIn(slot: ReactNode): boolean {\n if (Array.isArray(slot)) return slot.some(hasControlIn);\n return isValidElement(slot);\n}\n\n/** What a switch is named by. Read off an unknown element, so it stays loose. */\ninterface SwitchName {\n ariaLabel?: string;\n ariaLabelledBy?: string;\n}\n\n/**\n * Give a switch in the `action` slot the field's own label as its name.\n *\n * A switch carries no text, and `htmlFor` cannot bridge a <label> to a\n * <button>, so in a boolean row nothing connects the visible label to the\n * control unless something does it explicitly. Doing it HERE means the common\n * case is right with the caller saying nothing: the accessible name and the\n * visible name are the same string and cannot drift apart.\n *\n * It only ever fills a gap. A switch that names itself keeps its own name, and\n * anything that is not a switch — a reset dot, a badge — is left alone, since\n * labelling those with the field's name would be worse than not labelling them.\n */\nfunction nameSwitch(action: ReactNode, label: string, labelId?: string): ReactNode {\n if (!isValidElement(action) || action.type !== SettingsSwitch) return action;\n\n const named = action.props as SwitchName;\n if (named.ariaLabel || named.ariaLabelledBy) return action;\n\n return cloneElement(\n action as ReactElement<SwitchName>,\n labelId ? { ariaLabelledBy: labelId } : { ariaLabel: label },\n );\n}\n\n/**\n * One setting: a label row, then its control.\n *\n * The row is stacked rather than side-by-side so the control gets the panel's\n * full width — a 308px panel cannot afford a label column AND a usable slider.\n * The label reads small and quiet; the VALUE sits at the end of the same line in\n * the text colour, which is what a merchant scans for. That contrast is the\n * point: before this, label and value looked alike and the panel read as noise.\n *\n * `hint` is where a clause-long explanation goes. A label is a name, never a\n * sentence.\n *\n * A BOOLEAN row is the one exception to \"control on its own line\" (card 8801,\n * Option A, approved). A switch is small and it is the whole control, so it\n * rides the label line in the `action` slot and the row costs one line instead\n * of two — which is what lets a 308px panel show four settings where it used to\n * show three. That is the only reason `children` is optional: a field with no\n * children draws no control row, and its label line IS the row.\n *\n * A field with no children AND no action has no control on either slot. That is\n * a mistake at the call site, so it renders NOTHING — an empty row would hide\n * the mistake behind 20px of blank panel.\n */\nexport function SettingsField({\n label,\n labelId,\n icon,\n value,\n hint,\n changed,\n htmlFor,\n action,\n children,\n className,\n}: SettingsFieldProps) {\n const hasControl = hasControlIn(children);\n const hasAction = hasControlIn(action);\n if (!hasControl && !hasAction) return null;\n\n const classes = ['es-field'];\n if (!hasControl) classes.push('is-inline');\n if (changed) classes.push('is-changed');\n if (className) classes.push(className);\n\n const labelBody = (\n <>\n {icon ? (\n <span className=\"es-field-icon\" aria-hidden=\"true\">\n {icon}\n </span>\n ) : null}\n <span className=\"es-field-label\" id={labelId}>\n {label}\n </span>\n </>\n );\n\n return (\n <div className={classes.join(' ')}>\n <div className=\"es-field-head\">\n {htmlFor ? (\n <label className=\"es-field-name\" htmlFor={htmlFor}>\n {labelBody}\n </label>\n ) : (\n <span className=\"es-field-name\">{labelBody}</span>\n )}\n {value !== undefined && value !== null ? (\n <span className=\"es-field-value\">{value}</span>\n ) : null}\n {hasAction ? nameSwitch(action, label, labelId) : null}\n </div>\n {hasControl ? <div className=\"es-field-ctl\">{children}</div> : null}\n {hint ? <p className=\"es-field-hint\">{hint}</p> : null}\n </div>\n );\n}\n","import type { SettingsDrillProps } from './types';\n\n/**\n * A DRILL-IN ROW — a name, a one-line preview, and a chevron (card 8819).\n *\n * The storefront editor's text editing becomes a panel drill-down: the section\n * screen no longer holds a text box for every part; it LISTS the parts as these\n * rows, and pressing one takes the inspector down a level to that part's own\n * editor. A part the eye hid — or one that never draws on the page at all, like\n * a `Slide name` — has no words on the canvas to click, so this list is the only\n * way back to it. That is why the list exists, and why the row is the shell's,\n * not the shop's: the campaign designer draws the same list.\n *\n * IT IS ONE BUTTON, not a control beside some text. The name and the preview are\n * the button's own content, so they ARE its accessible name — a chevron floating\n * next to unlabelled text would be a press a screen reader could not describe.\n * The chevron is `aria-hidden` (pure direction); the status slot is NOT, because\n * its meaning is the host's to name — it passes an icon carrying its own label\n * (a `title`, a visually-hidden word), and hiding the slot would bury that.\n *\n * CONTROLLED and hook-free like the other four primitives: it owns no open state.\n * Pressing it calls `onOpen`, and the host decides what drilling down means —\n * which keeps this a directive-free leaf a Next-16 server component can import.\n */\nexport function SettingsDrill({\n name,\n preview,\n offPage,\n status,\n onOpen,\n id,\n className,\n}: SettingsDrillProps) {\n const hasPreview = preview !== undefined && preview !== null;\n return (\n <button\n type=\"button\"\n id={id}\n className={className ? `es-drill ${className}` : 'es-drill'}\n onClick={onOpen}\n >\n <span className=\"es-drill-text\">\n <span className=\"es-drill-name\">{name}</span>\n {hasPreview ? (\n <span className={offPage ? 'es-drill-preview is-offpage' : 'es-drill-preview'}>\n {preview}\n </span>\n ) : null}\n </span>\n {status ? <span className=\"es-drill-status\">{status}</span> : null}\n <span className=\"es-drill-chev\" aria-hidden=\"true\" />\n </button>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/panel/SettingsTabs.tsx","../../src/panel/SettingsSegmented.tsx","../../src/panel/SettingsGroup.tsx","../../src/panel/SettingsGroupHead.tsx","../../src/panel/SettingsGroupSub.tsx","../../src/panel/SettingsSwitch.tsx","../../src/panel/SettingsField.tsx","../../src/panel/SettingsDrill.tsx"],"names":["jsx","jsxs"],"mappings":"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","file":"index.js","sourcesContent":["import type { SettingsTabsProps } from './types';\n\n/**\n * The tab strip under the panel head.\n *\n * It replaces the old stack of fold headers a merchant had to open and hunt\n * through: the buckets a section actually has become tabs, in one fixed order,\n * so the panel keeps the same shape on every section. A section shows only the\n * tabs it has — pass three items and three tabs render.\n *\n * Controlled: the host decides `activeId`. No hooks, no browser globals.\n *\n * A tab can be WIRED to what it shows (`panelId` / `htmlId` on the item). That\n * is not decoration: `role=\"tab\"` promises a panel, and a tab strip that names\n * none announces \"tab, 1 of 4, selected\" over nothing. The shell cannot supply\n * those ids — only the host knows the element the settings land in — but until\n * card 8802.d it could not ACCEPT them either, so no host could fix it. A tab\n * with nothing to point at renders no attribute at all; an empty\n * `aria-controls` is worse than a missing one, because it names an element\n * that does not exist instead of admitting there is none.\n *\n * A SEGMENTED CONTROL (card 66138). The strip is a grey track and the chosen\n * bucket is a raised white chip on it. Lewis, looking at a real panel: the tab\n * he is NOT on does not invite a click — it does not even look like a button.\n * The track is the answer to that, because it makes every segment look\n * pressable, where a mark under the chosen tab only ever made the chosen tab\n * louder. It replaces the underline strip and card 8811's tint ground; see the\n * long note on `.es-tab.is-active` in `panel.css`.\n *\n * A segment takes the width its OWN label needs and shares what is left over,\n * rather than every segment taking the width of the widest label. Equal\n * segments were measured and cost too much: three of them need a 267px track\n * and four need 355px, against the 281px the shop's default panel gives.\n *\n * THE WORD DROPS WHERE IT STOPS FITTING. Natural widths fit two and three\n * buckets across the whole panel-drag band; they do not fit four, where the\n * last segment ends 39px past the panel edge. So given an `icon`, a segment can\n * spend less width instead of more — the word goes and the glyph stays.\n *\n * Where that happens depends on HOW MANY buckets a section has, because with\n * natural widths the question is about the sum of the labels:\n *\n * two buckets both words, at every width the panel drags to.\n * three buckets words down to 240px, then glyph plus the chosen word.\n * four buckets glyph plus the chosen word under 320px; glyphs under 200px.\n *\n * Which state is a CSS decision, taken against the PANEL's width — see the\n * `@container` blocks in `panel.css`, and the wrapper below that gives them\n * something to measure. React cannot know the answer at render time, and it\n * does not need to: BOTH parts are always in the markup, and CSS shows the one\n * that fits. The word is only ever hidden, never dropped, and the label rides\n * along as `title` and `aria-label` on every tab in every state, so the glyph\n * never has to speak for the tab.\n *\n * A tab with no icon is untouched at every width — it has nothing to fall back\n * to, so it keeps its word.\n */\nexport function SettingsTabs({\n items,\n activeId,\n onSelect,\n ariaLabel = 'Settings',\n className,\n}: SettingsTabsProps) {\n return (\n // The container context is the SHELL's, not a host's to remember. A panel\n // that forgot to declare one would silently pin the strip to one tier.\n <div className=\"es-tabs-fit\">\n <div\n role=\"tablist\"\n aria-label={ariaLabel}\n aria-orientation=\"horizontal\"\n className={className ? `es-tabs ${className}` : 'es-tabs'}\n >\n {items.map((item) => {\n const active = item.id === activeId;\n const cls = `es-tab${item.icon ? ' has-icon' : ''}${active ? ' is-active' : ''}`;\n return (\n <button\n key={item.id}\n type=\"button\"\n role=\"tab\"\n id={item.htmlId}\n title={item.label}\n aria-label={item.label}\n aria-selected={active}\n aria-controls={item.panelId}\n className={cls}\n onClick={() => onSelect(item.id)}\n >\n {item.icon ? (\n <span className=\"es-tab-icon\" aria-hidden=\"true\">\n {item.icon}\n </span>\n ) : null}\n <span className=\"es-tab-word\">{item.label}</span>\n {typeof item.badge === 'number' && item.badge > 0 ? (\n <span className=\"es-tab-badge\">{item.badge}</span>\n ) : null}\n </button>\n );\n })}\n </div>\n </div>\n );\n}\n","import type { CSSProperties } from 'react';\nimport type { SettingsSegmentedProps } from './types';\n\n/**\n * A segmented control: one row of chips, one of them pressed.\n *\n * THE SAME CHIP AS `SettingsTabs`, AND A DIFFERENT PROMISE. Everything a\n * merchant sees is shared — the grey track, the raised white chip for the one\n * he is on, the glyph drawn at every width, the word that drops where it stops\n * fitting. `panel.css` dresses both from one rule, so there is no second copy\n * of the chip grammar to drift.\n *\n * What differs is what the control SAYS it does. `SettingsTabs` is a tablist\n * and `role=\"tab\"` promises that pressing a chip switches a panel. These chips\n * switch nothing: they write a value and the panel stays where it is. So this\n * is a group of pressed buttons, and its marker is `aria-pressed` — which is\n * also what the storefront's own `Segmented` has carried since the 2026-08-06\n * audit, so the two editors keep saying the same thing about the same control.\n *\n * That distinction is not decoration. Reach for `SettingsTabs` on a Weight row\n * and a control that is correct today starts announcing \"tab, 1 of 4, selected\"\n * over a panel that is not there — three times in one part panel. It is the\n * same split the package already made between `SettingsGroup` and\n * `SettingsGroupHead`: same pill, different promise, two components.\n *\n * Controlled: the host owns `value`. No hooks, no browser globals.\n *\n * ── WHERE THE WORD DROPS, AND WHY THE STRIP HAS TO SAY ───────────────────────\n *\n * The tab strip's tiers are keyed on how many tabs there are, which works\n * because the shell knows those four words. It cannot work here. Measured\n * (`tests/fixtures/segmented-fit.html`), Case — Normal / Uppercase / Lowercase\n * — needs 296px for its words and Alignment — Left / Centre / Right — needs\n * 221px. Both are three-option rows, and the panel drags between 181px and\n * 281px, so a single three-option threshold is visibly wrong for one of them.\n * Whether the words fit is a question about the SUM of the labels; the count is\n * only a stand-in, and it stops standing in the moment a second strip arrives.\n *\n * So the strip carries its own measurement. `fit` is the width of its WORDS,\n * nothing else — the chip's padding, glyph and gaps are the shell's, named on\n * the panel root, and `panel.css` adds them. Those three custom properties\n * below are that measurement and the count, handed to the stylesheet, which\n * compares them against the panel's live width in `calc()`. A container query\n * could not: its condition takes a literal length, so it cannot ask a question\n * whose answer is different for every strip.\n *\n * Without `fit` nothing drops — an unmeasured strip keeps every word at every\n * width, which is exactly what a strip with no glyphs already does.\n *\n * ── WHAT CARD 66303 WIDENED, AND WHY IT HAD TO BE HERE ───────────────────────\n *\n * `efficient-shop` draws its own `Segmented` beside this one and could not stop:\n * four things twelve live call sites need were missing here. Generic `V`\n * (weights are numbers, \"image side\" is a boolean on every published page), a\n * DRAWN label with its own `title`, and `disabled` — on one option or on the\n * strip.\n *\n * The last two could not have been bridged by a wrapper the shop writes, which\n * is the reason they are props and not a consumer's problem. This component\n * renders the `<button>`s, so nothing outside it can disable them; dimming a\n * parent instead leaves them focusable and announcing as ENABLED, which is the\n * regression the 2026-08-06 audit and card 66034 closed. And `label` is spent as\n * `title` and `aria-label` as well as the visible mark, so a node in it reaches\n * both attributes as `[object Object]` — while splitting it into `icon` +\n * `label` renders BOTH spans, and Alignment would draw the rule and the word\n * \"Left\" beside it.\n */\nexport function SettingsSegmented<V = string>({\n options,\n value,\n onChange,\n ariaLabel,\n fit,\n disabled = false,\n className,\n}: SettingsSegmentedProps<V>) {\n // The measurement, handed to the stylesheet. `is-measured` is what the sheet\n // scopes the threshold arithmetic to, so a strip that declared nothing falls\n // back to no threshold at all rather than to a computed one over zeroes.\n const measured: CSSProperties | undefined = fit && {\n ['--es-seg-count' as string]: options.length,\n ['--es-seg-labels' as string]: `${fit.labelsPx}px`,\n ['--es-seg-widest' as string]: `${fit.widestLabelPx}px`,\n };\n\n return (\n // The container context is the SHELL's, not a host's to remember — a panel\n // that forgot to declare one would silently pin the strip to one state.\n <div className=\"es-segs-fit\">\n <div\n role=\"group\"\n aria-label={ariaLabel}\n // A state ATTRIBUTE rather than a second class, so the class string the\n // strip has always rendered is untouched and the sheet's one statement\n // about a whole control out of force has somewhere of its own to hang.\n data-disabled={disabled || undefined}\n className={`es-segs${measured ? ' is-measured' : ''}${className ? ` ${className}` : ''}`}\n style={measured}\n >\n {options.map((option) => {\n // AN EQUALITY, never a truthiness test. `false` is a real answer —\n // \"image left\" is stored as `false` on every published page across\n // every tenant — and a value matching nothing presses nothing, which\n // is the mixed selection the part panel hands in as `\"\"`.\n const pressed = option.id === value;\n // THE NAME, and every option has one. A drawn label has no word to be\n // read aloud, so its `title` is required and is the name; a worded one\n // is named by its word unless it says otherwise. The narrowing is what\n // keeps a node out of the two attributes it would land in as\n // `[object Object]`.\n const name = option.title ?? (typeof option.label === 'string' ? option.label : undefined);\n const off = disabled || option.disabled === true;\n const cls = `es-seg${option.icon ? ' has-icon' : ''}${pressed ? ' is-active' : ''}`;\n return (\n <button\n // Stringified because `V` is whatever the options declare — a\n // weight is a number. For the string strips that shipped before\n // this, the key is the one they already had.\n key={String(option.id)}\n type=\"button\"\n title={name}\n aria-label={name}\n aria-pressed={pressed}\n // The button's OWN disabled, so it leaves the tab order and\n // announces the state itself. React renders nothing for `false`,\n // so a strip in force is unchanged.\n disabled={off}\n className={cls}\n onClick={() => onChange(option.id)}\n >\n {option.icon ? (\n <span className=\"es-seg-icon\" aria-hidden=\"true\">\n {option.icon}\n </span>\n ) : null}\n {/* The label, drawn as given. A worded one is the word; a drawn\n one is the mark itself, in the SAME slot rather than beside it\n — which is why a mark belongs in `label` and not in `icon`. */}\n <span className=\"es-seg-word\">{option.label}</span>\n </button>\n );\n })}\n </div>\n </div>\n );\n}\n","import type { SettingsGroupProps } from './types';\n\n/**\n * A group of settings under a soft pill row that folds with − / +.\n *\n * The sign is drawn by `panel.css` from `aria-expanded`, so the open state has\n * exactly one home (the attribute assistive tech already reads) instead of a\n * second copy in the markup.\n *\n * `count` is the reason a fold here is safe: a folded group that holds changes\n * still says so on its pill, so nothing a merchant changed can hide.\n */\nexport function SettingsGroup({\n title,\n open,\n onToggle,\n count,\n id,\n children,\n className,\n}: SettingsGroupProps) {\n const bodyId = id ? `${id}-body` : undefined;\n return (\n <div className={className ? `es-group ${className}` : 'es-group'}>\n <button\n type=\"button\"\n id={id}\n aria-expanded={open}\n aria-controls={bodyId}\n className=\"es-group-head\"\n onClick={() => onToggle(!open)}\n >\n {title}\n {typeof count === 'number' && count > 0 ? (\n <span className=\"es-group-count\">{count}</span>\n ) : null}\n <span className=\"es-group-sign\" aria-hidden=\"true\" />\n </button>\n <div id={bodyId} className=\"es-group-body\" hidden={!open}>\n {children}\n </div>\n </div>\n );\n}\n","import type { SettingsGroupHeadProps } from './types';\n\n/**\n * A group heading with NO group under it — the pill on its own.\n *\n * It exists because Puck lays the inspector out as a FLAT sibling list: a field\n * renders next to the heading above it, never inside it, so nothing here can\n * wrap the rows that follow. Heading a run of settings is therefore a job for a\n * component that heads and nothing else.\n *\n * `SettingsGroup` cannot do that job. It always renders its body div, and its\n * pill is always a <button> carrying `aria-expanded`, `aria-controls` and the\n * − / + sign. Used header-only it would draw a control that claims to expand\n * something, does nothing when clicked, and tells a screen reader the same lie.\n * A component that folds and a component that titles are two different things,\n * and this is the one that titles.\n *\n * So it is a HEADING, not a control: a real <h3> (the level is the host's, since\n * only the host knows the page outline around it) with no role bolted on, no\n * expanded state, no sign, no body. In a flat list the heading list is the only\n * structure a screen reader has left to navigate by, which is the whole reason\n * this renders a heading element rather than a styled <div>.\n *\n * It wears `.es-group-head` — the same pill as a real group's — so a section\n * that folds and a section that does not read as the same panel. `panel.css`\n * scopes the cursor and the hover tint to `button.es-group-head`, so the\n * heading does not offer a press it cannot honour.\n *\n * NO `count`. A folded group needs one because a fold can hide a change; a\n * heading folds nothing, so nothing can hide behind it.\n */\nexport function SettingsGroupHead({\n title,\n level = 3,\n id,\n className,\n}: SettingsGroupHeadProps) {\n const Tag = `h${level}` as const;\n return (\n <Tag id={id} className={className ? `es-group-head ${className}` : 'es-group-head'}>\n {title}\n </Tag>\n );\n}\n","import type { SettingsGroupSubProps } from './types';\n\n/**\n * The QUIET second tier — a heading one rung under `SettingsGroupHead`.\n *\n * It exists because a panel sometimes has two levels of heading and this package\n * only ever drew one. `SettingsGroupHead` is a filled pill, and a pill cannot\n * serve a tier AND its subordinate: two runs of settings under one pill each\n * need a name, and giving them the same pill says they are siblings of the thing\n * above them rather than parts of it.\n *\n * THE SHOP ALREADY DREW THIS LOOK, TWICE, WHICH IS WHY IT BELONGS HERE.\n * `efficient-shop` hand-rolled `.sf-field-group` in the settings panel and\n * `.sf-group-label` in the Layers tree, and they are the same type to the\n * decimal — 10.5px / 600 / .07em / muted / uppercase. One look, hand-rolled in\n * two panels of one editor, is the definition of a thing the shell should own.\n *\n * ⚠️ NOT THE PILL WITH ITS FILL SWITCHED OFF, and that is the whole reason this\n * is a component and not a `variant` flag on the other one. The shop's own\n * `WHY NOT SettingsGroupHead` docblock refused that shape in advance: a named\n * look the shell draws, \"not a boolean that switches the pill off. A flag whose\n * job is to turn the design off moves the fork inside the shell instead of\n * removing it.\" So there is no path from here to the pill's look and none back —\n * two tiers, two looks, each one this package's own.\n *\n * WHY IT IS QUIET BY TYPE AND THE PILL IS LOUD BY GROUND. The pill separates\n * itself from the settings around it with a fill; this separates itself with\n * CASE and COLOUR and takes no ground at all. That is what lets the two sit in\n * one panel without competing, and it is also why this tier can carry no rule:\n * a hairline under a heading separates it from the rows it heads, which is the\n * opposite of what a heading is for. Removing those rules is the change the\n * owner asked for.\n *\n * A REAL HEADING, defaulting to `h4` — one under the pill's default `h3`,\n * because that is where a subordinate heading sits when the pill above it took\n * the 3. The host may say otherwise; only the host knows the outline around it.\n * Puck lays an inspector out as a FLAT sibling list, so the heading list is the\n * only structure a screen reader has left to navigate by.\n *\n * NO fold, no count, no sign, no body — the same discipline as\n * {@link SettingsGroupHead}, and for the same reason: a component that folds and\n * a component that titles are two different things, and this is one that titles.\n */\nexport function SettingsGroupSub({\n title,\n level = 4,\n id,\n className,\n}: SettingsGroupSubProps) {\n const Tag = `h${level}` as const;\n return (\n <Tag id={id} className={className ? `es-group-sub ${className}` : 'es-group-sub'}>\n {title}\n </Tag>\n );\n}\n","import type { SettingsSwitchProps } from './types';\n\n/**\n * The yes-or-no control — ONE shape, both editors (card 8801).\n *\n * Point 5 of the approved settings design says one switch style everywhere.\n * Before this, the storefront editor drew a tick box and the campaign designer\n * drew a green switch, so a merchant met two shapes for the same question and\n * had to learn the control twice. Both now import this file, which is the only\n * thing that stops them drifting apart again.\n *\n * It is a real <button>, and that is the whole keyboard story: a button is\n * activated by Space and by Enter in every browser, and that activation IS a\n * click — so the one `onClick` below serves mouse and keyboard alike. A div\n * wearing `role=\"switch\"` would need a key handler of our own. Do not add one.\n * `type=\"button\"` keeps it from submitting a form it happens to sit in.\n *\n * It cannot exist without a NAME. `SettingsSwitchProps` demands `ariaLabel` or\n * `ariaLabelledBy` and forbids both at once, so an unnamed switch — which a\n * screen reader reads out as \"switch, on\" and nothing more — is a compile error\n * instead of something a docstring asks you to remember.\n *\n * ONE-WAY DEPENDENCY. `SettingsField` imports this module so it can recognise a\n * switch in its `action` slot and hand it the field's label as a name. This\n * module must NEVER import `SettingsField` back — that would close a cycle\n * between two siblings in the same folder. It imports `./types` and nothing\n * else, and `tests/settings-switch.test.tsx` (o) keeps it that way.\n *\n * CONTROLLED and hook-free like the other three: `checked` comes from the host\n * and the switch only ever ASKS to be flipped. That is what keeps this module a\n * directive-free leaf a Next-16 server component can import.\n */\nexport function SettingsSwitch({\n checked,\n onChange,\n disabled,\n id,\n ariaLabel,\n ariaLabelledBy,\n className,\n}: SettingsSwitchProps) {\n return (\n <button\n type=\"button\"\n role=\"switch\"\n id={id}\n aria-checked={checked}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n disabled={disabled}\n className={className ? `es-switch ${className}` : 'es-switch'}\n onClick={() => onChange(!checked)}\n />\n );\n}\n","import { cloneElement, isValidElement } from 'react';\nimport type { ReactElement, ReactNode } from 'react';\n\nimport { SettingsSwitch } from './SettingsSwitch';\nimport type { SettingsFieldProps } from './types';\n\n/**\n * Does this slot hold a control?\n *\n * The rule is ELEMENT-OR-NOTHING, deliberately not a list of falsy values: a\n * list rots, and the next odd value nobody thought of walks straight through.\n * `{flag && <Slider/>}` yields `false`, `{count && <Slider/>}` yields `0` when\n * the count is zero — and React RENDERS that zero, a stray 0 sitting in the\n * panel where a control belongs. A bare string or number in a control slot is a\n * caller mistake in every case any of us can name.\n *\n * Arrays are walked rather than trusted: `Array.isArray([])` is true, so an\n * empty array — or one holding only `false` and `null`, which is what a list of\n * conditional controls collapses to — would otherwise draw the empty row this\n * check exists to prevent.\n */\nfunction hasControlIn(slot: ReactNode): boolean {\n if (Array.isArray(slot)) return slot.some(hasControlIn);\n return isValidElement(slot);\n}\n\n/** What a switch is named by. Read off an unknown element, so it stays loose. */\ninterface SwitchName {\n ariaLabel?: string;\n ariaLabelledBy?: string;\n}\n\n/**\n * Give a switch in the `action` slot the field's own label as its name.\n *\n * A switch carries no text, and `htmlFor` cannot bridge a <label> to a\n * <button>, so in a boolean row nothing connects the visible label to the\n * control unless something does it explicitly. Doing it HERE means the common\n * case is right with the caller saying nothing: the accessible name and the\n * visible name are the same string and cannot drift apart.\n *\n * It only ever fills a gap. A switch that names itself keeps its own name, and\n * anything that is not a switch — a reset dot, a badge — is left alone, since\n * labelling those with the field's name would be worse than not labelling them.\n */\nfunction nameSwitch(action: ReactNode, label: string, labelId?: string): ReactNode {\n if (!isValidElement(action) || action.type !== SettingsSwitch) return action;\n\n const named = action.props as SwitchName;\n if (named.ariaLabel || named.ariaLabelledBy) return action;\n\n return cloneElement(\n action as ReactElement<SwitchName>,\n labelId ? { ariaLabelledBy: labelId } : { ariaLabel: label },\n );\n}\n\n/**\n * One setting: a label row, then its control.\n *\n * The row is stacked rather than side-by-side so the control gets the panel's\n * full width — a 308px panel cannot afford a label column AND a usable slider.\n * The label reads small and quiet; the VALUE sits at the end of the same line in\n * the text colour, which is what a merchant scans for. That contrast is the\n * point: before this, label and value looked alike and the panel read as noise.\n *\n * `hint` is where a clause-long explanation goes. A label is a name, never a\n * sentence.\n *\n * A BOOLEAN row is the one exception to \"control on its own line\" (card 8801,\n * Option A, approved). A switch is small and it is the whole control, so it\n * rides the label line in the `action` slot and the row costs one line instead\n * of two — which is what lets a 308px panel show four settings where it used to\n * show three. That is the only reason `children` is optional: a field with no\n * children draws no control row, and its label line IS the row.\n *\n * A field with no children AND no action has no control on either slot. That is\n * a mistake at the call site, so it renders NOTHING — an empty row would hide\n * the mistake behind 20px of blank panel.\n */\nexport function SettingsField({\n label,\n labelId,\n icon,\n value,\n hint,\n changed,\n htmlFor,\n action,\n children,\n className,\n}: SettingsFieldProps) {\n const hasControl = hasControlIn(children);\n const hasAction = hasControlIn(action);\n if (!hasControl && !hasAction) return null;\n\n const classes = ['es-field'];\n if (!hasControl) classes.push('is-inline');\n if (changed) classes.push('is-changed');\n if (className) classes.push(className);\n\n const labelBody = (\n <>\n {icon ? (\n <span className=\"es-field-icon\" aria-hidden=\"true\">\n {icon}\n </span>\n ) : null}\n <span className=\"es-field-label\" id={labelId}>\n {label}\n </span>\n </>\n );\n\n return (\n <div className={classes.join(' ')}>\n <div className=\"es-field-head\">\n {htmlFor ? (\n <label className=\"es-field-name\" htmlFor={htmlFor}>\n {labelBody}\n </label>\n ) : (\n <span className=\"es-field-name\">{labelBody}</span>\n )}\n {value !== undefined && value !== null ? (\n <span className=\"es-field-value\">{value}</span>\n ) : null}\n {hasAction ? nameSwitch(action, label, labelId) : null}\n </div>\n {hasControl ? <div className=\"es-field-ctl\">{children}</div> : null}\n {hint ? <p className=\"es-field-hint\">{hint}</p> : null}\n </div>\n );\n}\n","import type { SettingsDrillProps } from './types';\n\n/**\n * A DRILL-IN ROW — a name, a one-line preview, and a chevron (card 8819).\n *\n * The storefront editor's text editing becomes a panel drill-down: the section\n * screen no longer holds a text box for every part; it LISTS the parts as these\n * rows, and pressing one takes the inspector down a level to that part's own\n * editor. A part the eye hid — or one that never draws on the page at all, like\n * a `Slide name` — has no words on the canvas to click, so this list is the only\n * way back to it. That is why the list exists, and why the row is the shell's,\n * not the shop's: the campaign designer draws the same list.\n *\n * IT IS ONE BUTTON, not a control beside some text. The name and the preview are\n * the button's own content, so they ARE its accessible name — a chevron floating\n * next to unlabelled text would be a press a screen reader could not describe.\n * The chevron is `aria-hidden` (pure direction); the status slot is NOT, because\n * its meaning is the host's to name — it passes an icon carrying its own label\n * (a `title`, a visually-hidden word), and hiding the slot would bury that.\n *\n * CONTROLLED and hook-free like the other four primitives: it owns no open state.\n * Pressing it calls `onOpen`, and the host decides what drilling down means —\n * which keeps this a directive-free leaf a Next-16 server component can import.\n */\nexport function SettingsDrill({\n name,\n preview,\n offPage,\n status,\n onOpen,\n id,\n className,\n}: SettingsDrillProps) {\n const hasPreview = preview !== undefined && preview !== null;\n return (\n <button\n type=\"button\"\n id={id}\n className={className ? `es-drill ${className}` : 'es-drill'}\n onClick={onOpen}\n >\n <span className=\"es-drill-text\">\n <span className=\"es-drill-name\">{name}</span>\n {hasPreview ? (\n <span className={offPage ? 'es-drill-preview is-offpage' : 'es-drill-preview'}>\n {preview}\n </span>\n ) : null}\n </span>\n {status ? <span className=\"es-drill-status\">{status}</span> : null}\n <span className=\"es-drill-chev\" aria-hidden=\"true\" />\n </button>\n );\n}\n"]}
|
package/dist/panel/panel.css
CHANGED
|
@@ -254,28 +254,52 @@
|
|
|
254
254
|
box-shadow: 0 0 0 3px var(--es-accent-ring);
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
-
/* THE CHOSEN
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
257
|
+
/* THE CHOSEN CHIP IS THE DARKEST THING IN THE ROW (card 66302).
|
|
258
|
+
|
|
259
|
+
IT WAS A RAISED WHITE CHIP, AND THAT WAS NOT A MISTAKE. Card 66138 put white
|
|
260
|
+
on the grey track with a soft lift, and Lewis picked that look himself from
|
|
261
|
+
six treatments. He picked it BY EYE, AT A WIDTH WHERE THE WORDS WERE STILL
|
|
262
|
+
THERE — which is the whole reason this is a revision and not a correction.
|
|
263
|
+
|
|
264
|
+
WHAT CHANGED IS THE CONDITION, TWICE.
|
|
265
|
+
|
|
266
|
+
First the words went. A four-tab strip is already wordless below 200px, and
|
|
267
|
+
the panel's drag band bottoms out at 181px, so that state is live in the
|
|
268
|
+
editor today; card 66246 then made the shop's Weight row show one word of
|
|
269
|
+
four at the DEFAULT panel and none at the narrow end. A chip that used to sit
|
|
270
|
+
beside a word became the only thing saying which option is set.
|
|
271
|
+
|
|
272
|
+
Then the second mark went, and nobody noticed. Card 66248 moved the strip
|
|
273
|
+
into this package and the quiet label darkened from the muted grey to
|
|
274
|
+
`--es-text-secondary`. That darkening is right on its own terms — it is card
|
|
275
|
+
66138's AA fix, 4.39:1 to 9.37:1 on the track — but it also cut the
|
|
276
|
+
chosen-vs-quiet GLYPH contrast from 3.67:1 to 1.72:1. The strip lost the one
|
|
277
|
+
state cue that was already above 3:1, and the change was described as
|
|
278
|
+
cosmetic when it merged.
|
|
279
|
+
|
|
280
|
+
So the raised white chip was left carrying the whole message alone, at
|
|
281
|
+
1.10:1 against its own track, where WCAG 1.4.11 asks 3:1 of anything
|
|
282
|
+
identifying a control's STATE.
|
|
283
|
+
|
|
284
|
+
WHY DARK AND NOT A RING. Four treatments were drawn and measured for Lewis;
|
|
285
|
+
two of the routes were closed by arithmetic before he saw them (darkening the
|
|
286
|
+
track drops the quiet label to 3.40:1 and fails AA; pushing the glyph delta
|
|
287
|
+
only reaches 3:1 by putting that label back where 66138 moved it FROM). Of
|
|
288
|
+
what was left, this is the one that survives the DISABLED state:
|
|
289
|
+
`.es-segs[data-disabled]` fades the strip to 0.5 over the panel, and a ring
|
|
290
|
+
at 4.39:1 composites down to 1.89:1 while this holds 3.24:1. A cue that only
|
|
291
|
+
works while the control is live is a cue with a hole in it.
|
|
292
|
+
|
|
293
|
+
⚠️ THE LIFT STAYS, AND IT IS NO LONGER THE MESSAGE. `--es-raised-shadow`
|
|
294
|
+
still seats the chip on the track. What it must not go back to being is the
|
|
295
|
+
thing telling the two states apart — a shadow is not a colour, so no
|
|
296
|
+
contrast ratio can score it, and that is exactly how this got to 1.10:1
|
|
297
|
+
without any guard noticing. `tests/panel-chip-contrast.test.ts` pins the
|
|
298
|
+
RATIO and not this colour, so a better answer is free to replace it. */
|
|
275
299
|
.es-tab.is-active,
|
|
276
300
|
.es-seg.is-active {
|
|
277
|
-
background: var(--es-
|
|
278
|
-
color: var(--es-
|
|
301
|
+
background: var(--es-text);
|
|
302
|
+
color: var(--es-on-accent);
|
|
279
303
|
box-shadow: var(--es-raised-shadow);
|
|
280
304
|
border-radius: 6px;
|
|
281
305
|
}
|
|
@@ -552,8 +576,17 @@
|
|
|
552
576
|
|
|
553
577
|
/* The chosen one stays the loudest thing on the track. It is still the
|
|
554
578
|
merchant's answer — a pressed chip faded to the same grey as the ones beside
|
|
555
|
-
it stops saying which answer that was.
|
|
556
|
-
|
|
579
|
+
it stops saying which answer that was.
|
|
580
|
+
|
|
581
|
+
⚠️ THIS COLOUR BELONGS TO THE CHIP'S GROUND, NOT TO THE TRACK (card 66302).
|
|
582
|
+
It was `--es-text-secondary`, which was right while the chip was white:
|
|
583
|
+
10.31:1 on it. The moment the chip went dark that same rule drew
|
|
584
|
+
`--es-text-secondary` on `--es-text` — 1.72:1, an unreadable label on a
|
|
585
|
+
control that had just been made MORE visible. Nothing about the rule changed; what it sat on moved underneath
|
|
586
|
+
it. So it reads `--es-on-accent` for the same reason the enabled chip does,
|
|
587
|
+
and the strip says which option is out of force through the track's fade and
|
|
588
|
+
its quiet siblings rather than by dimming the one word still worth reading. */
|
|
589
|
+
.es-seg:disabled.is-active { color: var(--es-on-accent); }
|
|
557
590
|
|
|
558
591
|
/* And none of them answers the pointer any more: the hover rule above would
|
|
559
592
|
otherwise keep lighting a segment that cannot be chosen. */
|
|
@@ -610,6 +643,45 @@
|
|
|
610
643
|
margin: max(var(--es-row-gap), var(--es-rhythm-group)) 0 0;
|
|
611
644
|
}
|
|
612
645
|
|
|
646
|
+
/* THE QUIET TIER (card 66300) — a heading one rung under the pill.
|
|
647
|
+
NO GROUND AND NO RULE, which is the whole point of it. The pill separates
|
|
648
|
+
itself from the settings around it with a fill; this separates itself with
|
|
649
|
+
case and colour, so the two can sit in one panel without competing. And a
|
|
650
|
+
heading takes no hairline under it: a rule there separates a heading from the
|
|
651
|
+
rows it heads, which is the opposite of what a heading is for. The storefront
|
|
652
|
+
ruled BOTH its tiers that way and the owner's words on 2026-08-31 were
|
|
653
|
+
"these lines, I still find them annoying to look at".
|
|
654
|
+
|
|
655
|
+
THE TYPE IS NOT INVENTED HERE. It is `.sf-field-group` and `.sf-group-label`
|
|
656
|
+
from `efficient-shop`, which are the same look to the decimal, hand-rolled in
|
|
657
|
+
two panels of one editor. Owning it here is what stops a third copy.
|
|
658
|
+
|
|
659
|
+
FLUSH, not inset. `.es-field` has no horizontal padding, so a heading that
|
|
660
|
+
started 11px in would sit to the RIGHT of the rows it heads and read as those
|
|
661
|
+
rows being outdented from it. The pill's text is inset only because a pill has
|
|
662
|
+
padding; this has no such excuse.
|
|
663
|
+
|
|
664
|
+
ONE NOTCH UNDER THE PILL IN AIR AS WELL AS IN TYPE. `--es-rhythm-between`
|
|
665
|
+
rather than the pill's `--es-rhythm-group`, so the tiers stay in order when a
|
|
666
|
+
reader is skimming rather than reading. Written as NAMES: every one of these
|
|
667
|
+
tokens grew between 0.20.0 and 0.23.0 (group 36->48, between 28->38,
|
|
668
|
+
group-first 14->24) and the ladder survived untouched because it reads names.
|
|
669
|
+
Pinned numbers would have crossed. */
|
|
670
|
+
.es-group-sub {
|
|
671
|
+
box-sizing: border-box;
|
|
672
|
+
font-size: 10.5px;
|
|
673
|
+
font-weight: 600;
|
|
674
|
+
letter-spacing: 0.07em;
|
|
675
|
+
text-transform: uppercase;
|
|
676
|
+
color: var(--es-muted);
|
|
677
|
+
margin: max(var(--es-row-gap), var(--es-rhythm-between)) 0
|
|
678
|
+
max(var(--es-row-gap), var(--es-rhythm-group-first));
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/* Directly under a pill it belongs TO that pill, so it does not take a second
|
|
682
|
+
helping of the air the pill already put above itself. */
|
|
683
|
+
.es-group-head + .es-group-sub { margin-top: max(var(--es-row-gap), var(--es-rhythm-group-first)); }
|
|
684
|
+
|
|
613
685
|
button.es-group-head {
|
|
614
686
|
cursor: pointer;
|
|
615
687
|
transition: background 0.12s;
|
package/dist/shell/index.d.ts
CHANGED
|
@@ -42,7 +42,7 @@ declare const shellTokens: {
|
|
|
42
42
|
readonly previewBg: "#f3f4f6";
|
|
43
43
|
/** `--es-panel` — raised cards / panels. */
|
|
44
44
|
readonly panel: "#ffffff";
|
|
45
|
-
/** `--es-raised` — active
|
|
45
|
+
/** `--es-raised` — a lifted surface. NOT the active chip since card 66302. */
|
|
46
46
|
readonly raised: "#ffffff";
|
|
47
47
|
/** `--es-topbar-bg`. */
|
|
48
48
|
readonly topbarBg: "#ffffff";
|
package/dist/shell/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "editor-shell",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "Shared editor-chrome primitives for the EFFICIENT editors: EditorRail (a Next-16-safe left icon-rail leaf), the shell token layer, and GoldTextInput — type in place and watch the markup formatting appear as you type.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Lewis Liu",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shell/tokens.ts"],"names":[],"mappings":"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","file":"chunk-EZKBKK3E.js","sourcesContent":["/**\n * Editor SHELL design tokens — the ONE source both editors' chrome converge on.\n *\n * Where `rail/tokens.ts` owns the slim icon-rail, this owns the rest of the\n * editor chrome theme: accent, surfaces, fields, text, lines, chips, named\n * chrome literals, danger/warning/success, radii, gap, shadows and font. Every\n * value bar one is taken verbatim from the storefront editor's `--sf-*` block\n * (`efficient-shop/components/storefront-editor.css`), the visual reference the\n * campaign designer (`--cd-*`) and the storefront's own TS mirror (`tk`) had\n * each re-copied. After card 66024 they all resolve to these values instead.\n * The one exception is `success`, which that block has no counterpart for; see\n * its own note below.\n *\n * Exposed BOTH ways, per the same value (mirrors the rail):\n * - `shellTokens` — this JS object, for code that needs the numbers/colours\n * (e.g. the storefront's `floating-editor/tokens.ts` inline-style mirror);\n * - `shell.css` — the stylesheet, which declares the identical values as CSS\n * custom properties on `.es-editor` (names listed in `shellCssVars`).\n *\n * Numeric fields are pixel magnitudes (unitless) so they compose in code; the\n * CSS applies the `px`. Both editing surfaces are LIGHT only (no dark mode), so\n * there is a single palette here — no dark counterpart.\n */\nexport const shellTokens = {\n // ── Accent (react-os-shell blue) ──────────────────────────────────────────\n /** `--es-accent` — blue-600. */\n accent: '#2563eb',\n /** `--es-accent-tint` — blue-50. */\n accentTint: '#eff6ff',\n /** `--es-accent-tint-strong` — blue-100. */\n accentTintStrong: '#dbeafe',\n /** `--es-accent-text` — blue-700. */\n accentText: '#1d4ed8',\n /** `--es-accent-ring` — focus ring wash. */\n accentRing: 'rgba(37, 99, 235, 0.25)',\n /** `--es-accent-shadow` — accent button shadow. */\n accentShadow: 'rgba(37, 99, 235, 0.35)',\n /** `--es-on-accent` — text/icon over the solid accent. */\n onAccent: '#ffffff',\n\n // ── Surfaces (cool gray ramp) ─────────────────────────────────────────────\n /** `--es-app-bg` — gray-50, sunken canvas. */\n appBg: '#f9fafb',\n /** `--es-preview-bg` — gray-100, preview well. */\n previewBg: '#f3f4f6',\n /** `--es-panel` — raised cards / panels. */\n panel: '#ffffff',\n /** `--es-raised` — active seg / chip. */\n raised: '#ffffff',\n /** `--es-topbar-bg`. */\n topbarBg: '#ffffff',\n /** `--es-frame-bg` — device \"paper\" behind the preview iframe. */\n frameBg: '#ffffff',\n /** `--es-hover` — gray-100 row / icon hover. */\n hover: '#f3f4f6',\n /** `--es-hatch` — 45° upload-slot texture. */\n hatch: 'rgba(0, 0, 0, 0.018)',\n /** `--es-addcard-veil` — add-section card hover wash. */\n addcardVeil: 'rgba(248, 249, 250, 0.66)',\n\n // ── Fields ────────────────────────────────────────────────────────────────\n /** `--es-field-bg`. */\n fieldBg: '#ffffff',\n /** `--es-track-bg` — gray-100 segmented / search track. */\n trackBg: '#f3f4f6',\n /** `--es-input-border` — gray-300. */\n inputBorder: '#d1d5db',\n /** `--es-field-text` — gray-800. */\n fieldText: '#1f2937',\n /** `--es-field-placeholder` — gray-400. */\n fieldPlaceholder: '#9ca3af',\n /** `--es-knob` — toggle knob. */\n knob: '#ffffff',\n\n // ── Text ────────────────────────────────────────────────────────────────\n /** `--es-text` — gray-900. */\n text: '#111827',\n /** `--es-text-strong` — gray-950 active labels. */\n textStrong: '#030712',\n /** `--es-text-secondary` — gray-700. */\n textSecondary: '#374151',\n /** `--es-muted` — gray-500. */\n muted: '#6b7280',\n /** `--es-icon` — gray-500 icon buttons. */\n icon: '#6b7280',\n /** `--es-disabled` — gray-400 disabled / faint glyphs. */\n disabled: '#9ca3af',\n\n // ── Lines / chips / buttons ──────────────────────────────────────────────\n /** `--es-divider` — gray-200. */\n divider: '#e5e7eb',\n /** `--es-line` — gray-200 hairlines. */\n line: '#e5e7eb',\n /** `--es-btn-border` — gray-300. */\n btnBorder: '#d1d5db',\n /** `--es-btn-text` — gray-700. */\n btnText: '#374151',\n /** `--es-chip-bg` — gray-100. */\n chipBg: '#f3f4f6',\n /** `--es-chip-fg` — gray-500. */\n chipFg: '#6b7280',\n\n // ── Named chrome literals ─────────────────────────────────────────────────\n /** `--es-brand-square` — gray-900 wordmark square. */\n brandSquare: '#111827',\n /** `--es-device-border` — gray-200 tablet/mobile frame. */\n deviceBorder: '#e5e7eb',\n /** `--es-toast-bg` — gray-900 dark chip / seam pill. */\n toastBg: '#111827',\n /** `--es-toggle-off` — gray-300 toggle track. */\n toggleOff: '#d1d5db',\n /** `--es-scrollbar` — gray-300. */\n scrollbar: '#d1d5db',\n /** `--es-scrollbar-hover` — gray-400. */\n scrollbarHover: '#9ca3af',\n\n // ── Danger / warning / success (shell red / amber / green) ────────────────\n /** `--es-danger` — red-600. */\n danger: '#dc2626',\n /** `--es-danger-strong` — red-700. */\n dangerStrong: '#b91c1c',\n /** `--es-danger-tint` — red-50. */\n dangerTint: '#fef2f2',\n /** `--es-warning` — amber-700. */\n warning: '#b45309',\n /** `--es-preview-tint` — amber-100 \"Preview\" pill. */\n previewTint: '#fef3c7',\n /**\n * `--es-success` — green-700. THE ONE VALUE HERE WITH NO `--sf-*` BEHIND IT:\n * the storefront editor's block has a red and an amber and no green, and card\n * 8802.g needs one for a green tick. So it is picked to sit with the block\n * rather than invented next to it — the same Tailwind ramp every other colour\n * in this file comes from, at the 700 step `--es-warning` already sits on.\n * 700 and not 600 for the reason amber went there: it is the darkest step\n * still plainly the hue, and it clears WCAG AA (5.0:1) on the shell's white\n * panels, so a tick may carry a label without needing a second token.\n */\n success: '#15803d',\n\n // ── Radii / shape / gap ───────────────────────────────────────────────────\n /** `--es-panel-radius`. */\n panelRadius: 12,\n /** `--es-row-radius`. */\n rowRadius: 8,\n /** `--es-field-radius`. */\n fieldRadius: 6,\n /** `--es-panel-gap` — inter-pane gutter + outer frame padding, ONE token. */\n panelGap: 10,\n\n // ── Shadows ───────────────────────────────────────────────────────────────\n /** `--es-panel-shadow`. */\n panelShadow: '0 1px 2px rgba(0, 0, 0, 0.05), 0 10px 30px rgba(0, 0, 0, 0.06)',\n /** `--es-topbar-shadow`. */\n topbarShadow: '0 1px 3px rgba(0, 0, 0, 0.06)',\n /**\n * `--es-raised-shadow` — what a small surface sitting ON another one casts:\n * the chip in the settings tab strip's track (card 66138). Tighter and darker\n * than `panelShadow`, which is a whole panel floating over the app; this one\n * has to read across 3px of track without looking like a second panel.\n */\n raisedShadow: '0 1px 2px rgba(16, 24, 40, 0.1)',\n\n // ── Type ──────────────────────────────────────────────────────────────────\n /** `--es-font` — system sans stack (also fed to `--puck-font-family`). */\n font: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, sans-serif',\n} as const;\n\nexport type ShellTokens = typeof shellTokens;\n\n/**\n * The CSS custom-property name behind each `shellTokens` key. `shell.css` sets\n * these on `.es-editor`; override any of them on (or above) the editor root to\n * re-theme without shipping new CSS. The `shell-tokens.test.ts` parity gate\n * proves this map, `shellTokens` and `shell.css` never drift apart.\n */\nexport const shellCssVars: Record<keyof ShellTokens, string> = {\n accent: '--es-accent',\n accentTint: '--es-accent-tint',\n accentTintStrong: '--es-accent-tint-strong',\n accentText: '--es-accent-text',\n accentRing: '--es-accent-ring',\n accentShadow: '--es-accent-shadow',\n onAccent: '--es-on-accent',\n appBg: '--es-app-bg',\n previewBg: '--es-preview-bg',\n panel: '--es-panel',\n raised: '--es-raised',\n topbarBg: '--es-topbar-bg',\n frameBg: '--es-frame-bg',\n hover: '--es-hover',\n hatch: '--es-hatch',\n addcardVeil: '--es-addcard-veil',\n fieldBg: '--es-field-bg',\n trackBg: '--es-track-bg',\n inputBorder: '--es-input-border',\n fieldText: '--es-field-text',\n fieldPlaceholder: '--es-field-placeholder',\n knob: '--es-knob',\n text: '--es-text',\n textStrong: '--es-text-strong',\n textSecondary: '--es-text-secondary',\n muted: '--es-muted',\n icon: '--es-icon',\n disabled: '--es-disabled',\n divider: '--es-divider',\n line: '--es-line',\n btnBorder: '--es-btn-border',\n btnText: '--es-btn-text',\n chipBg: '--es-chip-bg',\n chipFg: '--es-chip-fg',\n brandSquare: '--es-brand-square',\n deviceBorder: '--es-device-border',\n toastBg: '--es-toast-bg',\n toggleOff: '--es-toggle-off',\n scrollbar: '--es-scrollbar',\n scrollbarHover: '--es-scrollbar-hover',\n danger: '--es-danger',\n dangerStrong: '--es-danger-strong',\n dangerTint: '--es-danger-tint',\n warning: '--es-warning',\n previewTint: '--es-preview-tint',\n success: '--es-success',\n panelRadius: '--es-panel-radius',\n rowRadius: '--es-row-radius',\n fieldRadius: '--es-field-radius',\n panelGap: '--es-panel-gap',\n panelShadow: '--es-panel-shadow',\n topbarShadow: '--es-topbar-shadow',\n raisedShadow: '--es-raised-shadow',\n font: '--es-font',\n};\n\n/**\n * Puck's own accent ramp + font, re-tinted to the shell blue. Puck reads these\n * fixed variable names (`--puck-color-azure-NN`, `--puck-font-family`) directly,\n * so they are NOT part of the `--es-*` set — but they had been copied verbatim\n * into BOTH editors' CSS, so `shell.css` now declares them once on `.es-editor`.\n * Kept here as the JS record so the parity gate can prove `shell.css` matches.\n */\nexport const puckAzureRamp: Record<string, string> = {\n '--puck-color-azure-01': '#172554',\n '--puck-color-azure-02': '#1e3a8a',\n '--puck-color-azure-03': '#1d4ed8',\n '--puck-color-azure-04': '#2563eb',\n '--puck-color-azure-05': '#3b82f6',\n '--puck-color-azure-06': '#60a5fa',\n '--puck-color-azure-07': '#93c5fd',\n '--puck-color-azure-08': '#bfdbfe',\n '--puck-color-azure-09': '#dbeafe',\n '--puck-color-azure-10': '#eff6ff',\n '--puck-color-azure-11': '#f5f9ff',\n '--puck-color-azure-12': '#fafcff',\n};\n"]}
|