@wix/web5-core 1.63.43 → 1.63.44
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/cjs/client/applyThemeOverrides.js +41 -9
- package/dist/cjs/client/applyThemeOverrides.js.map +1 -1
- package/dist/cjs/client/themeDebug.js +15 -6
- package/dist/cjs/client/themeDebug.js.map +1 -1
- package/dist/cjs/index.js +15 -4
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/theme/themeScheme.js +375 -0
- package/dist/cjs/theme/themeScheme.js.map +1 -0
- package/dist/cjs/theme/tokenContract.js +8 -1
- package/dist/cjs/theme/tokenContract.js.map +1 -1
- package/dist/esm/client/applyThemeOverrides.js +42 -10
- package/dist/esm/client/applyThemeOverrides.js.map +1 -1
- package/dist/esm/client/themeDebug.js +15 -6
- package/dist/esm/client/themeDebug.js.map +1 -1
- package/dist/esm/index.js +2 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/theme/themeScheme.js +372 -0
- package/dist/esm/theme/themeScheme.js.map +1 -0
- package/dist/esm/theme/tokenContract.js +7 -0
- package/dist/esm/theme/tokenContract.js.map +1 -1
- package/dist/types/client/applyThemeOverrides.d.ts +1 -1
- package/dist/types/client/applyThemeOverrides.d.ts.map +1 -1
- package/dist/types/client/themeDebug.d.ts +2 -2
- package/dist/types/client/themeDebug.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/theme/themeScheme.d.ts +79 -0
- package/dist/types/theme/themeScheme.d.ts.map +1 -0
- package/dist/types/theme/tokenContract.d.ts +6 -0
- package/dist/types/theme/tokenContract.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_colorFormat","require","_tokenContract","SCHEME_COLOR_TOKENS","exports","Object","freeze","Set","entries","THEME_TOKEN_CONTRACT","filter","entry","type","map","token","SCHEME_COLOR_VALUE","SCHEME_RADIUS_VALUE","SCHEME_LOOSE_VALUE","MAX_SCHEME_VALUE_LENGTH","SCHEME_ANCHOR_TOKENS","MAX_THEME_SCHEMES","SCHEME_ID_PATTERN","MAX_SCHEME_NAME_LENGTH","isPlainObject","value","Array","isArray","isThemeScheme","id","name","origin","tokens","test","undefined","length","values","every","v","resolveActiveScheme","cfg","themeSchemes","schemes","activeId","activeThemeSchemeId","active","find","scheme","TRIPLET","tripletToRgb","triplet","parts","exec","trim","isHslTriplet","h","Number","s","l","c","Math","abs","x","m","r","g","b","mix","fg","ratio","bg","round","toTriplet","rn","gn","bn","max","min","delta","INHERITED_TOKENS","completeScheme","base","anchors","valid","isSchemeColorValue","fromBase","fromAnchor","out","put","background","foreground","bgChanged","fgChanged","derive","inputsChanged","compute","kept","mixOver","over","primaryTriplet","primary","has","isSchemeTokenEntry","schemeDisplayName","index","nextOwnerSchemeId","taken","n","key","_THEME_TOKEN_CONTRACT","THEME_OVERRIDE_KEY_PATTERN"],"sources":["../../../src/theme/themeScheme.ts"],"sourcesContent":["/**\n * Named theme schemes on a site configuration, and the one that is active.\n *\n * A store's theme states a SET of palettes rather than one — Dawn and Horizon\n * ship `scheme-1`..`scheme-5` for the page, the cards, the hero band, the\n * footer. The configuration therefore carries a list of schemes plus the id of\n * the active one, beside the flat `themeOverrides` map it has always had:\n *\n * themeSchemes?: [{ id, name?, origin?, tokens: { '--background': '0 0% 100%', … } }]\n * activeThemeSchemeId?: 'scheme-1'\n *\n * A scheme's `tokens` are finished CSS tokens, spelled exactly as\n * `themeOverrides` spells them — and, like `themeOverrides`, the key set is\n * OPEN. Today's writers put the 18 colour roles (`SCHEME_COLOR_TOKENS`) there,\n * sometimes `--radius`; that a palette may carry shape as well as colour is why\n * this is a THEME scheme and not a colour scheme. A new token (a font stack,\n * something the contract has not catalogued yet) needs no release anywhere but\n * its writer. The server checks only the loose shape `themeOverrides` gets (key\n * `THEME_OVERRIDE_KEY_PATTERN`, a value free of `;`, `{`, `}` and control\n * characters, ≤ 256, at most 24 entries); the typed per-token grammar is the\n * CLIENT's contract, enforced by `isSchemeTokenEntry` before the panel writes.\n * Nothing is expanded at apply time: the reader resolves the active entry and\n * hands its map to `applyThemeOverrides`, which layers it between the store map\n * and the owner's overrides, filtered and bucketed exactly like the store map.\n * A configuration with no schemes renders exactly as it did before they existed.\n *\n * A leaf module beside `tokenContract.ts`, and for the same reason that file\n * documents: requiring core's ENTRY from plain Node fails on its CSS import,\n * but a deep import of a zero-CSS leaf works. It imports only its siblings\n * `./tokenContract` and `./colorFormat`, both leaves themselves.\n *\n * ## `completeScheme` — what the owner panel recomputes\n *\n * The owner edits the four anchors. The rest of the palette has to move with\n * them the way `web50-shopify-adapter/src/theme-tokens.ts` would have moved it\n * had the theme stated those anchors, so the arithmetic below is a COPY of the\n * adapter's (`composite`/`mix`/`toTriplet` and the token assignments at the end\n * of `extractThemeOverrides`), integer rounding included. It is copied rather\n * than shared on purpose: the adapter must not import core.\n *\n * | token | role |\n * | ------------------------ | ------------------------------------------------------ |\n * | `--background` | anchor (else inherited from base) |\n * | `--foreground` | anchor (else inherited from base) |\n * | `--card` | anchor; when not given, derived: foreground 4% over background |\n * | `--card-foreground` | anchor; when not given, derived: = foreground |\n * | `--popover` | derived: = background |\n * | `--popover-foreground` | derived: = foreground |\n * | `--muted` | derived: foreground 4% over background |\n * | `--muted-foreground` | derived: foreground 60% over background |\n * | `--secondary` | derived: primary 10% over background |\n * | `--accent` | derived: primary 10% over background |\n * | `--primary` | inherited from base (the theme's button colour) |\n * | `--primary-foreground` | inherited from base (the theme's button label) |\n * | `--secondary-foreground` | inherited from base (the theme's outline-button label) |\n * | `--accent-foreground` | inherited from base; fallback = primary |\n * | `--ring` | inherited from base; fallback = primary |\n * | `--border` | inherited from base (no fallback — see below) |\n * | `--input` | inherited from base (no fallback — see below) |\n * | `--heading` | inherited from base (no adapter fills it) |\n * | any non-colour key | `--radius`, fonts, unknown: copied from base when valid |\n *\n * \"Inherited\" tokens are ones the adapter reads straight off the theme, which\n * no arithmetic over the anchors can recover. Where base lacks one, the\n * adapter's own fallback applies: `--ring` and `--accent-foreground` are the\n * primary colour. `--border`/`--input` have none — the adapter reconstructs a\n * missing border from the theme's `*_border_opacity` setting, which a token map\n * does not carry — so they are omitted, as the adapter omits them.\n *\n * ## Rounding: why an unchanged input keeps base's value\n *\n * The adapter mixes 8-bit channels parsed from the theme's hex; a scheme only\n * has the integer-rounded triplet, and the hex cannot be recovered from it.\n * Re-deriving from triplets therefore lands a point off now and then — Horizon\n * `color_palette`'s `--muted-foreground` comes back `359 83% 68%` against the\n * adapter's `359 84% 68%`. So a derived token is recomputed only when one of\n * its INPUTS differs from base; otherwise base's value stands. A merchant who\n * edits nothing gets the adapter's map back exactly, and one who edits the\n * background gets the family recomputed.\n *\n * When it is recomputed, triplets convert back to RGB WITHOUT rounding to\n * 8-bit first — that would be a second quantisation the adapter never\n * performs, and enough to move smallflower's `--secondary` from `230 38% 94%`\n * to `230 42% 94%`. The mix result is rounded exactly where the adapter\n * rounds it.\n */\nimport { isHslTriplet } from './colorFormat';\nimport {\n THEME_OVERRIDE_KEY_PATTERN,\n THEME_TOKEN_CONTRACT,\n} from './tokenContract';\n\n/** Where a scheme came from. Any other value on the wire reads as unknown. */\nexport type SchemeOrigin = 'UNKNOWN_SCHEME_ORIGIN' | 'THEME' | 'OWNER';\n\n/** One named palette as the configuration carries it. */\nexport interface ThemeScheme {\n /** `^[a-z0-9][a-z0-9-]{0,31}$`, unique within the list. */\n id: string;\n /** Shown in the owner panel; at most 48 characters. */\n name?: string;\n origin?: SchemeOrigin | string;\n /**\n * Token → value, keys as open as `themeOverrides`': colours as bare HSL\n * triplets (`0 0% 100%`), `--radius` as a length (`0.5rem`), anything else\n * in the loose value shape. See `isSchemeTokenEntry`.\n */\n tokens: Record<string, string>;\n}\n\n/** Every colour role in the contract (`type: 'color-hsl'`, `--heading` included). */\nexport const SCHEME_COLOR_TOKENS: ReadonlySet<string> = Object.freeze(\n new Set(\n Object.entries(THEME_TOKEN_CONTRACT)\n .filter(([, entry]) => entry.type === 'color-hsl')\n .map(([token]) => token),\n ),\n) as ReadonlySet<string>;\n\n/**\n * A scheme colour (`type: 'color-hsl'`): `H S% L%`, single-spaced, no `hsl()`\n * wrapper. Saturation and lightness are additionally capped at 100\n * (`isSchemeColorValue`); hue is bounded only by its three digits.\n */\nconst SCHEME_COLOR_VALUE =\n /^(\\d{1,3}(?:\\.\\d+)?) (\\d{1,3}(?:\\.\\d+)?)% (\\d{1,3}(?:\\.\\d+)?)%$/;\n\n/**\n * A scheme length (`type: 'length'`, i.e. `--radius`): px/rem/em, unit REQUIRED\n * (`0px`, not `0`). A bare `0` would turn `calc(var(--radius) - 4px)` into\n * invalid CSS — see the `length` note in `tokenContract.ts`.\n */\nconst SCHEME_RADIUS_VALUE = /^\\d{1,4}(?:\\.\\d+)?(?:px|rem|em)$/;\n\n/**\n * Any other scheme value — a font stack, a key the contract has not catalogued:\n * no `;`, `{`, `}` or control character. The server's `themeOverrides` value\n * shape (`^[^;{}\\p{Cntrl}]{1,256}$`); the length is checked separately in\n * UTF-16 units, as the server counts, since a `u`-flag quantifier counts code\n * points.\n */\nconst SCHEME_LOOSE_VALUE = /^[^;{}\\p{Cc}]+$/u;\nconst MAX_SCHEME_VALUE_LENGTH = 256;\n\n/** The four the owner edits, in panel order. Equal to the contract's editable colour tokens. */\nexport const SCHEME_ANCHOR_TOKENS = [\n '--background',\n '--foreground',\n '--card',\n '--card-foreground',\n] as const;\n\nexport type SchemeAnchorToken = (typeof SCHEME_ANCHOR_TOKENS)[number];\n\n/** Upper bound on schemes per configuration. */\nexport const MAX_THEME_SCHEMES = 16;\n\nconst SCHEME_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,31}$/;\nconst MAX_SCHEME_NAME_LENGTH = 48;\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\n/**\n * Whether an untrusted value has a scheme's SHAPE.\n *\n * Token content is deliberately not judged here: `applyThemeOverrides` drops a\n * malformed key one entry at a time, exactly as it does for the flat store map\n * (and says so), so one bad token costs that token rather than the whole\n * palette. The typed value grammar is `isSchemeTokenEntry`, checked by writers.\n */\nexport function isThemeScheme(value: unknown): value is ThemeScheme {\n if (!isPlainObject(value)) {\n return false;\n }\n const { id, name, origin, tokens } = value;\n if (typeof id !== 'string' || !SCHEME_ID_PATTERN.test(id)) {\n return false;\n }\n if (\n name !== undefined &&\n (typeof name !== 'string' || name.length > MAX_SCHEME_NAME_LENGTH)\n ) {\n return false;\n }\n if (origin !== undefined && typeof origin !== 'string') {\n return false;\n }\n return (\n isPlainObject(tokens) &&\n Object.values(tokens).every((v) => typeof v === 'string')\n );\n}\n\n/**\n * The active scheme: the entry whose id is `activeThemeSchemeId`, else the\n * first entry, else null. Entries failing `isThemeScheme` are skipped first, so\n * a dangling id or a malformed entry falls through to the first usable one.\n */\nexport function resolveActiveScheme(\n cfg:\n | { themeSchemes?: unknown; activeThemeSchemeId?: string | null }\n | null\n | undefined,\n): ThemeScheme | null {\n if (!cfg || !Array.isArray(cfg.themeSchemes)) {\n return null;\n }\n const schemes = cfg.themeSchemes.filter(isThemeScheme);\n if (schemes.length === 0) {\n return null;\n }\n const activeId = cfg.activeThemeSchemeId;\n const active =\n typeof activeId === 'string'\n ? schemes.find((scheme) => scheme.id === activeId)\n : undefined;\n return active ?? schemes[0];\n}\n\n// ── Arithmetic copied from web50-shopify-adapter/src/theme-tokens.ts ────────\n\ninterface Rgb {\n r: number;\n g: number;\n b: number;\n}\n\nconst TRIPLET = /^(\\d{1,3}(?:\\.\\d+)?)\\s+(\\d{1,3}(?:\\.\\d+)?)%\\s+(\\d{1,3}(?:\\.\\d+)?)%$/;\n\n/**\n * A triplet as RGB in 0–255, NOT rounded to 8-bit — see the module header for\n * why. Same conversion as `colorFormat.hslTripletToHex`, minus its rounding.\n */\nfunction tripletToRgb(triplet: string): Rgb | null {\n const parts = TRIPLET.exec(triplet.trim());\n if (!parts || !isHslTriplet(triplet)) {\n return null;\n }\n const h = Number(parts[1]);\n const s = Number(parts[2]) / 100;\n const l = Number(parts[3]) / 100;\n\n const c = (1 - Math.abs(2 * l - 1)) * s;\n const x = c * (1 - Math.abs(((h / 60) % 2) - 1));\n const m = l - c / 2;\n const [r, g, b] =\n h < 60\n ? [c, x, 0]\n : h < 120\n ? [x, c, 0]\n : h < 180\n ? [0, c, x]\n : h < 240\n ? [0, x, c]\n : h < 300\n ? [x, 0, c]\n : [c, 0, x];\n return { r: (r + m) * 255, g: (g + m) * 255, b: (b + m) * 255 };\n}\n\n/** Adapter `composite` for an opaque-over-opaque mix, via its `mix`: channels rounded to 8-bit. */\nfunction mix(fg: Rgb, ratio: number, bg: Rgb): Rgb {\n return {\n r: Math.round(fg.r * ratio + bg.r * (1 - ratio)),\n g: Math.round(fg.g * ratio + bg.g * (1 - ratio)),\n b: Math.round(fg.b * ratio + bg.b * (1 - ratio)),\n };\n}\n\n/** Adapter `toTriplet`: `\"H S% L%\"`, integer-rounded. */\nfunction toTriplet({ r, g, b }: Rgb): string {\n const rn = r / 255;\n const gn = g / 255;\n const bn = b / 255;\n const max = Math.max(rn, gn, bn);\n const min = Math.min(rn, gn, bn);\n const l = (max + min) / 2;\n const delta = max - min;\n\n let h = 0;\n let s = 0;\n if (delta !== 0) {\n s = l > 0.5 ? delta / (2 - max - min) : delta / (max + min);\n if (max === rn) {\n h = (gn - bn) / delta + (gn < bn ? 6 : 0);\n } else if (max === gn) {\n h = (bn - rn) / delta + 2;\n } else {\n h = (rn - gn) / delta + 4;\n }\n h *= 60;\n }\n return `${Math.round(h)} ${Math.round(s * 100)}% ${Math.round(l * 100)}%`;\n}\n\n/** Tokens the adapter reads off the theme rather than computing from the anchors. */\nconst INHERITED_TOKENS = [\n '--primary',\n '--primary-foreground',\n '--secondary-foreground',\n '--accent-foreground',\n '--ring',\n '--border',\n '--input',\n '--heading',\n] as const;\n\n/**\n * A full colour-token map for a palette whose anchors the owner just edited.\n *\n * `anchors` win; everything the adapter derives from them is recomputed when\n * one of its inputs changed (and kept from `base` when none did — see \"Rounding\"\n * in the module header); everything it reads directly is taken from `base`.\n * Invalid triplets in either input are ignored, and a derived token whose\n * inputs are unavailable keeps `base`'s value, if any.\n *\n * Returns the whole scheme map, not only its colours: every non-colour entry of\n * `base` that passes `isSchemeTokenEntry` — `--radius`, a font stack, a token\n * this module does not know — is copied as is, so customizing a palette keeps\n * everything it states beyond colour. Every output entry passes\n * `isSchemeTokenEntry`.\n */\nexport function completeScheme(\n base: Record<string, string>,\n anchors: Partial<Record<SchemeAnchorToken, string>>,\n): Record<string, string> {\n // Both checks: `isHslTriplet` is what the arithmetic can convert, and the\n // scheme grammar is what a write accepts — failing either would poison a\n // derivation or get the whole save refused.\n const valid = (value: unknown): string | undefined =>\n typeof value === 'string' &&\n isHslTriplet(value) &&\n isSchemeColorValue(value.trim())\n ? value.trim()\n : undefined;\n const fromBase = (token: string): string | undefined => valid(base[token]);\n const fromAnchor = (token: SchemeAnchorToken): string | undefined =>\n valid(anchors[token]);\n\n const out: Record<string, string> = {};\n const put = (token: string, value: string | undefined): void => {\n if (value !== undefined) {\n out[token] = value;\n }\n };\n\n const background = fromAnchor('--background') ?? fromBase('--background');\n const foreground = fromAnchor('--foreground') ?? fromBase('--foreground');\n const bgChanged = background !== fromBase('--background');\n const fgChanged = foreground !== fromBase('--foreground');\n const bg = background === undefined ? null : tripletToRgb(background);\n const fg = foreground === undefined ? null : tripletToRgb(foreground);\n\n /** base's value while the inputs are unchanged, else the recomputation (else base's). */\n const derive = (\n token: string,\n inputsChanged: boolean,\n compute: () => string | undefined,\n ): string | undefined => {\n const kept = fromBase(token);\n return !inputsChanged && kept !== undefined ? kept : compute() ?? kept;\n };\n const mixOver = (over: Rgb | null, ratio: number) => (): string | undefined =>\n over !== null && bg !== null ? toTriplet(mix(over, ratio, bg)) : undefined;\n\n put('--background', background);\n put('--foreground', foreground);\n put(\n '--card',\n fromAnchor('--card') ??\n derive('--card', bgChanged || fgChanged, mixOver(fg, 0.04)),\n );\n put(\n '--card-foreground',\n fromAnchor('--card-foreground') ??\n derive('--card-foreground', fgChanged, () => foreground),\n );\n put('--popover', derive('--popover', bgChanged, () => background));\n put(\n '--popover-foreground',\n derive('--popover-foreground', fgChanged, () => foreground),\n );\n put('--muted', derive('--muted', bgChanged || fgChanged, mixOver(fg, 0.04)));\n put(\n '--muted-foreground',\n derive('--muted-foreground', bgChanged || fgChanged, mixOver(fg, 0.6)),\n );\n\n for (const token of INHERITED_TOKENS) {\n put(token, fromBase(token));\n }\n // Primary is inherited, so of the button family's inputs only the page can change.\n const primaryTriplet = out['--primary'];\n const primary =\n primaryTriplet === undefined ? null : tripletToRgb(primaryTriplet);\n put('--ring', out['--ring'] ?? primaryTriplet);\n put('--accent-foreground', out['--accent-foreground'] ?? primaryTriplet);\n put('--secondary', derive('--secondary', bgChanged, mixOver(primary, 0.1)));\n put('--accent', derive('--accent', bgChanged, mixOver(primary, 0.1)));\n\n // Not colours, so no arithmetic touches them: the palette keeps its shape,\n // its type, and any token a writer added that this module has never heard of.\n for (const [token, value] of Object.entries(base)) {\n if (!SCHEME_COLOR_TOKENS.has(token) && isSchemeTokenEntry(token, value)) {\n out[token] = value;\n }\n }\n\n return out;\n}\n\n/** The label the panel shows: the trimmed name, else `Palette N` (1-based). */\nexport function schemeDisplayName(scheme: ThemeScheme, index: number): string {\n const name = typeof scheme.name === 'string' ? scheme.name.trim() : '';\n return name !== '' ? name : `Palette ${index + 1}`;\n}\n\n/** `owner-N` with the smallest N ≥ 1 not already taken. */\nexport function nextOwnerSchemeId(schemes: readonly { id: string }[]): string {\n const taken = new Set(schemes.map((scheme) => scheme.id));\n let n = 1;\n while (taken.has(`owner-${n}`)) {\n n += 1;\n }\n return `owner-${n}`;\n}\n\n/** A colour value a scheme may hold: `SCHEME_COLOR_VALUE`, with S and L ≤ 100. */\nfunction isSchemeColorValue(value: string): boolean {\n const parts = SCHEME_COLOR_VALUE.exec(value);\n return parts !== null && Number(parts[2]) <= 100 && Number(parts[3]) <= 100;\n}\n\n/**\n * Whether a key/value pair may be written as a scheme token — the CLIENT's\n * contract, checked before the panel writes and by `completeScheme`.\n *\n * There is no allow-list: any key of the `themeOverrides` shape\n * (`THEME_OVERRIDE_KEY_PATTERN`) is a candidate. The value grammar follows the\n * key's contract type: `color-hsl` → an HSL triplet with S and L ≤ 100;\n * `length` → a px/rem/em length with its unit; anything else (font stacks,\n * keys the contract does not know) → the loose `themeOverrides` value shape.\n *\n * The server enforces only that loose shape for every key, so a `0 101% 50%`\n * colour or a unitless radius would be ACCEPTED there — and render wrong. That\n * is why the panel refuses them here. The renderer does not re-check values.\n */\nexport function isSchemeTokenEntry(key: string, value: unknown): boolean {\n if (typeof value !== 'string' || !THEME_OVERRIDE_KEY_PATTERN.test(key)) {\n return false;\n }\n switch (THEME_TOKEN_CONTRACT[key]?.type) {\n case 'color-hsl':\n return isSchemeColorValue(value);\n case 'length':\n return SCHEME_RADIUS_VALUE.test(value);\n default:\n return (\n value.length <= MAX_SCHEME_VALUE_LENGTH &&\n SCHEME_LOOSE_VALUE.test(value)\n );\n }\n}\n"],"mappings":";;;;;;;;;;AAsFA,IAAAA,YAAA,GAAAC,OAAA;AACA,IAAAC,cAAA,GAAAD,OAAA;AAvFA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAOA;;AAGA;;AAeA;AACO,MAAME,mBAAwC,GAAAC,OAAA,CAAAD,mBAAA,GAAGE,MAAM,CAACC,MAAM,CACnE,IAAIC,GAAG,CACLF,MAAM,CAACG,OAAO,CAACC,mCAAoB,CAAC,CACjCC,MAAM,CAAC,CAAC,GAAGC,KAAK,CAAC,KAAKA,KAAK,CAACC,IAAI,KAAK,WAAW,CAAC,CACjDC,GAAG,CAAC,CAAC,CAACC,KAAK,CAAC,KAAKA,KAAK,CAC3B,CACF,CAAwB;;AAExB;AACA;AACA;AACA;AACA;AACA,MAAMC,kBAAkB,GACtB,iEAAiE;;AAEnE;AACA;AACA;AACA;AACA;AACA,MAAMC,mBAAmB,GAAG,kCAAkC;;AAE9D;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,kBAAkB,GAAG,kBAAkB;AAC7C,MAAMC,uBAAuB,GAAG,GAAG;;AAEnC;AACO,MAAMC,oBAAoB,GAAAf,OAAA,CAAAe,oBAAA,GAAG,CAClC,cAAc,EACd,cAAc,EACd,QAAQ,EACR,mBAAmB,CACX;AAIV;AACO,MAAMC,iBAAiB,GAAAhB,OAAA,CAAAgB,iBAAA,GAAG,EAAE;AAEnC,MAAMC,iBAAiB,GAAG,2BAA2B;AACrD,MAAMC,sBAAsB,GAAG,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","ignoreList":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
exports.__esModule = true;
|
|
4
|
-
exports.TOKEN_NAME_PATTERN = exports.THEME_TOKEN_CONTRACT = exports.EDITABLE_TOKENS = exports.BRAND_TOKENS = void 0;
|
|
4
|
+
exports.TOKEN_NAME_PATTERN = exports.THEME_TOKEN_CONTRACT = exports.THEME_OVERRIDE_KEY_PATTERN = exports.EDITABLE_TOKENS = exports.BRAND_TOKENS = void 0;
|
|
5
5
|
exports.bucketOf = bucketOf;
|
|
6
6
|
exports.hostAliasFor = hostAliasFor;
|
|
7
7
|
/**
|
|
@@ -172,6 +172,13 @@ const BRAND_TOKENS = exports.BRAND_TOKENS = Object.freeze(new Set(Object.entries
|
|
|
172
172
|
*/
|
|
173
173
|
const TOKEN_NAME_PATTERN = exports.TOKEN_NAME_PATTERN = /^--[a-z0-9-]+$/;
|
|
174
174
|
|
|
175
|
+
/**
|
|
176
|
+
* The key shape the server accepts in `themeOverrides`, `userThemeOverrides`
|
|
177
|
+
* and a theme scheme's `tokens` (its `Consts.ThemeOverrideKeyPattern`). Stricter
|
|
178
|
+
* than `TOKEN_NAME_PATTERN`: a letter after the dashes, at most 65 characters.
|
|
179
|
+
*/
|
|
180
|
+
const THEME_OVERRIDE_KEY_PATTERN = exports.THEME_OVERRIDE_KEY_PATTERN = /^--[a-z][a-z0-9-]{0,62}$/;
|
|
181
|
+
|
|
175
182
|
/**
|
|
176
183
|
* What the owner's panel offers, in the order it offers it.
|
|
177
184
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["brandColor","label","bucket","type","editable","undefined","THEME_TOKEN_CONTRACT","exports","Object","freeze","BRAND_TOKENS","Set","entries","filter","entry","map","token","TOKEN_NAME_PATTERN","EDITABLE_TOKENS","hostAliasFor","slice","bucketOf","_THEME_TOKEN_CONTRACT"],"sources":["../../../src/theme/tokenContract.ts"],"sourcesContent":["/**\n * The theme-token contract (DL #193).\n *\n * One home for the answer to \"who is allowed to set this token, and who wins\n * when more than one of them does\". Three things need that answer and must not\n * each keep their own copy:\n *\n * - **`applyThemeOverrides`** decides, per key, whether to write the real\n * token or its `--web5-host-*` alias.\n * - **the seed's `validate-templates`** fails a template that sets a brand\n * token.\n * - **the shop owner's panel** renders a control per `editable` entry.\n *\n * A leaf module beside `hostScope.ts`, and for the same reason: both are data\n * that several packages must agree on, and both have to be readable from plain\n * Node tooling. Requiring this package's ENTRY from Node fails — the CSS import\n * in it is a syntax error to the CJS loader, which is what `scope-css.ts`\n * documents — but a deep import of a leaf like this one works, so the seed's\n * validator can read it without loading a component library.\n *\n * It was briefly its own package. That was justified by `embed-loader` needing\n * the list without taking core's eleven dependencies — and the loader turned\n * out not to need it at all, because `applyThemeOverrides` has owned runtime\n * token injection here since DL #131.\n *\n * ## The two buckets\n *\n * The split is about who WINS, not about what may be imported. A platform\n * adapter is free to read anything the store states; the bucket decides what\n * happens when a template has an opinion about the same token.\n *\n * - `brand` — the store's identity: colour roles and font families. The\n * imported value is written as the real token, last, so it wins\n * unconditionally. A template is not permitted to set these.\n * - `host` — the store's shape: corner radius today. The imported value is\n * written as `--web5-host-<name>` and consumed by core as a `var()`\n * fallback, so a template stating the real token beats it and a template\n * that stays silent inherits the store's.\n *\n * A token absent from this map is treated as `host`. That is deliberate and\n * safe: an alias nothing consumes renders nothing, so an adapter that learns\n * to read a token core has never heard of cannot override a template by\n * accident. The wiring line in core is the real gate for a host token, not the\n * entry here.\n *\n * ## Why all but one are brand\n *\n * This is not a new taxonomy. Twenty of these are the set\n * `web50-shopify-adapter` already emits, with a bucket attached; the\n * twenty-first, `--heading`, has been in core's catalogue since DL #131 and is\n * consumed by the seed, but no adapter fills it yet. Exactly one token changes\n * hands relative to the behaviour before DL #193 — `--radius`, which used to\n * win against the template that had chosen a different one. That single\n * misfiling is the whole of the bug this contract exists to fix.\n */\n\n/** How a value is spelled, which is what makes validation possible. */\nexport type TokenType =\n /** An HSL triplet with no `hsl()` wrapper — `0 0% 9%`. The core bridge wraps it. */\n | 'color-hsl'\n /** A CSS font stack — `'Poppins', ui-sans-serif, sans-serif`. */\n | 'font-stack'\n /** A CSS length. MUST carry a unit: a bare `0` turns `calc(var(--radius) - 4px)`\n * into a type error and takes the derived radius scale out entirely. */\n | 'length';\n\n/** Which layer wins when both a store and a template state this token. */\nexport type TokenBucket = 'brand' | 'host';\n\nexport interface TokenContractEntry {\n bucket: TokenBucket;\n type: TokenType;\n /** May the shop owner set this from the panel? */\n editable: boolean;\n /** Shown in the owner's panel. Absent for entries that are not editable. */\n label?: string;\n}\n\n/**\n * A colour role. Giving one a label is what puts it in the owner's panel.\n *\n * The two travel together on purpose: `editable` without a label is a control\n * with no name, and a label on a token nobody may set is a promise the panel\n * cannot keep. One argument, one decision.\n *\n * Labels are the merchant's words rather than the token's — \"Card background\",\n * not \"card\". Someone choosing their shop's colours is not reading a design\n * system, and `--primary-foreground` is not a colour anyone picks: it is\n * whatever stays legible ON primary, which the theme derives. Exposing the\n * derived half is how a panel produces white text on a white button.\n *\n * Only FOUR carry a label (product request, 2026-09-09): the page's surface and\n * text, and a card's. They are the four a shop owner can point at on the page\n * and name without being taught the vocabulary, and — unlike `--secondary`,\n * `--accent` or `--muted`, which the importer MIXES from the anchors — they are\n * independent of each other, so setting one cannot contradict the arithmetic\n * that produced another. Everything else stays import-only: the store still\n * fills it, the panel just does not offer it.\n */\nconst brandColor = (label?: string): TokenContractEntry => ({\n bucket: 'brand',\n type: 'color-hsl',\n editable: label !== undefined,\n ...(label === undefined ? {} : { label }),\n});\n\n/**\n * Every token a platform adapter may meaningfully emit today.\n *\n * Growth is adapter-driven on purpose: a token joins when an adapter can\n * actually read it, not on spec. `theme_overrides` is capped at 32 entries by\n * its proto, so there are twelve slots left and no reason to spend them on\n * speculation. `--spacing` is the tempting next one and should be resisted —\n * it scales every `p-*`, `m-*` and `gap-*` in the bundle from one number.\n */\nexport const THEME_TOKEN_CONTRACT: Readonly<Record<string, TokenContractEntry>> =\n Object.freeze({\n // ── brand · colour roles (18) ────────────────────────────────────────────\n '--background': brandColor('Background color'),\n '--foreground': brandColor('Text color'),\n '--card': brandColor('Card background'),\n '--card-foreground': brandColor('Card text'),\n '--popover': brandColor(),\n '--popover-foreground': brandColor(),\n '--primary': brandColor(),\n '--primary-foreground': brandColor(),\n '--secondary': brandColor(),\n '--secondary-foreground': brandColor(),\n '--muted': brandColor(),\n '--muted-foreground': brandColor(),\n '--accent': brandColor(),\n '--accent-foreground': brandColor(),\n '--border': brandColor(),\n '--input': brandColor(),\n '--ring': brandColor(),\n\n /**\n * Brand-shaped and supported end to end, but no adapter fills it yet: the\n * seed consumes it (`--color-heading-fg: hsl(var(--heading, var(--foreground)))`)\n * and core has catalogued it since DL #131, while `web50-shopify-adapter`\n * emits the other twenty. A store that states a distinct heading colour has\n * somewhere for it to go the moment an adapter learns to read one.\n */\n '--heading': brandColor(),\n\n // ── brand · type (2) ─────────────────────────────────────────────────────\n '--font-sans': { bucket: 'brand', type: 'font-stack', editable: false },\n '--font-display': { bucket: 'brand', type: 'font-stack', editable: false },\n\n // ── host · shape (1) ─────────────────────────────────────────────────────\n /**\n * The one token whose bucket changed, and the reason this contract exists.\n * A store's imported radius still reaches the page as `--web5-host-radius`\n * and a template still beats it; what changed is that the OWNER no longer\n * gets a control for it — the panel is four colours now, and shape is not\n * one of them.\n */\n '--radius': {\n bucket: 'host',\n type: 'length',\n editable: false,\n },\n });\n\n/**\n * The tokens written directly, which is the only question the loader has to\n * answer per key — everything else takes the alias path.\n */\nexport const BRAND_TOKENS: ReadonlySet<string> = Object.freeze(\n new Set(\n Object.entries(THEME_TOKEN_CONTRACT)\n .filter(([, entry]) => entry.bucket === 'brand')\n .map(([token]) => token),\n ),\n) as ReadonlySet<string>;\n\n/**\n * A token name we are willing to compose into a CSS declaration.\n *\n * The keys reaching the loader came out of a backend map, which came out of an\n * adapter reading a merchant's theme file. A key carrying `:` or `;` would\n * write arbitrary declarations onto the mount, so the shape is checked where\n * the declaration is built rather than trusted from the source.\n */\nexport const TOKEN_NAME_PATTERN = /^--[a-z0-9-]+$/;\n\n/**\n * What the owner's panel offers, in the order it offers it.\n *\n * Derived from the contract rather than listed again beside it, so a token\n * cannot be editable in one place and absent from the other. The order is the\n * declaration order above, which reads outside-in — the page, then its text,\n * then the card drawn on it, then the card's text — and is a better first\n * impression than alphabetical or than the order the tokens happen to be\n * defined for CSS.\n */\nexport const EDITABLE_TOKENS: readonly (readonly [\n token: string,\n entry: TokenContractEntry,\n])[] = Object.freeze(\n Object.entries(THEME_TOKEN_CONTRACT).filter(([, entry]) => entry.editable),\n) as readonly (readonly [string, TokenContractEntry])[];\n\n/** `--radius` → `--web5-host-radius`. Derived, never stored: one fewer thing to get wrong. */\nexport function hostAliasFor(token: string): string {\n return `--web5-host-${token.slice(2)}`;\n}\n\n/** Whether this token is written directly (brand) or as an alias (host, and anything unknown). */\nexport function bucketOf(token: string): TokenBucket {\n return THEME_TOKEN_CONTRACT[token]?.bucket ?? 'host';\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["brandColor","label","bucket","type","editable","undefined","THEME_TOKEN_CONTRACT","exports","Object","freeze","BRAND_TOKENS","Set","entries","filter","entry","map","token","TOKEN_NAME_PATTERN","THEME_OVERRIDE_KEY_PATTERN","EDITABLE_TOKENS","hostAliasFor","slice","bucketOf","_THEME_TOKEN_CONTRACT"],"sources":["../../../src/theme/tokenContract.ts"],"sourcesContent":["/**\n * The theme-token contract (DL #193).\n *\n * One home for the answer to \"who is allowed to set this token, and who wins\n * when more than one of them does\". Three things need that answer and must not\n * each keep their own copy:\n *\n * - **`applyThemeOverrides`** decides, per key, whether to write the real\n * token or its `--web5-host-*` alias.\n * - **the seed's `validate-templates`** fails a template that sets a brand\n * token.\n * - **the shop owner's panel** renders a control per `editable` entry.\n *\n * A leaf module beside `hostScope.ts`, and for the same reason: both are data\n * that several packages must agree on, and both have to be readable from plain\n * Node tooling. Requiring this package's ENTRY from Node fails — the CSS import\n * in it is a syntax error to the CJS loader, which is what `scope-css.ts`\n * documents — but a deep import of a leaf like this one works, so the seed's\n * validator can read it without loading a component library.\n *\n * It was briefly its own package. That was justified by `embed-loader` needing\n * the list without taking core's eleven dependencies — and the loader turned\n * out not to need it at all, because `applyThemeOverrides` has owned runtime\n * token injection here since DL #131.\n *\n * ## The two buckets\n *\n * The split is about who WINS, not about what may be imported. A platform\n * adapter is free to read anything the store states; the bucket decides what\n * happens when a template has an opinion about the same token.\n *\n * - `brand` — the store's identity: colour roles and font families. The\n * imported value is written as the real token, last, so it wins\n * unconditionally. A template is not permitted to set these.\n * - `host` — the store's shape: corner radius today. The imported value is\n * written as `--web5-host-<name>` and consumed by core as a `var()`\n * fallback, so a template stating the real token beats it and a template\n * that stays silent inherits the store's.\n *\n * A token absent from this map is treated as `host`. That is deliberate and\n * safe: an alias nothing consumes renders nothing, so an adapter that learns\n * to read a token core has never heard of cannot override a template by\n * accident. The wiring line in core is the real gate for a host token, not the\n * entry here.\n *\n * ## Why all but one are brand\n *\n * This is not a new taxonomy. Twenty of these are the set\n * `web50-shopify-adapter` already emits, with a bucket attached; the\n * twenty-first, `--heading`, has been in core's catalogue since DL #131 and is\n * consumed by the seed, but no adapter fills it yet. Exactly one token changes\n * hands relative to the behaviour before DL #193 — `--radius`, which used to\n * win against the template that had chosen a different one. That single\n * misfiling is the whole of the bug this contract exists to fix.\n */\n\n/** How a value is spelled, which is what makes validation possible. */\nexport type TokenType =\n /** An HSL triplet with no `hsl()` wrapper — `0 0% 9%`. The core bridge wraps it. */\n | 'color-hsl'\n /** A CSS font stack — `'Poppins', ui-sans-serif, sans-serif`. */\n | 'font-stack'\n /** A CSS length. MUST carry a unit: a bare `0` turns `calc(var(--radius) - 4px)`\n * into a type error and takes the derived radius scale out entirely. */\n | 'length';\n\n/** Which layer wins when both a store and a template state this token. */\nexport type TokenBucket = 'brand' | 'host';\n\nexport interface TokenContractEntry {\n bucket: TokenBucket;\n type: TokenType;\n /** May the shop owner set this from the panel? */\n editable: boolean;\n /** Shown in the owner's panel. Absent for entries that are not editable. */\n label?: string;\n}\n\n/**\n * A colour role. Giving one a label is what puts it in the owner's panel.\n *\n * The two travel together on purpose: `editable` without a label is a control\n * with no name, and a label on a token nobody may set is a promise the panel\n * cannot keep. One argument, one decision.\n *\n * Labels are the merchant's words rather than the token's — \"Card background\",\n * not \"card\". Someone choosing their shop's colours is not reading a design\n * system, and `--primary-foreground` is not a colour anyone picks: it is\n * whatever stays legible ON primary, which the theme derives. Exposing the\n * derived half is how a panel produces white text on a white button.\n *\n * Only FOUR carry a label (product request, 2026-09-09): the page's surface and\n * text, and a card's. They are the four a shop owner can point at on the page\n * and name without being taught the vocabulary, and — unlike `--secondary`,\n * `--accent` or `--muted`, which the importer MIXES from the anchors — they are\n * independent of each other, so setting one cannot contradict the arithmetic\n * that produced another. Everything else stays import-only: the store still\n * fills it, the panel just does not offer it.\n */\nconst brandColor = (label?: string): TokenContractEntry => ({\n bucket: 'brand',\n type: 'color-hsl',\n editable: label !== undefined,\n ...(label === undefined ? {} : { label }),\n});\n\n/**\n * Every token a platform adapter may meaningfully emit today.\n *\n * Growth is adapter-driven on purpose: a token joins when an adapter can\n * actually read it, not on spec. `theme_overrides` is capped at 32 entries by\n * its proto, so there are twelve slots left and no reason to spend them on\n * speculation. `--spacing` is the tempting next one and should be resisted —\n * it scales every `p-*`, `m-*` and `gap-*` in the bundle from one number.\n */\nexport const THEME_TOKEN_CONTRACT: Readonly<Record<string, TokenContractEntry>> =\n Object.freeze({\n // ── brand · colour roles (18) ────────────────────────────────────────────\n '--background': brandColor('Background color'),\n '--foreground': brandColor('Text color'),\n '--card': brandColor('Card background'),\n '--card-foreground': brandColor('Card text'),\n '--popover': brandColor(),\n '--popover-foreground': brandColor(),\n '--primary': brandColor(),\n '--primary-foreground': brandColor(),\n '--secondary': brandColor(),\n '--secondary-foreground': brandColor(),\n '--muted': brandColor(),\n '--muted-foreground': brandColor(),\n '--accent': brandColor(),\n '--accent-foreground': brandColor(),\n '--border': brandColor(),\n '--input': brandColor(),\n '--ring': brandColor(),\n\n /**\n * Brand-shaped and supported end to end, but no adapter fills it yet: the\n * seed consumes it (`--color-heading-fg: hsl(var(--heading, var(--foreground)))`)\n * and core has catalogued it since DL #131, while `web50-shopify-adapter`\n * emits the other twenty. A store that states a distinct heading colour has\n * somewhere for it to go the moment an adapter learns to read one.\n */\n '--heading': brandColor(),\n\n // ── brand · type (2) ─────────────────────────────────────────────────────\n '--font-sans': { bucket: 'brand', type: 'font-stack', editable: false },\n '--font-display': { bucket: 'brand', type: 'font-stack', editable: false },\n\n // ── host · shape (1) ─────────────────────────────────────────────────────\n /**\n * The one token whose bucket changed, and the reason this contract exists.\n * A store's imported radius still reaches the page as `--web5-host-radius`\n * and a template still beats it; what changed is that the OWNER no longer\n * gets a control for it — the panel is four colours now, and shape is not\n * one of them.\n */\n '--radius': {\n bucket: 'host',\n type: 'length',\n editable: false,\n },\n });\n\n/**\n * The tokens written directly, which is the only question the loader has to\n * answer per key — everything else takes the alias path.\n */\nexport const BRAND_TOKENS: ReadonlySet<string> = Object.freeze(\n new Set(\n Object.entries(THEME_TOKEN_CONTRACT)\n .filter(([, entry]) => entry.bucket === 'brand')\n .map(([token]) => token),\n ),\n) as ReadonlySet<string>;\n\n/**\n * A token name we are willing to compose into a CSS declaration.\n *\n * The keys reaching the loader came out of a backend map, which came out of an\n * adapter reading a merchant's theme file. A key carrying `:` or `;` would\n * write arbitrary declarations onto the mount, so the shape is checked where\n * the declaration is built rather than trusted from the source.\n */\nexport const TOKEN_NAME_PATTERN = /^--[a-z0-9-]+$/;\n\n/**\n * The key shape the server accepts in `themeOverrides`, `userThemeOverrides`\n * and a theme scheme's `tokens` (its `Consts.ThemeOverrideKeyPattern`). Stricter\n * than `TOKEN_NAME_PATTERN`: a letter after the dashes, at most 65 characters.\n */\nexport const THEME_OVERRIDE_KEY_PATTERN = /^--[a-z][a-z0-9-]{0,62}$/;\n\n/**\n * What the owner's panel offers, in the order it offers it.\n *\n * Derived from the contract rather than listed again beside it, so a token\n * cannot be editable in one place and absent from the other. The order is the\n * declaration order above, which reads outside-in — the page, then its text,\n * then the card drawn on it, then the card's text — and is a better first\n * impression than alphabetical or than the order the tokens happen to be\n * defined for CSS.\n */\nexport const EDITABLE_TOKENS: readonly (readonly [\n token: string,\n entry: TokenContractEntry,\n])[] = Object.freeze(\n Object.entries(THEME_TOKEN_CONTRACT).filter(([, entry]) => entry.editable),\n) as readonly (readonly [string, TokenContractEntry])[];\n\n/** `--radius` → `--web5-host-radius`. Derived, never stored: one fewer thing to get wrong. */\nexport function hostAliasFor(token: string): string {\n return `--web5-host-${token.slice(2)}`;\n}\n\n/** Whether this token is written directly (brand) or as an alias (host, and anything unknown). */\nexport function bucketOf(token: string): TokenBucket {\n return THEME_TOKEN_CONTRACT[token]?.bucket ?? 'host';\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -38,12 +38,29 @@
|
|
|
38
38
|
* and set a value has already settled that argument, so aliasing theirs would
|
|
39
39
|
* let the template overrule the person who chose. They are written last, so a
|
|
40
40
|
* token both maps carry resolves to the owner's value.
|
|
41
|
+
* - **The active theme scheme sits between the two**: `schemeTokens` is the
|
|
42
|
+
* resolved entry of `Configuration.themeSchemes` (see `theme/themeScheme`).
|
|
43
|
+
* It is written after the store map and before the owner's overrides, under
|
|
44
|
+
* the SAME bucket rule as the store map: a `brand` token (colours, fonts) as
|
|
45
|
+
* the real token, anything else — `--radius`, a key the contract has never
|
|
46
|
+
* heard of — as its `--web5-host-*` alias. Being the later write of either
|
|
47
|
+
* property, the chosen palette beats the flat store map on colours and on
|
|
48
|
+
* radius alike — yet a template that states `--radius` still wins on shape,
|
|
49
|
+
* exactly as it does over an imported radius. A scheme is written like the
|
|
50
|
+
* store map it stands in for, not like an owner override, so it gets no
|
|
51
|
+
* exemption from aliasing; the owner's layer still wins over it. Its entries
|
|
52
|
+
* are filtered exactly like the store map's too: the same key check, the same
|
|
53
|
+
* warning, and no per-type value check — `setProperty` keeps any value inert,
|
|
54
|
+
* and the typed grammar (`isSchemeTokenEntry`) is the panel's to enforce
|
|
55
|
+
* before a write. So the scheme token set is as open as `themeOverrides`: a
|
|
56
|
+
* new scheme token needs no release here. Absent → exactly the pre-scheme
|
|
57
|
+
* behaviour.
|
|
41
58
|
* - **Idempotent**: one marker element per document, replaced wholesale on
|
|
42
|
-
* re-apply; empty/absent input removes it.
|
|
59
|
+
* re-apply; empty/absent input (all three layers) removes it.
|
|
43
60
|
*/
|
|
44
61
|
import { WEB5_SCOPE } from '../hostScope.js';
|
|
45
62
|
import { traceThemeOverrides } from './themeDebug.js';
|
|
46
|
-
import { THEME_TOKEN_CONTRACT, bucketOf, hostAliasFor } from '../theme/tokenContract.js';
|
|
63
|
+
import { THEME_OVERRIDE_KEY_PATTERN, THEME_TOKEN_CONTRACT, bucketOf, hostAliasFor } from '../theme/tokenContract.js';
|
|
47
64
|
|
|
48
65
|
/** A themeOverrides key: always a CSS custom property. */
|
|
49
66
|
|
|
@@ -64,30 +81,38 @@ import { THEME_TOKEN_CONTRACT, bucketOf, hostAliasFor } from '../theme/tokenCont
|
|
|
64
81
|
* who wins when a store and a template disagree.
|
|
65
82
|
*/
|
|
66
83
|
export const THEME_OVERRIDE_TOKENS = new Set(Object.keys(THEME_TOKEN_CONTRACT));
|
|
67
|
-
|
|
68
|
-
/** Mirrors the server's Consts.ThemeOverrideKeyPattern. */
|
|
69
|
-
const KEY_PATTERN = /^--[a-z][a-z0-9-]{0,62}$/;
|
|
70
84
|
const MARKER_ATTR = 'data-web5-theme-overrides';
|
|
71
85
|
|
|
72
|
-
/**
|
|
86
|
+
/**
|
|
87
|
+
* Drops keys that are not well-formed custom properties, warning about each.
|
|
88
|
+
* Used for all three layers — store, scheme and owner.
|
|
89
|
+
*/
|
|
73
90
|
function wellFormedEntries(overrides) {
|
|
74
91
|
return Object.entries(overrides ?? {}).filter(_ref => {
|
|
75
92
|
let [key] = _ref;
|
|
76
|
-
const wellFormed =
|
|
93
|
+
const wellFormed = THEME_OVERRIDE_KEY_PATTERN.test(key);
|
|
77
94
|
if (!wellFormed) {
|
|
78
95
|
console.warn(`[web5-theme] Skipping malformed theme override key: ${key}`);
|
|
79
96
|
}
|
|
80
97
|
return wellFormed;
|
|
81
98
|
});
|
|
82
99
|
}
|
|
83
|
-
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Where a store-side token lands: a `brand` token as itself, so the store's
|
|
103
|
+
* identity beats the template's; everything else as its `--web5-host-*` alias,
|
|
104
|
+
* which core consumes only as a fallback the template can beat.
|
|
105
|
+
*/
|
|
106
|
+
const bucketed = key => bucketOf(key) === 'brand' ? key : hostAliasFor(key);
|
|
107
|
+
export function applyThemeOverrides(overrides, userOverrides, schemeTokens) {
|
|
84
108
|
if (typeof document === 'undefined') {
|
|
85
109
|
return;
|
|
86
110
|
}
|
|
87
111
|
const existing = document.head.querySelector(`style[${MARKER_ATTR}]`);
|
|
88
112
|
const entries = wellFormedEntries(overrides);
|
|
89
113
|
const userEntries = wellFormedEntries(userOverrides);
|
|
90
|
-
|
|
114
|
+
const activeSchemeEntries = wellFormedEntries(schemeTokens);
|
|
115
|
+
if (entries.length === 0 && activeSchemeEntries.length === 0 && userEntries.length === 0) {
|
|
91
116
|
existing == null || existing.remove();
|
|
92
117
|
// Nothing to apply is itself a traceable answer: it means every token on
|
|
93
118
|
// the page is the template's, which is otherwise indistinguishable from
|
|
@@ -124,7 +149,14 @@ export function applyThemeOverrides(overrides, userOverrides) {
|
|
|
124
149
|
// Brand wins over the template, so it is written as the token the
|
|
125
150
|
// stylesheets actually read. Everything else lands in the host namespace,
|
|
126
151
|
// where core consumes it only as a fallback the template can beat.
|
|
127
|
-
write(key, value,
|
|
152
|
+
write(key, value, bucketed(key), 'store');
|
|
153
|
+
}
|
|
154
|
+
// The active scheme over the store map, bucketed the same way: a brand token
|
|
155
|
+
// as itself, `--radius` and unknown keys as their alias. Either way this is
|
|
156
|
+
// the later write of that property, so it beats the store's — and a
|
|
157
|
+
// template's `--radius` still beats the alias.
|
|
158
|
+
for (const [key, value] of activeSchemeEntries) {
|
|
159
|
+
write(key, value, bucketed(key), 'scheme');
|
|
128
160
|
}
|
|
129
161
|
// Owner choices last and never aliased: within one declaration block the last
|
|
130
162
|
// write of a property wins, so a token both maps carry ends up the owner's.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["WEB5_SCOPE","traceThemeOverrides","THEME_TOKEN_CONTRACT","bucketOf","hostAliasFor","THEME_OVERRIDE_TOKENS","Set","Object","keys","KEY_PATTERN","MARKER_ATTR","wellFormedEntries","overrides","entries","filter","_ref","key","wellFormed","test","console","warn","applyThemeOverrides","userOverrides","document","existing","head","querySelector","userEntries","length","remove","style","createElement","setAttribute","appendChild","sheet","insertRule","rule","cssRules","applied","write","value","target","source","setProperty","push","token","writtenAs"],"sources":["../../../src/client/applyThemeOverrides.ts"],"sourcesContent":["/**\n * Runtime theme token injection (DL #131).\n *\n * `Configuration.themeOverrides` carries per-store CSS custom-property\n * overrides (`--primary`, `--radius`, `--heading`, ...) computed from the\n * store's own theme (e.g. the Shopify `settings_data.json` extractor). This\n * applies them to the page so they win over the bundle's baked `theme.css`:\n *\n * - **Scoped, not `:root`**: the rule targets `WEB5_SCOPE`, the same\n * `:is(#root, .w5-root, [data-web5-placement], .debug-view)` selector every\n * compiled bundle stylesheet uses — host-page styling is never touched, and\n * equal specificity + later document order makes the override win. Callers\n * must therefore apply AFTER the client bundle's CSS is in the document.\n * - **Bucketed, since DL #193**: a `brand` token (colour roles, font families)\n * is written as itself, so the store's identity beats the template's. Every\n * other key — `host` tokens and anything the contract has never heard of —\n * is written as its `--web5-host-*` alias, which core's stylesheets consume\n * as a `var()` fallback. A template stating the real token therefore wins on\n * shape, and an unknown key is inert rather than dangerous: a custom property\n * nothing references renders nothing, so an adapter that learns to read a new\n * token cannot silently override a template.\n * - **The token set stays open, but the destination changed.** Before DL #193\n * an unrecognised key was applied verbatim, so a template could consume\n * `var(--foo)` and grow a token with no release. It now arrives as\n * `--web5-host-foo`, so the template consumes `var(--web5-host-foo, <default>)`\n * instead. One line different, and the provenance is explicit — the wiring\n * line is what activates a host token, not the contract entry.\n * - The mandatory `--` prefix means an override can only define custom\n * properties — it can never set a real CSS property inside the scope. The\n * server enforces the same shape (plus value sanitation) at write time.\n * - **Values are inert**: entries are written with\n * `CSSStyleDeclaration.setProperty`, never string-concatenated into CSS, so\n * a hostile value cannot terminate the declaration or open a new rule.\n * - **The shop owner is the last word (DL #193)**: `userOverrides` carries the\n * choices a human made in the owner panel, and every one of them is written\n * as the real token whatever its bucket. Bucketing exists to arbitrate a\n * disagreement between a store and a template; an owner who opened a panel\n * and set a value has already settled that argument, so aliasing theirs would\n * let the template overrule the person who chose. They are written last, so a\n * token both maps carry resolves to the owner's value.\n * - **Idempotent**: one marker element per document, replaced wholesale on\n * re-apply; empty/absent input removes it.\n */\nimport { WEB5_SCOPE } from '../hostScope';\nimport {\n traceThemeOverrides,\n type AppliedToken,\n type ThemeOverrideSource,\n} from './themeDebug';\nimport {\n THEME_TOKEN_CONTRACT,\n bucketOf,\n hostAliasFor,\n} from '../theme/tokenContract';\n\n/** A themeOverrides key: always a CSS custom property. */\nexport type ThemeOverrideKey = `--${string}`;\n\n/**\n * Writer-side shape for a theme override map. The catalog below documents\n * the tokens the seed consumes today; the type stays open so new tokens\n * flow end-to-end with no release anywhere but the seed.\n */\nexport type ThemeOverrides = Record<ThemeOverrideKey, string>;\n\n/**\n * The known token catalog. GUIDANCE, NOT A GATE — `applyThemeOverrides` still\n * applies any well-formed custom property, so the vocabulary can grow without a\n * release here.\n *\n * Derived from `@wix/web5-token-contract` rather than restated, because two\n * hand-maintained copies of one list is how they drift. The contract also\n * carries what this Set cannot: which bucket each token is in, and therefore\n * who wins when a store and a template disagree.\n */\nexport const THEME_OVERRIDE_TOKENS: ReadonlySet<string> = new Set(\n Object.keys(THEME_TOKEN_CONTRACT),\n);\n\n/** Mirrors the server's Consts.ThemeOverrideKeyPattern. */\nconst KEY_PATTERN = /^--[a-z][a-z0-9-]{0,62}$/;\n\nconst MARKER_ATTR = 'data-web5-theme-overrides';\n\n/** Drops keys that are not well-formed custom properties, warning about each. */\nfunction wellFormedEntries(\n overrides?: Record<string, string> | null,\n): [string, string][] {\n return Object.entries(overrides ?? {}).filter(([key]) => {\n const wellFormed = KEY_PATTERN.test(key);\n if (!wellFormed) {\n console.warn(\n `[web5-theme] Skipping malformed theme override key: ${key}`,\n );\n }\n return wellFormed;\n });\n}\n\nexport function applyThemeOverrides(\n overrides?: Record<string, string> | null,\n userOverrides?: Record<string, string> | null,\n): void {\n if (typeof document === 'undefined') {\n return;\n }\n\n const existing = document.head.querySelector(`style[${MARKER_ATTR}]`);\n const entries = wellFormedEntries(overrides);\n const userEntries = wellFormedEntries(userOverrides);\n\n if (entries.length === 0 && userEntries.length === 0) {\n existing?.remove();\n // Nothing to apply is itself a traceable answer: it means every token on\n // the page is the template's, which is otherwise indistinguishable from\n // tracing having failed.\n traceThemeOverrides([]);\n return;\n }\n\n const style = document.createElement('style');\n style.setAttribute(MARKER_ATTR, '');\n document.head.appendChild(style);\n const sheet = style.sheet;\n if (!sheet) {\n style.remove();\n return;\n }\n sheet.insertRule(`${WEB5_SCOPE} {}`, 0);\n const rule = sheet.cssRules[0] as CSSStyleRule;\n const applied: AppliedToken[] = [];\n const write = (\n key: string,\n value: string,\n target: string,\n source: ThemeOverrideSource,\n ): void => {\n try {\n rule.style.setProperty(target, value);\n applied.push({ token: key, value, writtenAs: target, source });\n } catch {\n // An engine that rejects the value leaves the token at its baked\n // default — degraded theming, never broken CSS.\n }\n };\n\n for (const [key, value] of entries) {\n // Brand wins over the template, so it is written as the token the\n // stylesheets actually read. Everything else lands in the host namespace,\n // where core consumes it only as a fallback the template can beat.\n write(\n key,\n value,\n bucketOf(key) === 'brand' ? key : hostAliasFor(key),\n 'store',\n );\n }\n // Owner choices last and never aliased: within one declaration block the last\n // write of a property wins, so a token both maps carry ends up the owner's.\n for (const [key, value] of userEntries) {\n write(key, value, key, 'user');\n }\n // Replace-on-reapply: the fresh element is appended (so it stays last in\n // document order) before the stale one is dropped.\n existing?.remove();\n // AFTER the stale element is gone, so the resolved values traced below are\n // the ones the page will actually render. Off unless explicitly enabled.\n traceThemeOverrides(applied);\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["WEB5_SCOPE","traceThemeOverrides","THEME_OVERRIDE_KEY_PATTERN","THEME_TOKEN_CONTRACT","bucketOf","hostAliasFor","THEME_OVERRIDE_TOKENS","Set","Object","keys","MARKER_ATTR","wellFormedEntries","overrides","entries","filter","_ref","key","wellFormed","test","console","warn","bucketed","applyThemeOverrides","userOverrides","schemeTokens","document","existing","head","querySelector","userEntries","activeSchemeEntries","length","remove","style","createElement","setAttribute","appendChild","sheet","insertRule","rule","cssRules","applied","write","value","target","source","setProperty","push","token","writtenAs"],"sources":["../../../src/client/applyThemeOverrides.ts"],"sourcesContent":["/**\n * Runtime theme token injection (DL #131).\n *\n * `Configuration.themeOverrides` carries per-store CSS custom-property\n * overrides (`--primary`, `--radius`, `--heading`, ...) computed from the\n * store's own theme (e.g. the Shopify `settings_data.json` extractor). This\n * applies them to the page so they win over the bundle's baked `theme.css`:\n *\n * - **Scoped, not `:root`**: the rule targets `WEB5_SCOPE`, the same\n * `:is(#root, .w5-root, [data-web5-placement], .debug-view)` selector every\n * compiled bundle stylesheet uses — host-page styling is never touched, and\n * equal specificity + later document order makes the override win. Callers\n * must therefore apply AFTER the client bundle's CSS is in the document.\n * - **Bucketed, since DL #193**: a `brand` token (colour roles, font families)\n * is written as itself, so the store's identity beats the template's. Every\n * other key — `host` tokens and anything the contract has never heard of —\n * is written as its `--web5-host-*` alias, which core's stylesheets consume\n * as a `var()` fallback. A template stating the real token therefore wins on\n * shape, and an unknown key is inert rather than dangerous: a custom property\n * nothing references renders nothing, so an adapter that learns to read a new\n * token cannot silently override a template.\n * - **The token set stays open, but the destination changed.** Before DL #193\n * an unrecognised key was applied verbatim, so a template could consume\n * `var(--foo)` and grow a token with no release. It now arrives as\n * `--web5-host-foo`, so the template consumes `var(--web5-host-foo, <default>)`\n * instead. One line different, and the provenance is explicit — the wiring\n * line is what activates a host token, not the contract entry.\n * - The mandatory `--` prefix means an override can only define custom\n * properties — it can never set a real CSS property inside the scope. The\n * server enforces the same shape (plus value sanitation) at write time.\n * - **Values are inert**: entries are written with\n * `CSSStyleDeclaration.setProperty`, never string-concatenated into CSS, so\n * a hostile value cannot terminate the declaration or open a new rule.\n * - **The shop owner is the last word (DL #193)**: `userOverrides` carries the\n * choices a human made in the owner panel, and every one of them is written\n * as the real token whatever its bucket. Bucketing exists to arbitrate a\n * disagreement between a store and a template; an owner who opened a panel\n * and set a value has already settled that argument, so aliasing theirs would\n * let the template overrule the person who chose. They are written last, so a\n * token both maps carry resolves to the owner's value.\n * - **The active theme scheme sits between the two**: `schemeTokens` is the\n * resolved entry of `Configuration.themeSchemes` (see `theme/themeScheme`).\n * It is written after the store map and before the owner's overrides, under\n * the SAME bucket rule as the store map: a `brand` token (colours, fonts) as\n * the real token, anything else — `--radius`, a key the contract has never\n * heard of — as its `--web5-host-*` alias. Being the later write of either\n * property, the chosen palette beats the flat store map on colours and on\n * radius alike — yet a template that states `--radius` still wins on shape,\n * exactly as it does over an imported radius. A scheme is written like the\n * store map it stands in for, not like an owner override, so it gets no\n * exemption from aliasing; the owner's layer still wins over it. Its entries\n * are filtered exactly like the store map's too: the same key check, the same\n * warning, and no per-type value check — `setProperty` keeps any value inert,\n * and the typed grammar (`isSchemeTokenEntry`) is the panel's to enforce\n * before a write. So the scheme token set is as open as `themeOverrides`: a\n * new scheme token needs no release here. Absent → exactly the pre-scheme\n * behaviour.\n * - **Idempotent**: one marker element per document, replaced wholesale on\n * re-apply; empty/absent input (all three layers) removes it.\n */\nimport { WEB5_SCOPE } from '../hostScope';\nimport {\n traceThemeOverrides,\n type AppliedToken,\n type ThemeOverrideSource,\n} from './themeDebug';\nimport {\n THEME_OVERRIDE_KEY_PATTERN,\n THEME_TOKEN_CONTRACT,\n bucketOf,\n hostAliasFor,\n} from '../theme/tokenContract';\n\n/** A themeOverrides key: always a CSS custom property. */\nexport type ThemeOverrideKey = `--${string}`;\n\n/**\n * Writer-side shape for a theme override map. The catalog below documents\n * the tokens the seed consumes today; the type stays open so new tokens\n * flow end-to-end with no release anywhere but the seed.\n */\nexport type ThemeOverrides = Record<ThemeOverrideKey, string>;\n\n/**\n * The known token catalog. GUIDANCE, NOT A GATE — `applyThemeOverrides` still\n * applies any well-formed custom property, so the vocabulary can grow without a\n * release here.\n *\n * Derived from `@wix/web5-token-contract` rather than restated, because two\n * hand-maintained copies of one list is how they drift. The contract also\n * carries what this Set cannot: which bucket each token is in, and therefore\n * who wins when a store and a template disagree.\n */\nexport const THEME_OVERRIDE_TOKENS: ReadonlySet<string> = new Set(\n Object.keys(THEME_TOKEN_CONTRACT),\n);\n\nconst MARKER_ATTR = 'data-web5-theme-overrides';\n\n/**\n * Drops keys that are not well-formed custom properties, warning about each.\n * Used for all three layers — store, scheme and owner.\n */\nfunction wellFormedEntries(\n overrides?: Record<string, string> | null,\n): [string, string][] {\n return Object.entries(overrides ?? {}).filter(([key]) => {\n const wellFormed = THEME_OVERRIDE_KEY_PATTERN.test(key);\n if (!wellFormed) {\n console.warn(\n `[web5-theme] Skipping malformed theme override key: ${key}`,\n );\n }\n return wellFormed;\n });\n}\n\n/**\n * Where a store-side token lands: a `brand` token as itself, so the store's\n * identity beats the template's; everything else as its `--web5-host-*` alias,\n * which core consumes only as a fallback the template can beat.\n */\nconst bucketed = (key: string): string =>\n bucketOf(key) === 'brand' ? key : hostAliasFor(key);\n\nexport function applyThemeOverrides(\n overrides?: Record<string, string> | null,\n userOverrides?: Record<string, string> | null,\n schemeTokens?: Record<string, string> | null,\n): void {\n if (typeof document === 'undefined') {\n return;\n }\n\n const existing = document.head.querySelector(`style[${MARKER_ATTR}]`);\n const entries = wellFormedEntries(overrides);\n const userEntries = wellFormedEntries(userOverrides);\n const activeSchemeEntries = wellFormedEntries(schemeTokens);\n\n if (\n entries.length === 0 &&\n activeSchemeEntries.length === 0 &&\n userEntries.length === 0\n ) {\n existing?.remove();\n // Nothing to apply is itself a traceable answer: it means every token on\n // the page is the template's, which is otherwise indistinguishable from\n // tracing having failed.\n traceThemeOverrides([]);\n return;\n }\n\n const style = document.createElement('style');\n style.setAttribute(MARKER_ATTR, '');\n document.head.appendChild(style);\n const sheet = style.sheet;\n if (!sheet) {\n style.remove();\n return;\n }\n sheet.insertRule(`${WEB5_SCOPE} {}`, 0);\n const rule = sheet.cssRules[0] as CSSStyleRule;\n const applied: AppliedToken[] = [];\n const write = (\n key: string,\n value: string,\n target: string,\n source: ThemeOverrideSource,\n ): void => {\n try {\n rule.style.setProperty(target, value);\n applied.push({ token: key, value, writtenAs: target, source });\n } catch {\n // An engine that rejects the value leaves the token at its baked\n // default — degraded theming, never broken CSS.\n }\n };\n\n for (const [key, value] of entries) {\n // Brand wins over the template, so it is written as the token the\n // stylesheets actually read. Everything else lands in the host namespace,\n // where core consumes it only as a fallback the template can beat.\n write(key, value, bucketed(key), 'store');\n }\n // The active scheme over the store map, bucketed the same way: a brand token\n // as itself, `--radius` and unknown keys as their alias. Either way this is\n // the later write of that property, so it beats the store's — and a\n // template's `--radius` still beats the alias.\n for (const [key, value] of activeSchemeEntries) {\n write(key, value, bucketed(key), 'scheme');\n }\n // Owner choices last and never aliased: within one declaration block the last\n // write of a property wins, so a token both maps carry ends up the owner's.\n for (const [key, value] of userEntries) {\n write(key, value, key, 'user');\n }\n // Replace-on-reapply: the fresh element is appended (so it stays last in\n // document order) before the stale one is dropped.\n existing?.remove();\n // AFTER the stale element is gone, so the resolved values traced below are\n // the ones the page will actually render. Off unless explicitly enabled.\n traceThemeOverrides(applied);\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASA,UAAU,QAAQ,cAAc;AACzC,SACEC,mBAAmB,QAGd,cAAc;AACrB,SACEC,0BAA0B,EAC1BC,oBAAoB,EACpBC,QAAQ,EACRC,YAAY,QACP,wBAAwB;;AAE/B;;AAGA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,qBAA0C,GAAG,IAAIC,GAAG,CAC/DC,MAAM,CAACC,IAAI,CAACN,oBAAoB,CAClC,CAAC;AAED,MAAMO,WAAW,GAAG,2BAA2B;;AAE/C;AACA;AACA;AACA;AACA,SAASC,iBAAiBA,CACxBC,SAAyC,EACrB;EACpB,OAAOJ,MAAM,CAACK,OAAO,CAACD,SAAS,IAAI,CAAC,CAAC,CAAC,CAACE,MAAM,CAACC,IAAA,IAAW;IAAA,IAAV,CAACC,GAAG,CAAC,GAAAD,IAAA;IAClD,MAAME,UAAU,GAAGf,0BAA0B,CAACgB,IAAI,CAACF,GAAG,CAAC;IACvD,IAAI,CAACC,UAAU,EAAE;MACfE,OAAO,CAACC,IAAI,CACV,uDAAuDJ,GAAG,EAC5D,CAAC;IACH;IACA,OAAOC,UAAU;EACnB,CAAC,CAAC;AACJ;;AAEA;AACA;AACA;AACA;AACA;AACA,MAAMI,QAAQ,GAAIL,GAAW,IAC3BZ,QAAQ,CAACY,GAAG,CAAC,KAAK,OAAO,GAAGA,GAAG,GAAGX,YAAY,CAACW,GAAG,CAAC;AAErD,OAAO,SAASM,mBAAmBA,CACjCV,SAAyC,EACzCW,aAA6C,EAC7CC,YAA4C,EACtC;EACN,IAAI,OAAOC,QAAQ,KAAK,WAAW,EAAE;IACnC;EACF;EAEA,MAAMC,QAAQ,GAAGD,QAAQ,CAACE,IAAI,CAACC,aAAa,CAAC,SAASlB,WAAW,GAAG,CAAC;EACrE,MAAMG,OAAO,GAAGF,iBAAiB,CAACC,SAAS,CAAC;EAC5C,MAAMiB,WAAW,GAAGlB,iBAAiB,CAACY,aAAa,CAAC;EACpD,MAAMO,mBAAmB,GAAGnB,iBAAiB,CAACa,YAAY,CAAC;EAE3D,IACEX,OAAO,CAACkB,MAAM,KAAK,CAAC,IACpBD,mBAAmB,CAACC,MAAM,KAAK,CAAC,IAChCF,WAAW,CAACE,MAAM,KAAK,CAAC,EACxB;IACAL,QAAQ,YAARA,QAAQ,CAAEM,MAAM,CAAC,CAAC;IAClB;IACA;IACA;IACA/B,mBAAmB,CAAC,EAAE,CAAC;IACvB;EACF;EAEA,MAAMgC,KAAK,GAAGR,QAAQ,CAACS,aAAa,CAAC,OAAO,CAAC;EAC7CD,KAAK,CAACE,YAAY,CAACzB,WAAW,EAAE,EAAE,CAAC;EACnCe,QAAQ,CAACE,IAAI,CAACS,WAAW,CAACH,KAAK,CAAC;EAChC,MAAMI,KAAK,GAAGJ,KAAK,CAACI,KAAK;EACzB,IAAI,CAACA,KAAK,EAAE;IACVJ,KAAK,CAACD,MAAM,CAAC,CAAC;IACd;EACF;EACAK,KAAK,CAACC,UAAU,CAAC,GAAGtC,UAAU,KAAK,EAAE,CAAC,CAAC;EACvC,MAAMuC,IAAI,GAAGF,KAAK,CAACG,QAAQ,CAAC,CAAC,CAAiB;EAC9C,MAAMC,OAAuB,GAAG,EAAE;EAClC,MAAMC,KAAK,GAAGA,CACZ1B,GAAW,EACX2B,KAAa,EACbC,MAAc,EACdC,MAA2B,KAClB;IACT,IAAI;MACFN,IAAI,CAACN,KAAK,CAACa,WAAW,CAACF,MAAM,EAAED,KAAK,CAAC;MACrCF,OAAO,CAACM,IAAI,CAAC;QAAEC,KAAK,EAAEhC,GAAG;QAAE2B,KAAK;QAAEM,SAAS,EAAEL,MAAM;QAAEC;MAAO,CAAC,CAAC;IAChE,CAAC,CAAC,MAAM;MACN;MACA;IAAA;EAEJ,CAAC;EAED,KAAK,MAAM,CAAC7B,GAAG,EAAE2B,KAAK,CAAC,IAAI9B,OAAO,EAAE;IAClC;IACA;IACA;IACA6B,KAAK,CAAC1B,GAAG,EAAE2B,KAAK,EAAEtB,QAAQ,CAACL,GAAG,CAAC,EAAE,OAAO,CAAC;EAC3C;EACA;EACA;EACA;EACA;EACA,KAAK,MAAM,CAACA,GAAG,EAAE2B,KAAK,CAAC,IAAIb,mBAAmB,EAAE;IAC9CY,KAAK,CAAC1B,GAAG,EAAE2B,KAAK,EAAEtB,QAAQ,CAACL,GAAG,CAAC,EAAE,QAAQ,CAAC;EAC5C;EACA;EACA;EACA,KAAK,MAAM,CAACA,GAAG,EAAE2B,KAAK,CAAC,IAAId,WAAW,EAAE;IACtCa,KAAK,CAAC1B,GAAG,EAAE2B,KAAK,EAAE3B,GAAG,EAAE,MAAM,CAAC;EAChC;EACA;EACA;EACAU,QAAQ,YAARA,QAAQ,CAAEM,MAAM,CAAC,CAAC;EAClB;EACA;EACA/B,mBAAmB,CAACwC,OAAO,CAAC;AAC9B","ignoreList":[]}
|
|
@@ -20,8 +20,12 @@
|
|
|
20
20
|
*
|
|
21
21
|
* [web5:theme] --radius host wrote --web5-host-radius=0rem → 1.25rem TEMPLATE
|
|
22
22
|
* [web5:theme] --primary brand wrote --primary=0 0% 9% → 0 0% 9% STORE
|
|
23
|
+
* [web5:theme] --radius scheme wrote --web5-host-radius=1rem → 1rem SCHEME
|
|
23
24
|
* [web5:theme] --radius owner wrote --radius=2rem → 2rem OWNER
|
|
24
25
|
*
|
|
26
|
+
* A scheme is bucketed like the store map, so its `--radius` is written as the
|
|
27
|
+
* alias too — and can lose to a template the same way.
|
|
28
|
+
*
|
|
25
29
|
* Off by default. Enable with `?web5DebugTheme=1` (one-shot, wins over storage)
|
|
26
30
|
* or `localStorage["web5_debug_theme"] = "1"` (sticky) — the same shape as
|
|
27
31
|
* `matchDebug`, so there is one convention to learn rather than two.
|
|
@@ -67,7 +71,7 @@ export const resetThemeDebugCache = () => {
|
|
|
67
71
|
cached = null;
|
|
68
72
|
};
|
|
69
73
|
|
|
70
|
-
/** Which layer asked for a value: the platform import, or a human. */
|
|
74
|
+
/** Which layer asked for a value: the platform import, the active theme scheme, or a human. */
|
|
71
75
|
|
|
72
76
|
/** One token as it was actually written to the mount rule. */
|
|
73
77
|
|
|
@@ -106,13 +110,18 @@ export function traceThemeOverrides(applied) {
|
|
|
106
110
|
// One computed-style read for the whole batch: the expensive part is the style
|
|
107
111
|
// recalculation it forces, not the per-property lookups off the result.
|
|
108
112
|
const computed = getComputedStyle(mount);
|
|
109
|
-
//
|
|
110
|
-
//
|
|
113
|
+
// Layers are written store → scheme → owner, and the last write of a token is
|
|
114
|
+
// the one that resolves — so the latest layer is credited, or a store value
|
|
111
115
|
// that happens to match would be credited with a win it did not have.
|
|
116
|
+
const rank = {
|
|
117
|
+
store: 0,
|
|
118
|
+
scheme: 1,
|
|
119
|
+
user: 2
|
|
120
|
+
};
|
|
112
121
|
const byToken = new Map();
|
|
113
122
|
for (const entry of applied) {
|
|
114
123
|
const held = byToken.get(entry.token);
|
|
115
|
-
if (!held || entry.source
|
|
124
|
+
if (!held || rank[entry.source] >= rank[held.source]) {
|
|
116
125
|
byToken.set(entry.token, entry);
|
|
117
126
|
}
|
|
118
127
|
}
|
|
@@ -127,11 +136,11 @@ export function traceThemeOverrides(applied) {
|
|
|
127
136
|
const wanted = value.trim();
|
|
128
137
|
return {
|
|
129
138
|
token,
|
|
130
|
-
bucket: source === 'user' ? 'owner' : bucketOf(token),
|
|
139
|
+
bucket: source === 'user' ? 'owner' : source === 'scheme' ? 'scheme' : bucketOf(token),
|
|
131
140
|
'written as': writtenAs,
|
|
132
141
|
'asked for': wanted,
|
|
133
142
|
'resolves to': resolved || '(nothing reads it)',
|
|
134
|
-
winner: !resolved ? 'NOBODY — no stylesheet consumes this token' : resolved !== wanted ? 'TEMPLATE' : source === 'user' ? 'OWNER' : 'STORE'
|
|
143
|
+
winner: !resolved ? 'NOBODY — no stylesheet consumes this token' : resolved !== wanted ? 'TEMPLATE' : source === 'user' ? 'OWNER' : source === 'scheme' ? 'SCHEME' : 'STORE'
|
|
135
144
|
};
|
|
136
145
|
});
|
|
137
146
|
const overridden = rows.filter(r => r.winner === 'TEMPLATE').length;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["WEB5_SCOPES","bucketOf","THEME_DEBUG_KEY","THEME_DEBUG_QUERY_PARAM","LOG_PREFIX","isTruthy","value","cached","isThemeDebugEnabled","URLSearchParams","window","location","search","get","localStorage","getItem","resetThemeDebugCache","traceThemeOverrides","applied","length","console","info","mount","document","querySelector","join","warn","computed","getComputedStyle","byToken","Map","entry","held","token","source","set","rows","values","map","writtenAs","resolved","getPropertyValue","trim","wanted","bucket","winner","overridden","filter","r","inert","startsWith","groupCollapsed","table","groupEnd"],"sources":["../../../src/client/themeDebug.ts"],"sourcesContent":["/**\n * Theme-override tracing (debug-only).\n *\n * Answers the one question the token pipeline cannot otherwise be asked:\n * **which layer actually won?**\n *\n * Since DL #193 a `brand` token is written as itself and everything else as its\n * `--web5-host-*` alias, which core's stylesheets consume as a `var()` fallback\n * a template can beat. That makes the outcome a cascade decision, and a cascade\n * decision is invisible — there is no callback, no return value, and nothing in\n * the DOM to read. Worse, `applyThemeOverrides` writes through the CSSOM\n * (`insertRule` + `setProperty`, never `textContent`) so that a hostile value\n * can never terminate a declaration, which means DevTools renders the marker as\n * `<style data-web5-theme-overrides=\"\">` — an empty tag, with the rules real but\n * nowhere in the DOM tree. Every part of that is deliberate and every part of it\n * looks broken.\n *\n * So this reads the resolved value back off the mount **after** the rule is in,\n * and reports what the browser actually decided:\n *\n * [web5:theme] --radius host wrote --web5-host-radius=0rem → 1.25rem TEMPLATE\n * [web5:theme] --primary brand wrote --primary=0 0% 9% → 0 0% 9% STORE\n * [web5:theme] --radius owner wrote --radius=2rem → 2rem OWNER\n *\n * Off by default. Enable with `?web5DebugTheme=1` (one-shot, wins over storage)\n * or `localStorage[\"web5_debug_theme\"] = \"1\"` (sticky) — the same shape as\n * `matchDebug`, so there is one convention to learn rather than two.\n *\n * It is gated rather than always-on because reading a computed value forces a\n * style recalculation, and this runs during page setup. One read is taken for\n * the whole batch, not one per token.\n */\nimport { WEB5_SCOPES } from '../hostScope';\nimport { bucketOf } from '../theme/tokenContract';\n\nexport const THEME_DEBUG_KEY = 'web5_debug_theme';\n\n/** Query param that forces theme tracing on, overriding localStorage. */\nexport const THEME_DEBUG_QUERY_PARAM = 'web5DebugTheme';\n\nconst LOG_PREFIX = '[web5:theme]';\n\nconst isTruthy = (value: unknown): boolean => value === '1' || value === 'true';\n\nlet cached: boolean | null = null;\n\n/** Whether theme tracing is enabled. Memoized — resolved once per page load. */\nexport const isThemeDebugEnabled = (): boolean => {\n if (cached !== null) {\n return cached;\n }\n cached = false;\n try {\n if (\n isTruthy(\n new URLSearchParams(window.location.search).get(\n THEME_DEBUG_QUERY_PARAM,\n ),\n )\n ) {\n cached = true;\n return cached;\n }\n } catch {\n // window / URLSearchParams unavailable (SSR, non-DOM environments).\n }\n try {\n cached = isTruthy(localStorage.getItem(THEME_DEBUG_KEY));\n } catch {\n // localStorage can throw in privacy mode / non-DOM environments.\n }\n return cached;\n};\n\n/** Test seam — the flag is memoized for the page, so tests must be able to clear it. */\nexport const resetThemeDebugCache = (): void => {\n cached = null;\n};\n\n/** Which layer asked for a value: the platform import, or a human. */\nexport type ThemeOverrideSource = 'store' | 'user';\n\n/** One token as it was actually written to the mount rule. */\nexport interface AppliedToken {\n token: string;\n value: string;\n writtenAs: string;\n source: ThemeOverrideSource;\n}\n\ninterface TraceRow {\n token: string;\n bucket: string;\n 'written as': string;\n 'asked for': string;\n 'resolves to': string;\n winner: string;\n}\n\n/**\n * Report what each override did, after the rule is live.\n *\n * `applied` is what `applyThemeOverrides` actually wrote — already filtered for\n * key shape, so anything malformed has been dropped and warned about before\n * reaching here.\n */\nexport function traceThemeOverrides(applied: AppliedToken[]): void {\n if (!isThemeDebugEnabled()) {\n return;\n }\n if (applied.length === 0) {\n // Silence here would be indistinguishable from tracing being broken, and\n // \"no overrides reached the page\" is itself the answer often enough — a\n // store with nothing imported means every token is the template's.\n // eslint-disable-next-line no-console\n console.info(\n `${LOG_PREFIX} no overrides applied — nothing usable arrived for this store, so every token is whatever the template and core defaults say`,\n );\n return;\n }\n let mount: Element | null = null;\n try {\n mount = document.querySelector(WEB5_SCOPES.join(','));\n } catch {\n // Malformed selector cannot happen with the constant, but querySelector is\n // the one call here that can throw, and a debug aid must never be the thing\n // that breaks a page.\n }\n if (!mount) {\n console.warn(\n `${LOG_PREFIX} no mount found (${WEB5_SCOPES.join(\n ', ',\n )}) — tokens were written, but their resolved values cannot be read`,\n );\n return;\n }\n\n // One computed-style read for the whole batch: the expensive part is the style\n // recalculation it forces, not the per-property lookups off the result.\n const computed = getComputedStyle(mount);\n // An owner's write is the last one into the rule, so when both layers name a\n // token the resolved value is theirs — check the owner first or a store value\n // that happens to match would be credited with a win it did not have.\n const byToken = new Map<string, AppliedToken>();\n for (const entry of applied) {\n const held = byToken.get(entry.token);\n if (!held || entry.source === 'user') {\n byToken.set(entry.token, entry);\n }\n }\n const rows: TraceRow[] = [...byToken.values()].map((entry) => {\n const { token, value, writtenAs, source } = entry;\n const resolved = computed.getPropertyValue(token).trim();\n const wanted = value.trim();\n return {\n token,\n bucket: source === 'user' ? 'owner' : bucketOf(token),\n 'written as': writtenAs,\n 'asked for': wanted,\n 'resolves to': resolved || '(nothing reads it)',\n winner: !resolved\n ? 'NOBODY — no stylesheet consumes this token'\n : resolved !== wanted\n ? 'TEMPLATE'\n : source === 'user'\n ? 'OWNER'\n : 'STORE',\n };\n });\n\n const overridden = rows.filter((r) => r.winner === 'TEMPLATE').length;\n const inert = rows.filter((r) => r.winner.startsWith('NOBODY')).length;\n\n /* eslint-disable no-console */\n console.groupCollapsed(\n `${LOG_PREFIX} ${rows.length} override(s) applied · ${overridden} kept by the template · ${inert} inert`,\n );\n console.table(rows);\n if (inert > 0) {\n console.info(\n `${LOG_PREFIX} \"inert\" means the value was written but no stylesheet reads that token — ` +\n `for a host token that means core has no \\`var(--web5-host-…)\\` wiring for it yet.`,\n );\n }\n console.info(\n `${LOG_PREFIX} the marker <style data-web5-theme-overrides> looks empty in DevTools on purpose: ` +\n `rules are inserted through the CSSOM, never as text. Read them with ` +\n `document.querySelector('style[data-web5-theme-overrides]').sheet.cssRules[0].cssText`,\n );\n console.groupEnd();\n /* eslint-enable no-console */\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASA,WAAW,QAAQ,cAAc;AAC1C,SAASC,QAAQ,QAAQ,wBAAwB;AAEjD,OAAO,MAAMC,eAAe,GAAG,kBAAkB;;AAEjD;AACA,OAAO,MAAMC,uBAAuB,GAAG,gBAAgB;AAEvD,MAAMC,UAAU,GAAG,cAAc;AAEjC,MAAMC,QAAQ,GAAIC,KAAc,IAAcA,KAAK,KAAK,GAAG,IAAIA,KAAK,KAAK,MAAM;AAE/E,IAAIC,MAAsB,GAAG,IAAI;;AAEjC;AACA,OAAO,MAAMC,mBAAmB,GAAGA,CAAA,KAAe;EAChD,IAAID,MAAM,KAAK,IAAI,EAAE;IACnB,OAAOA,MAAM;EACf;EACAA,MAAM,GAAG,KAAK;EACd,IAAI;IACF,IACEF,QAAQ,CACN,IAAII,eAAe,CAACC,MAAM,CAACC,QAAQ,CAACC,MAAM,CAAC,CAACC,GAAG,CAC7CV,uBACF,CACF,CAAC,EACD;MACAI,MAAM,GAAG,IAAI;MACb,OAAOA,MAAM;IACf;EACF,CAAC,CAAC,MAAM;IACN;EAAA;EAEF,IAAI;IACFA,MAAM,GAAGF,QAAQ,CAACS,YAAY,CAACC,OAAO,CAACb,eAAe,CAAC,CAAC;EAC1D,CAAC,CAAC,MAAM;IACN;EAAA;EAEF,OAAOK,MAAM;AACf,CAAC;;AAED;AACA,OAAO,MAAMS,oBAAoB,GAAGA,CAAA,KAAY;EAC9CT,MAAM,GAAG,IAAI;AACf,CAAC;;AAED;;AAGA;;AAiBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASU,mBAAmBA,CAACC,OAAuB,EAAQ;EACjE,IAAI,CAACV,mBAAmB,CAAC,CAAC,EAAE;IAC1B;EACF;EACA,IAAIU,OAAO,CAACC,MAAM,KAAK,CAAC,EAAE;IACxB;IACA;IACA;IACA;IACAC,OAAO,CAACC,IAAI,CACV,GAAGjB,UAAU,8HACf,CAAC;IACD;EACF;EACA,IAAIkB,KAAqB,GAAG,IAAI;EAChC,IAAI;IACFA,KAAK,GAAGC,QAAQ,CAACC,aAAa,CAACxB,WAAW,CAACyB,IAAI,CAAC,GAAG,CAAC,CAAC;EACvD,CAAC,CAAC,MAAM;IACN;IACA;IACA;EAAA;EAEF,IAAI,CAACH,KAAK,EAAE;IACVF,OAAO,CAACM,IAAI,CACV,GAAGtB,UAAU,oBAAoBJ,WAAW,CAACyB,IAAI,CAC/C,IACF,CAAC,mEACH,CAAC;IACD;EACF;;EAEA;EACA;EACA,MAAME,QAAQ,GAAGC,gBAAgB,CAACN,KAAK,CAAC;EACxC;EACA;EACA;EACA,MAAMO,OAAO,GAAG,IAAIC,GAAG,CAAuB,CAAC;EAC/C,KAAK,MAAMC,KAAK,IAAIb,OAAO,EAAE;IAC3B,MAAMc,IAAI,GAAGH,OAAO,CAAChB,GAAG,CAACkB,KAAK,CAACE,KAAK,CAAC;IACrC,IAAI,CAACD,IAAI,IAAID,KAAK,CAACG,MAAM,KAAK,MAAM,EAAE;MACpCL,OAAO,CAACM,GAAG,CAACJ,KAAK,CAACE,KAAK,EAAEF,KAAK,CAAC;IACjC;EACF;EACA,MAAMK,IAAgB,GAAG,CAAC,GAAGP,OAAO,CAACQ,MAAM,CAAC,CAAC,CAAC,CAACC,GAAG,CAAEP,KAAK,IAAK;IAC5D,MAAM;MAAEE,KAAK;MAAE3B,KAAK;MAAEiC,SAAS;MAAEL;IAAO,CAAC,GAAGH,KAAK;IACjD,MAAMS,QAAQ,GAAGb,QAAQ,CAACc,gBAAgB,CAACR,KAAK,CAAC,CAACS,IAAI,CAAC,CAAC;IACxD,MAAMC,MAAM,GAAGrC,KAAK,CAACoC,IAAI,CAAC,CAAC;IAC3B,OAAO;MACLT,KAAK;MACLW,MAAM,EAAEV,MAAM,KAAK,MAAM,GAAG,OAAO,GAAGjC,QAAQ,CAACgC,KAAK,CAAC;MACrD,YAAY,EAAEM,SAAS;MACvB,WAAW,EAAEI,MAAM;MACnB,aAAa,EAAEH,QAAQ,IAAI,oBAAoB;MAC/CK,MAAM,EAAE,CAACL,QAAQ,GACb,4CAA4C,GAC5CA,QAAQ,KAAKG,MAAM,GACnB,UAAU,GACVT,MAAM,KAAK,MAAM,GACjB,OAAO,GACP;IACN,CAAC;EACH,CAAC,CAAC;EAEF,MAAMY,UAAU,GAAGV,IAAI,CAACW,MAAM,CAAEC,CAAC,IAAKA,CAAC,CAACH,MAAM,KAAK,UAAU,CAAC,CAAC1B,MAAM;EACrE,MAAM8B,KAAK,GAAGb,IAAI,CAACW,MAAM,CAAEC,CAAC,IAAKA,CAAC,CAACH,MAAM,CAACK,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC/B,MAAM;;EAEtE;EACAC,OAAO,CAAC+B,cAAc,CACpB,GAAG/C,UAAU,IAAIgC,IAAI,CAACjB,MAAM,0BAA0B2B,UAAU,2BAA2BG,KAAK,QAClG,CAAC;EACD7B,OAAO,CAACgC,KAAK,CAAChB,IAAI,CAAC;EACnB,IAAIa,KAAK,GAAG,CAAC,EAAE;IACb7B,OAAO,CAACC,IAAI,CACV,GAAGjB,UAAU,4EAA4E,GACvF,mFACJ,CAAC;EACH;EACAgB,OAAO,CAACC,IAAI,CACV,GAAGjB,UAAU,oFAAoF,GAC/F,sEAAsE,GACtE,sFACJ,CAAC;EACDgB,OAAO,CAACiC,QAAQ,CAAC,CAAC;EAClB;AACF","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["WEB5_SCOPES","bucketOf","THEME_DEBUG_KEY","THEME_DEBUG_QUERY_PARAM","LOG_PREFIX","isTruthy","value","cached","isThemeDebugEnabled","URLSearchParams","window","location","search","get","localStorage","getItem","resetThemeDebugCache","traceThemeOverrides","applied","length","console","info","mount","document","querySelector","join","warn","computed","getComputedStyle","rank","store","scheme","user","byToken","Map","entry","held","token","source","set","rows","values","map","writtenAs","resolved","getPropertyValue","trim","wanted","bucket","winner","overridden","filter","r","inert","startsWith","groupCollapsed","table","groupEnd"],"sources":["../../../src/client/themeDebug.ts"],"sourcesContent":["/**\n * Theme-override tracing (debug-only).\n *\n * Answers the one question the token pipeline cannot otherwise be asked:\n * **which layer actually won?**\n *\n * Since DL #193 a `brand` token is written as itself and everything else as its\n * `--web5-host-*` alias, which core's stylesheets consume as a `var()` fallback\n * a template can beat. That makes the outcome a cascade decision, and a cascade\n * decision is invisible — there is no callback, no return value, and nothing in\n * the DOM to read. Worse, `applyThemeOverrides` writes through the CSSOM\n * (`insertRule` + `setProperty`, never `textContent`) so that a hostile value\n * can never terminate a declaration, which means DevTools renders the marker as\n * `<style data-web5-theme-overrides=\"\">` — an empty tag, with the rules real but\n * nowhere in the DOM tree. Every part of that is deliberate and every part of it\n * looks broken.\n *\n * So this reads the resolved value back off the mount **after** the rule is in,\n * and reports what the browser actually decided:\n *\n * [web5:theme] --radius host wrote --web5-host-radius=0rem → 1.25rem TEMPLATE\n * [web5:theme] --primary brand wrote --primary=0 0% 9% → 0 0% 9% STORE\n * [web5:theme] --radius scheme wrote --web5-host-radius=1rem → 1rem SCHEME\n * [web5:theme] --radius owner wrote --radius=2rem → 2rem OWNER\n *\n * A scheme is bucketed like the store map, so its `--radius` is written as the\n * alias too — and can lose to a template the same way.\n *\n * Off by default. Enable with `?web5DebugTheme=1` (one-shot, wins over storage)\n * or `localStorage[\"web5_debug_theme\"] = \"1\"` (sticky) — the same shape as\n * `matchDebug`, so there is one convention to learn rather than two.\n *\n * It is gated rather than always-on because reading a computed value forces a\n * style recalculation, and this runs during page setup. One read is taken for\n * the whole batch, not one per token.\n */\nimport { WEB5_SCOPES } from '../hostScope';\nimport { bucketOf } from '../theme/tokenContract';\n\nexport const THEME_DEBUG_KEY = 'web5_debug_theme';\n\n/** Query param that forces theme tracing on, overriding localStorage. */\nexport const THEME_DEBUG_QUERY_PARAM = 'web5DebugTheme';\n\nconst LOG_PREFIX = '[web5:theme]';\n\nconst isTruthy = (value: unknown): boolean => value === '1' || value === 'true';\n\nlet cached: boolean | null = null;\n\n/** Whether theme tracing is enabled. Memoized — resolved once per page load. */\nexport const isThemeDebugEnabled = (): boolean => {\n if (cached !== null) {\n return cached;\n }\n cached = false;\n try {\n if (\n isTruthy(\n new URLSearchParams(window.location.search).get(\n THEME_DEBUG_QUERY_PARAM,\n ),\n )\n ) {\n cached = true;\n return cached;\n }\n } catch {\n // window / URLSearchParams unavailable (SSR, non-DOM environments).\n }\n try {\n cached = isTruthy(localStorage.getItem(THEME_DEBUG_KEY));\n } catch {\n // localStorage can throw in privacy mode / non-DOM environments.\n }\n return cached;\n};\n\n/** Test seam — the flag is memoized for the page, so tests must be able to clear it. */\nexport const resetThemeDebugCache = (): void => {\n cached = null;\n};\n\n/** Which layer asked for a value: the platform import, the active theme scheme, or a human. */\nexport type ThemeOverrideSource = 'store' | 'scheme' | 'user';\n\n/** One token as it was actually written to the mount rule. */\nexport interface AppliedToken {\n token: string;\n value: string;\n writtenAs: string;\n source: ThemeOverrideSource;\n}\n\ninterface TraceRow {\n token: string;\n bucket: string;\n 'written as': string;\n 'asked for': string;\n 'resolves to': string;\n winner: string;\n}\n\n/**\n * Report what each override did, after the rule is live.\n *\n * `applied` is what `applyThemeOverrides` actually wrote — already filtered for\n * key shape, so anything malformed has been dropped and warned about before\n * reaching here.\n */\nexport function traceThemeOverrides(applied: AppliedToken[]): void {\n if (!isThemeDebugEnabled()) {\n return;\n }\n if (applied.length === 0) {\n // Silence here would be indistinguishable from tracing being broken, and\n // \"no overrides reached the page\" is itself the answer often enough — a\n // store with nothing imported means every token is the template's.\n // eslint-disable-next-line no-console\n console.info(\n `${LOG_PREFIX} no overrides applied — nothing usable arrived for this store, so every token is whatever the template and core defaults say`,\n );\n return;\n }\n let mount: Element | null = null;\n try {\n mount = document.querySelector(WEB5_SCOPES.join(','));\n } catch {\n // Malformed selector cannot happen with the constant, but querySelector is\n // the one call here that can throw, and a debug aid must never be the thing\n // that breaks a page.\n }\n if (!mount) {\n console.warn(\n `${LOG_PREFIX} no mount found (${WEB5_SCOPES.join(\n ', ',\n )}) — tokens were written, but their resolved values cannot be read`,\n );\n return;\n }\n\n // One computed-style read for the whole batch: the expensive part is the style\n // recalculation it forces, not the per-property lookups off the result.\n const computed = getComputedStyle(mount);\n // Layers are written store → scheme → owner, and the last write of a token is\n // the one that resolves — so the latest layer is credited, or a store value\n // that happens to match would be credited with a win it did not have.\n const rank: Record<ThemeOverrideSource, number> = {\n store: 0,\n scheme: 1,\n user: 2,\n };\n const byToken = new Map<string, AppliedToken>();\n for (const entry of applied) {\n const held = byToken.get(entry.token);\n if (!held || rank[entry.source] >= rank[held.source]) {\n byToken.set(entry.token, entry);\n }\n }\n const rows: TraceRow[] = [...byToken.values()].map((entry) => {\n const { token, value, writtenAs, source } = entry;\n const resolved = computed.getPropertyValue(token).trim();\n const wanted = value.trim();\n return {\n token,\n bucket:\n source === 'user'\n ? 'owner'\n : source === 'scheme'\n ? 'scheme'\n : bucketOf(token),\n 'written as': writtenAs,\n 'asked for': wanted,\n 'resolves to': resolved || '(nothing reads it)',\n winner: !resolved\n ? 'NOBODY — no stylesheet consumes this token'\n : resolved !== wanted\n ? 'TEMPLATE'\n : source === 'user'\n ? 'OWNER'\n : source === 'scheme'\n ? 'SCHEME'\n : 'STORE',\n };\n });\n\n const overridden = rows.filter((r) => r.winner === 'TEMPLATE').length;\n const inert = rows.filter((r) => r.winner.startsWith('NOBODY')).length;\n\n /* eslint-disable no-console */\n console.groupCollapsed(\n `${LOG_PREFIX} ${rows.length} override(s) applied · ${overridden} kept by the template · ${inert} inert`,\n );\n console.table(rows);\n if (inert > 0) {\n console.info(\n `${LOG_PREFIX} \"inert\" means the value was written but no stylesheet reads that token — ` +\n `for a host token that means core has no \\`var(--web5-host-…)\\` wiring for it yet.`,\n );\n }\n console.info(\n `${LOG_PREFIX} the marker <style data-web5-theme-overrides> looks empty in DevTools on purpose: ` +\n `rules are inserted through the CSSOM, never as text. Read them with ` +\n `document.querySelector('style[data-web5-theme-overrides]').sheet.cssRules[0].cssText`,\n );\n console.groupEnd();\n /* eslint-enable no-console */\n}\n"],"mappings":"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","ignoreList":[]}
|
package/dist/esm/index.js
CHANGED
|
@@ -161,7 +161,8 @@ export { getClientBundleOverride, isTrustedBundleHost } from './client/clientBun
|
|
|
161
161
|
export { TEMPLATES_CDN_BASE, TEMPLATES_MANIFEST_URL, getTemplateOverride, isTemplatePickerRequested, isValidTemplateId, resolveClientBundleUrl } from './client/clientBundleUrl.js';
|
|
162
162
|
export { mergeClientConfig } from './client/mergeClientConfig.js';
|
|
163
163
|
export { applyThemeOverrides, THEME_OVERRIDE_TOKENS } from './client/applyThemeOverrides.js';
|
|
164
|
-
export { THEME_TOKEN_CONTRACT, BRAND_TOKENS, EDITABLE_TOKENS, TOKEN_NAME_PATTERN, bucketOf, hostAliasFor } from './theme/tokenContract.js';
|
|
164
|
+
export { THEME_TOKEN_CONTRACT, BRAND_TOKENS, EDITABLE_TOKENS, TOKEN_NAME_PATTERN, THEME_OVERRIDE_KEY_PATTERN, bucketOf, hostAliasFor } from './theme/tokenContract.js';
|
|
165
|
+
export { SCHEME_COLOR_TOKENS, MAX_THEME_SCHEMES, SCHEME_ANCHOR_TOKENS, completeScheme, isThemeScheme, isSchemeTokenEntry, nextOwnerSchemeId, resolveActiveScheme, schemeDisplayName } from './theme/themeScheme.js';
|
|
165
166
|
export { isThemeDebugEnabled, THEME_DEBUG_KEY, THEME_DEBUG_QUERY_PARAM } from './client/themeDebug.js';
|
|
166
167
|
export { hexToHslTriplet, hslTripletToHex, isHslTriplet } from './theme/colorFormat.js';
|
|
167
168
|
|