@bettercms-ai/ui 0.7.1 → 0.8.1

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/tokens.d.ts CHANGED
@@ -70,6 +70,11 @@ declare const BRAND_TOKEN_FAMILIES: {
70
70
  readonly unit: "color";
71
71
  readonly from: "extras[]";
72
72
  };
73
+ readonly gradient: {
74
+ readonly keyed: true;
75
+ readonly unit: "gradient";
76
+ readonly from: "graphics.gradients[]";
77
+ };
73
78
  readonly ease: {
74
79
  readonly keyed: false;
75
80
  readonly unit: "easing";
package/dist/tokens.js CHANGED
@@ -26,6 +26,10 @@ var BRAND_TOKEN_FAMILIES = {
26
26
  shadow: { keyed: true, unit: "box-shadow", from: "elevation[]" },
27
27
  duration: { keyed: true, unit: "ms", from: "motion.durations" },
28
28
  color: { keyed: true, unit: "color", from: "extras[]" },
29
+ // A gradient is a VALUE, not a file — which is why it lives in the kit rather than the media
30
+ // library. The emitter composes the CSS from the kit's numbers and colour keys; nothing
31
+ // author-written reaches the stylesheet but the key in this name.
32
+ gradient: { keyed: true, unit: "gradient", from: "graphics.gradients[]" },
29
33
  ease: { keyed: false, unit: "easing", from: "motion.ease" }
30
34
  };
31
35
  var KEY = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tokens.ts"],"sourcesContent":["/**\n * The design-token NAME grammar — one spelling, shared by everything that writes or reads a\n * BetterCMS custom property.\n *\n * Token names are a CROSS-PACKAGE grammar. The backend's brand compiler mints them, starter\n * stylesheets and block components consume them, and until now the only thing keeping the two\n * in step was that somebody typed the same string twice. A grammar with no compiler is how\n * every prop-keyspace schism in this project started: the emitter renames `--brand-leading-*`,\n * every template that reads it silently falls back to its initial value, and nothing is red.\n *\n * So the names live here, in the package both sides already depend on, as CONSTRUCTORS rather\n * than a list of strings — an indexed family (`--brand-space-3`, `--brand-text-lg`) cannot be\n * enumerated ahead of time, but its shape can be, and a builder is the only spelling either\n * side can reach. `assertTokenName` closes the loop for anything generated dynamically.\n *\n * DTCG note (FLO-1178 Lane 2): this is the NAME grammar only. What a token means — its\n * `$type`/`$value` and who owns it — belongs to the template presentation manifest. Keeping\n * names separate is what lets a template declare new knobs without the emitter changing.\n */\n\n/** The two prefixes in the system. `bcms` is the renderer's own palette contract; `brand` is\n * the tenant's compiled design system. Never mix them: a `--bcms-*` name is guaranteed to\n * exist on every page, a `--brand-*` name only when the kit declares that group. */\nexport const TOKEN_PREFIXES = [\"bcms\", \"brand\"] as const;\nexport type TokenPrefix = (typeof TOKEN_PREFIXES)[number];\n\n/**\n * Fixed renderer palette. Emitted for every page that has a brand kit, so a consumer may use\n * these unconditionally.\n *\n * ⚠️ `bg-surface` and `bg-muted` invert against the kit's own vocabulary — renderer `surface`\n * is the PAGE background and renderer `muted` is the banded section. That inversion is the\n * emitter's business (see brandCss), but it is named here so nobody re-derives it wrongly.\n */\nexport const BCMS_PALETTE_TOKENS = [\n \"--bcms-bg-surface\",\n \"--bcms-bg-muted\",\n \"--bcms-bg-accent\",\n \"--bcms-bg-dark\",\n \"--bcms-text-muted\",\n \"--bcms-accent\",\n // `primary` is the BUTTON fill, NOT body text. Binding it to the brand hue is the documented\n // trap in brandCss — brand `primary` is the brand colour while the renderer's `primary` used\n // to mean near-black body text, and mapping the two repaints every heading.\n \"--bcms-primary\",\n // The corner language, per element ROLE. Only the two roles BASE_CSS consumes are emitted:\n // a `--bcms-radius-chip` with no consumer is dead flexibility wearing a token's clothes.\n \"--bcms-radius-card\",\n \"--bcms-radius-control\",\n] as const;\nexport type BcmsPaletteToken = (typeof BCMS_PALETTE_TOKENS)[number];\n\n/** Every `--brand-*` family, with the shape of its variable part. A family with `keyed: false`\n * has exactly one name; the rest are open sets the kit decides. */\nexport const BRAND_TOKEN_FAMILIES = {\n text: { keyed: true, unit: \"px\", from: \"typeScale[].size\" },\n leading: { keyed: true, unit: \"ratio\", from: \"typeScale[].lineHeight\" },\n weight: { keyed: true, unit: \"number\", from: \"typeScale[].weight\" },\n space: { keyed: true, unit: \"px\", from: \"spacing.steps[] (index-keyed) + `base`\" },\n shadow: { keyed: true, unit: \"box-shadow\", from: \"elevation[]\" },\n duration: { keyed: true, unit: \"ms\", from: \"motion.durations\" },\n color: { keyed: true, unit: \"color\", from: \"extras[]\" },\n ease: { keyed: false, unit: \"easing\", from: \"motion.ease\" },\n} as const;\nexport type BrandTokenFamily = keyof typeof BRAND_TOKEN_FAMILIES;\n\n/**\n * A key inside a family. Deliberately narrow — these names reach a stylesheet, and the\n * grammar is what stops an author-controlled key from becoming CSS. The emitter's values are\n * already composed from validated numbers and enums; this guards the other half, the name.\n */\nconst KEY = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\n/** `brandToken(\"space\", 3)` → `--brand-space-3`. Throws on a key the grammar forbids, because\n * the alternative is emitting a property no consumer can ever read. */\nexport function brandToken(family: BrandTokenFamily, key?: string | number): string {\n const definition = BRAND_TOKEN_FAMILIES[family];\n if (!definition.keyed) {\n if (key !== undefined) throw new Error(`Token family \"${family}\" takes no key`);\n return `--brand-${family}`;\n }\n const suffix = String(key ?? \"\");\n if (!KEY.test(suffix)) throw new Error(`Invalid ${family} token key: ${JSON.stringify(key)}`);\n return `--brand-${family}-${suffix}`;\n}\n\n/** The one un-suffixed member of a keyed family: `--brand-space-base`. */\nexport const BRAND_SPACE_BASE = \"--brand-space-base\";\n\n/** Does `name` belong to the grammar at all? The check both sides run so a drift is a failing\n * test rather than a property nothing reads. */\nexport function isTokenName(name: string): boolean {\n if ((BCMS_PALETTE_TOKENS as readonly string[]).includes(name)) return true;\n if (name === BRAND_SPACE_BASE) return true;\n const match = /^--brand-([a-z]+)(?:-(.+))?$/.exec(name);\n if (!match) return false;\n const family = match[1] as BrandTokenFamily;\n const definition = BRAND_TOKEN_FAMILIES[family];\n if (!definition) return false;\n return definition.keyed ? Boolean(match[2] && KEY.test(match[2])) : match[2] === undefined;\n}\n\nexport function assertTokenName(name: string): string {\n if (!isTokenName(name)) throw new Error(`Not a BetterCMS design token: ${name}`);\n return name;\n}\n"],"mappings":";;;AAuBO,IAAM,iBAAiB,CAAC,QAAQ,OAAO;AAWvC,IAAM,sBAAsB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA,EAIA;AAAA;AAAA;AAAA,EAGA;AAAA,EACA;AACF;AAKO,IAAM,uBAAuB;AAAA,EAClC,MAAM,EAAE,OAAO,MAAM,MAAM,MAAM,MAAM,mBAAmB;AAAA,EAC1D,SAAS,EAAE,OAAO,MAAM,MAAM,SAAS,MAAM,yBAAyB;AAAA,EACtE,QAAQ,EAAE,OAAO,MAAM,MAAM,UAAU,MAAM,qBAAqB;AAAA,EAClE,OAAO,EAAE,OAAO,MAAM,MAAM,MAAM,MAAM,yCAAyC;AAAA,EACjF,QAAQ,EAAE,OAAO,MAAM,MAAM,cAAc,MAAM,cAAc;AAAA,EAC/D,UAAU,EAAE,OAAO,MAAM,MAAM,MAAM,MAAM,mBAAmB;AAAA,EAC9D,OAAO,EAAE,OAAO,MAAM,MAAM,SAAS,MAAM,WAAW;AAAA,EACtD,MAAM,EAAE,OAAO,OAAO,MAAM,UAAU,MAAM,cAAc;AAC5D;AAQA,IAAM,MAAM;AAIL,SAAS,WAAW,QAA0B,KAA+B;AAClF,QAAM,aAAa,qBAAqB,MAAM;AAC9C,MAAI,CAAC,WAAW,OAAO;AACrB,QAAI,QAAQ,OAAW,OAAM,IAAI,MAAM,iBAAiB,MAAM,gBAAgB;AAC9E,WAAO,WAAW,MAAM;AAAA,EAC1B;AACA,QAAM,SAAS,OAAO,OAAO,EAAE;AAC/B,MAAI,CAAC,IAAI,KAAK,MAAM,EAAG,OAAM,IAAI,MAAM,WAAW,MAAM,eAAe,KAAK,UAAU,GAAG,CAAC,EAAE;AAC5F,SAAO,WAAW,MAAM,IAAI,MAAM;AACpC;AAGO,IAAM,mBAAmB;AAIzB,SAAS,YAAY,MAAuB;AACjD,MAAK,oBAA0C,SAAS,IAAI,EAAG,QAAO;AACtE,MAAI,SAAS,iBAAkB,QAAO;AACtC,QAAM,QAAQ,+BAA+B,KAAK,IAAI;AACtD,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,SAAS,MAAM,CAAC;AACtB,QAAM,aAAa,qBAAqB,MAAM;AAC9C,MAAI,CAAC,WAAY,QAAO;AACxB,SAAO,WAAW,QAAQ,QAAQ,MAAM,CAAC,KAAK,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,MAAM;AACnF;AAEO,SAAS,gBAAgB,MAAsB;AACpD,MAAI,CAAC,YAAY,IAAI,EAAG,OAAM,IAAI,MAAM,iCAAiC,IAAI,EAAE;AAC/E,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../src/tokens.ts"],"sourcesContent":["/**\n * The design-token NAME grammar — one spelling, shared by everything that writes or reads a\n * BetterCMS custom property.\n *\n * Token names are a CROSS-PACKAGE grammar. The backend's brand compiler mints them, starter\n * stylesheets and block components consume them, and until now the only thing keeping the two\n * in step was that somebody typed the same string twice. A grammar with no compiler is how\n * every prop-keyspace schism in this project started: the emitter renames `--brand-leading-*`,\n * every template that reads it silently falls back to its initial value, and nothing is red.\n *\n * So the names live here, in the package both sides already depend on, as CONSTRUCTORS rather\n * than a list of strings — an indexed family (`--brand-space-3`, `--brand-text-lg`) cannot be\n * enumerated ahead of time, but its shape can be, and a builder is the only spelling either\n * side can reach. `assertTokenName` closes the loop for anything generated dynamically.\n *\n * DTCG note (FLO-1178 Lane 2): this is the NAME grammar only. What a token means — its\n * `$type`/`$value` and who owns it — belongs to the template presentation manifest. Keeping\n * names separate is what lets a template declare new knobs without the emitter changing.\n */\n\n/** The two prefixes in the system. `bcms` is the renderer's own palette contract; `brand` is\n * the tenant's compiled design system. Never mix them: a `--bcms-*` name is guaranteed to\n * exist on every page, a `--brand-*` name only when the kit declares that group. */\nexport const TOKEN_PREFIXES = [\"bcms\", \"brand\"] as const;\nexport type TokenPrefix = (typeof TOKEN_PREFIXES)[number];\n\n/**\n * Fixed renderer palette. Emitted for every page that has a brand kit, so a consumer may use\n * these unconditionally.\n *\n * ⚠️ `bg-surface` and `bg-muted` invert against the kit's own vocabulary — renderer `surface`\n * is the PAGE background and renderer `muted` is the banded section. That inversion is the\n * emitter's business (see brandCss), but it is named here so nobody re-derives it wrongly.\n */\nexport const BCMS_PALETTE_TOKENS = [\n \"--bcms-bg-surface\",\n \"--bcms-bg-muted\",\n \"--bcms-bg-accent\",\n \"--bcms-bg-dark\",\n \"--bcms-text-muted\",\n \"--bcms-accent\",\n // `primary` is the BUTTON fill, NOT body text. Binding it to the brand hue is the documented\n // trap in brandCss — brand `primary` is the brand colour while the renderer's `primary` used\n // to mean near-black body text, and mapping the two repaints every heading.\n \"--bcms-primary\",\n // The corner language, per element ROLE. Only the two roles BASE_CSS consumes are emitted:\n // a `--bcms-radius-chip` with no consumer is dead flexibility wearing a token's clothes.\n \"--bcms-radius-card\",\n \"--bcms-radius-control\",\n] as const;\nexport type BcmsPaletteToken = (typeof BCMS_PALETTE_TOKENS)[number];\n\n/** Every `--brand-*` family, with the shape of its variable part. A family with `keyed: false`\n * has exactly one name; the rest are open sets the kit decides. */\nexport const BRAND_TOKEN_FAMILIES = {\n text: { keyed: true, unit: \"px\", from: \"typeScale[].size\" },\n leading: { keyed: true, unit: \"ratio\", from: \"typeScale[].lineHeight\" },\n weight: { keyed: true, unit: \"number\", from: \"typeScale[].weight\" },\n space: { keyed: true, unit: \"px\", from: \"spacing.steps[] (index-keyed) + `base`\" },\n shadow: { keyed: true, unit: \"box-shadow\", from: \"elevation[]\" },\n duration: { keyed: true, unit: \"ms\", from: \"motion.durations\" },\n color: { keyed: true, unit: \"color\", from: \"extras[]\" },\n // A gradient is a VALUE, not a file — which is why it lives in the kit rather than the media\n // library. The emitter composes the CSS from the kit's numbers and colour keys; nothing\n // author-written reaches the stylesheet but the key in this name.\n gradient: { keyed: true, unit: \"gradient\", from: \"graphics.gradients[]\" },\n ease: { keyed: false, unit: \"easing\", from: \"motion.ease\" },\n} as const;\nexport type BrandTokenFamily = keyof typeof BRAND_TOKEN_FAMILIES;\n\n/**\n * A key inside a family. Deliberately narrow — these names reach a stylesheet, and the\n * grammar is what stops an author-controlled key from becoming CSS. The emitter's values are\n * already composed from validated numbers and enums; this guards the other half, the name.\n */\nconst KEY = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\n/** `brandToken(\"space\", 3)` → `--brand-space-3`. Throws on a key the grammar forbids, because\n * the alternative is emitting a property no consumer can ever read. */\nexport function brandToken(family: BrandTokenFamily, key?: string | number): string {\n const definition = BRAND_TOKEN_FAMILIES[family];\n if (!definition.keyed) {\n if (key !== undefined) throw new Error(`Token family \"${family}\" takes no key`);\n return `--brand-${family}`;\n }\n const suffix = String(key ?? \"\");\n if (!KEY.test(suffix)) throw new Error(`Invalid ${family} token key: ${JSON.stringify(key)}`);\n return `--brand-${family}-${suffix}`;\n}\n\n/** The one un-suffixed member of a keyed family: `--brand-space-base`. */\nexport const BRAND_SPACE_BASE = \"--brand-space-base\";\n\n/** Does `name` belong to the grammar at all? The check both sides run so a drift is a failing\n * test rather than a property nothing reads. */\nexport function isTokenName(name: string): boolean {\n if ((BCMS_PALETTE_TOKENS as readonly string[]).includes(name)) return true;\n if (name === BRAND_SPACE_BASE) return true;\n const match = /^--brand-([a-z]+)(?:-(.+))?$/.exec(name);\n if (!match) return false;\n const family = match[1] as BrandTokenFamily;\n const definition = BRAND_TOKEN_FAMILIES[family];\n if (!definition) return false;\n return definition.keyed ? Boolean(match[2] && KEY.test(match[2])) : match[2] === undefined;\n}\n\nexport function assertTokenName(name: string): string {\n if (!isTokenName(name)) throw new Error(`Not a BetterCMS design token: ${name}`);\n return name;\n}\n"],"mappings":";;;AAuBO,IAAM,iBAAiB,CAAC,QAAQ,OAAO;AAWvC,IAAM,sBAAsB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA,EAIA;AAAA;AAAA;AAAA,EAGA;AAAA,EACA;AACF;AAKO,IAAM,uBAAuB;AAAA,EAClC,MAAM,EAAE,OAAO,MAAM,MAAM,MAAM,MAAM,mBAAmB;AAAA,EAC1D,SAAS,EAAE,OAAO,MAAM,MAAM,SAAS,MAAM,yBAAyB;AAAA,EACtE,QAAQ,EAAE,OAAO,MAAM,MAAM,UAAU,MAAM,qBAAqB;AAAA,EAClE,OAAO,EAAE,OAAO,MAAM,MAAM,MAAM,MAAM,yCAAyC;AAAA,EACjF,QAAQ,EAAE,OAAO,MAAM,MAAM,cAAc,MAAM,cAAc;AAAA,EAC/D,UAAU,EAAE,OAAO,MAAM,MAAM,MAAM,MAAM,mBAAmB;AAAA,EAC9D,OAAO,EAAE,OAAO,MAAM,MAAM,SAAS,MAAM,WAAW;AAAA;AAAA;AAAA;AAAA,EAItD,UAAU,EAAE,OAAO,MAAM,MAAM,YAAY,MAAM,uBAAuB;AAAA,EACxE,MAAM,EAAE,OAAO,OAAO,MAAM,UAAU,MAAM,cAAc;AAC5D;AAQA,IAAM,MAAM;AAIL,SAAS,WAAW,QAA0B,KAA+B;AAClF,QAAM,aAAa,qBAAqB,MAAM;AAC9C,MAAI,CAAC,WAAW,OAAO;AACrB,QAAI,QAAQ,OAAW,OAAM,IAAI,MAAM,iBAAiB,MAAM,gBAAgB;AAC9E,WAAO,WAAW,MAAM;AAAA,EAC1B;AACA,QAAM,SAAS,OAAO,OAAO,EAAE;AAC/B,MAAI,CAAC,IAAI,KAAK,MAAM,EAAG,OAAM,IAAI,MAAM,WAAW,MAAM,eAAe,KAAK,UAAU,GAAG,CAAC,EAAE;AAC5F,SAAO,WAAW,MAAM,IAAI,MAAM;AACpC;AAGO,IAAM,mBAAmB;AAIzB,SAAS,YAAY,MAAuB;AACjD,MAAK,oBAA0C,SAAS,IAAI,EAAG,QAAO;AACtE,MAAI,SAAS,iBAAkB,QAAO;AACtC,QAAM,QAAQ,+BAA+B,KAAK,IAAI;AACtD,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,SAAS,MAAM,CAAC;AACtB,QAAM,aAAa,qBAAqB,MAAM;AAC9C,MAAI,CAAC,WAAY,QAAO;AACxB,SAAO,WAAW,QAAQ,QAAQ,MAAM,CAAC,KAAK,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,MAAM;AACnF;AAEO,SAAS,gBAAgB,MAAsB;AACpD,MAAI,CAAC,YAAY,IAAI,EAAG,OAAM,IAAI,MAAM,iCAAiC,IAAI,EAAE;AAC/E,SAAO;AACT;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bettercms-ai/ui",
3
- "version": "0.7.1",
3
+ "version": "0.8.1",
4
4
  "type": "module",
5
5
  "description": "BetterCMS block components + registry/renderer for rendering page block_json in React (dashboard builder preview + headless sites).",
6
6
  "sideEffects": false,
@@ -52,11 +52,11 @@
52
52
  "add": "echo 'Use npx shadcn@latest add -p ../.. <component>'"
53
53
  },
54
54
  "dependencies": {
55
- "@bettercms-ai/types": "^1.10.0",
55
+ "@bettercms-ai/types": "^1.18.0",
56
56
  "clsx": "^2.1.1",
57
57
  "tailwind-merge": "^2.6.0",
58
58
  "@portabletext/react": "^6.2.0",
59
- "@bettercms-ai/richtext": "^0.3.0"
59
+ "@bettercms-ai/richtext": "^0.4.0"
60
60
  },
61
61
  "peerDependencies": {
62
62
  "react": ">=18",