@rojaostudio/ds-core 1.1.0-next.0 → 1.1.0-next.2

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.
@@ -681,16 +681,25 @@ var WHITE = "#ffffff";
681
681
  var BLACK = "#000000";
682
682
  var black = (pct) => BLACK + Math.round(pct / 100 * 255).toString(16).padStart(2, "0");
683
683
  var white = (pct) => WHITE + Math.round(pct / 100 * 255).toString(16).padStart(2, "0");
684
- function palette(name) {
684
+ function palette(name, custom) {
685
+ const own = custom == null ? void 0 : custom[name];
686
+ if (typeof own === "string") {
687
+ if (!isHex(own)) throw new Error(`rdsTheme: palette "${name}" is not a hex colour`);
688
+ return buildScale(own);
689
+ }
690
+ if (own) return own;
685
691
  const p = palettes[name];
686
692
  if (!p || typeof p === "string") throw new Error(`rdsTheme: unknown palette "${name}"`);
687
693
  return p;
688
694
  }
689
- function ramp(c) {
695
+ function ramp(c, custom) {
690
696
  var _a;
691
697
  if (isHex(c)) return { base: c, scale: buildScale(c) };
692
- const m = c.match(/^([a-zA-Z]+)-(\d{2,3})$/);
693
- if (m) return { base: (_a = refToHex(c)) != null ? _a : palette(m[1])[Number(m[2])], scale: palette(m[1]) };
698
+ const m = c.match(/^([a-zA-Z][a-zA-Z0-9]*)-(\d{2,3})$/);
699
+ if (m) {
700
+ const scale = palette(m[1], custom);
701
+ return { base: (_a = scale[Number(m[2])]) != null ? _a : refToHex(c), scale };
702
+ }
694
703
  const hex = refToHex(c);
695
704
  if (!hex) throw new Error(`rdsTheme: cannot resolve colour "${c}"`);
696
705
  return { base: hex, scale: buildScale(hex) };
@@ -806,15 +815,16 @@ var lighterStep = (s) => s - 100 ;
806
815
  var roleVar = (role) => `--${role.replaceAll("/", "-")}`;
807
816
  function generateRdsTheme(def) {
808
817
  const b = def.brand;
809
- const P = ramp(b.primary);
810
- const S = b.secondary ? ramp(b.secondary) : P;
811
- const A = b.accent ? ramp(b.accent) : P;
812
- const N = palette(def.text && palettes[def.text] && typeof palettes[def.text] !== "string" ? def.text : "zinc");
818
+ const own = def.palettes;
819
+ const P = ramp(b.primary, own);
820
+ const S = b.secondary ? ramp(b.secondary, own) : P;
821
+ const A = b.accent ? ramp(b.accent, own) : P;
822
+ const N = def.text && ((own == null ? void 0 : own[def.text]) || palettes[def.text] && typeof palettes[def.text] !== "string") ? palette(def.text, own) : palette("zinc");
813
823
  const L = palette("blue");
814
824
  const red = palette("red"), green = palette("green"), orange = palette("orange");
815
825
  const teal = palette("teal"), purple = palette("purple");
816
- const heading = b.heading ? ramp(b.heading).base : P.base;
817
- const invert = b.invert ? ramp(b.invert).base : WHITE;
826
+ const heading = b.heading ? ramp(b.heading, own).base : P.base;
827
+ const invert = b.invert ? ramp(b.invert, own).base : WHITE;
818
828
  const l = {
819
829
  "colors/primary/light": P.scale[500],
820
830
  "colors/primary/default": P.base,
@@ -1092,6 +1102,31 @@ function rdsContrast(theme, mode, fg, bg) {
1092
1102
  if (!/^#[0-9a-f]{6}$/i.test(a) || !/^#[0-9a-f]{6}$/i.test(c)) return null;
1093
1103
  return contrastRatio(a, c);
1094
1104
  }
1105
+ var isColourRef = (v) => /^[a-z][a-z0-9-]*\/[a-z]?\d+$/i.test(v);
1106
+ function rdsThemeFromTable(table) {
1107
+ var _a;
1108
+ const out = { light: {}, dark: {}, brand: {} };
1109
+ const problems = [];
1110
+ for (const mode of ["light", "dark", "brand"]) {
1111
+ const roles = (_a = table.modes) == null ? void 0 : _a[mode];
1112
+ if (!roles) {
1113
+ problems.push(`mode "${mode}" is missing`);
1114
+ continue;
1115
+ }
1116
+ for (const [role] of ROLES) {
1117
+ const ref2 = roles[role];
1118
+ if (ref2 === void 0) problems.push(`${mode}: role "${role}" is missing`);
1119
+ else if (isColourRef(ref2)) {
1120
+ const value = table.primitives[ref2];
1121
+ if (value === void 0) problems.push(`${mode}: "${role}" points to unknown primitive "${ref2}"`);
1122
+ else out[mode][roleVar(role)] = value.toLowerCase();
1123
+ } else out[mode][roleVar(role)] = ref2;
1124
+ }
1125
+ }
1126
+ if (problems.length) throw new Error(`rdsThemeFromTable(${table.name}):
1127
+ ${problems.join("\n ")}`);
1128
+ return out;
1129
+ }
1095
1130
 
1096
1131
  // generate/emitCss.ts
1097
1132
  var SECTIONS = [
@@ -1313,4 +1348,4 @@ neutral 50 ${neutral[50]} \xB7 500 ${neutral[500]} \xB7 900 ${neutral[900]}
1313
1348
  `;
1314
1349
  }
1315
1350
 
1316
- export { ROLES, SCALE_STEPS, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, emitRdsCss, generateRdsTheme, generateTheme, hexToHsl, hslToHex, isHex, isNeutralBrand, onColor, rdsContrast, refToHex, relativeLuminance, resolveTheme, roleVar };
1351
+ export { ROLES, SCALE_STEPS, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, emitRdsCss, generateRdsTheme, generateTheme, hexToHsl, hslToHex, isHex, isNeutralBrand, onColor, rdsContrast, rdsThemeFromTable, refToHex, relativeLuminance, resolveTheme, roleVar };
@@ -28,6 +28,19 @@ type RdsCssOptions = {
28
28
  declare function emitRdsCss(theme: RdsTheme, opts?: RdsCssOptions): string;
29
29
  /** Contrast of a role pair in one mode (alpha colours are not measured: they depend on what is below). */
30
30
  declare function rdsContrast(theme: RdsTheme, mode: RdsMode, fg: string, bg: string): number | null;
31
+ /**
32
+ * A brand as drawn in the [RDS] Base Tokens file: for each theme mode, every role points to a primitive of the
33
+ * [RDS] Primitives library by name ("accyan/400"), and `primitives` holds the colour Figma resolves for each.
34
+ * Exported by `figma/export-brand.js`. The theme comes out one to one with the Figma brand mode.
35
+ */
36
+ type RdsBrandTable = {
37
+ $schema?: "rds-brand-table/1";
38
+ name: string;
39
+ primitives: Record<string, string>;
40
+ modes: Record<RdsMode, Record<string, string>>;
41
+ };
42
+ /** The theme of a brand table. Fails on a missing role or an unknown primitive, listing them all. */
43
+ declare function rdsThemeFromTable(table: RdsBrandTable): RdsTheme;
31
44
 
32
45
  /**
33
46
  * emitCss.ts — formata o output do generateTheme num theme.css legível.
@@ -110,4 +123,4 @@ declare function isNeutralBrand(def: {
110
123
  palettes?: PaletteDefs;
111
124
  }): boolean;
112
125
 
113
- export { BrandDef, type ClaudeMdOptions, type ClaudeMdTarget, ROLES as RDS_ROLES, type RdsCssOptions, type RdsMode, type RdsTheme, SCALE_STEPS, type Scale, type ScaleStep, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, emitRdsCss, generateRdsTheme, hexToHsl, hslToHex, isHex, isNeutralBrand, onColor, rdsContrast, refToHex, relativeLuminance, roleVar };
126
+ export { BrandDef, type ClaudeMdOptions, type ClaudeMdTarget, ROLES as RDS_ROLES, type RdsBrandTable, type RdsCssOptions, type RdsMode, type RdsTheme, SCALE_STEPS, type Scale, type ScaleStep, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, emitRdsCss, generateRdsTheme, hexToHsl, hslToHex, isHex, isNeutralBrand, onColor, rdsContrast, rdsThemeFromTable, refToHex, relativeLuminance, roleVar };
package/dist/generate.js CHANGED
@@ -1,3 +1,3 @@
1
- export { ROLES as RDS_ROLES, SCALE_STEPS, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, emitRdsCss, generateRdsTheme, generateTheme, hexToHsl, hslToHex, isHex, isNeutralBrand, onColor, rdsContrast, refToHex, relativeLuminance, resolveTheme, roleVar } from './chunk-CFCKPKAF.js';
1
+ export { ROLES as RDS_ROLES, SCALE_STEPS, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, emitRdsCss, generateRdsTheme, generateTheme, hexToHsl, hslToHex, isHex, isNeutralBrand, onColor, rdsContrast, rdsThemeFromTable, refToHex, relativeLuminance, resolveTheme, roleVar } from './chunk-DZAJKHNW.js';
2
2
  import './chunk-FRWRLCYM.js';
3
3
  import './chunk-ORMEWXMH.js';
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { G as GenResult, R as ResolvedTheme, T as TokenMap, g as generateTheme, r as resolveTheme } from './generateTheme-CUfNpsKs.js';
2
- export { ClaudeMdOptions, ClaudeMdTarget, RDS_ROLES, RdsCssOptions, RdsMode, RdsTheme, SCALE_STEPS, Scale, ScaleStep, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, emitRdsCss, generateRdsTheme, hexToHsl, hslToHex, isHex, isNeutralBrand, onColor, rdsContrast, refToHex, relativeLuminance, roleVar } from './generate.js';
2
+ export { ClaudeMdOptions, ClaudeMdTarget, RDS_ROLES, RdsBrandTable, RdsCssOptions, RdsMode, RdsTheme, SCALE_STEPS, Scale, ScaleStep, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, emitRdsCss, generateRdsTheme, hexToHsl, hslToHex, isHex, isNeutralBrand, onColor, rdsContrast, rdsThemeFromTable, refToHex, relativeLuminance, roleVar } from './generate.js';
3
3
  export { A as Archetype, B as BrandColors, a as BrandDef, C as ColorRef, D as DarkStrategy, F as FontDef, P as PaletteRef, S as ScaleToken, T as TextStyle, b as TextStyleName, c as TypeScale } from './recipe.schema-Cq7e82nx.js';
4
4
  export { primitives, tokens } from './tokens.js';
5
5
  export { RecipeName, recipes } from './recipes.js';
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  export { recipes } from './chunk-3QTDO2HW.js';
2
- export { ROLES as RDS_ROLES, SCALE_STEPS, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, emitRdsCss, generateRdsTheme, generateTheme, hexToHsl, hslToHex, isHex, isNeutralBrand, onColor, rdsContrast, refToHex, relativeLuminance, resolveTheme, roleVar } from './chunk-CFCKPKAF.js';
2
+ export { ROLES as RDS_ROLES, SCALE_STEPS, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, emitRdsCss, generateRdsTheme, generateTheme, hexToHsl, hslToHex, isHex, isNeutralBrand, onColor, rdsContrast, rdsThemeFromTable, refToHex, relativeLuminance, resolveTheme, roleVar } from './chunk-DZAJKHNW.js';
3
3
  export { primitives, tokens } from './chunk-FRWRLCYM.js';
4
4
  import './chunk-ORMEWXMH.js';
package/dist/themes.js CHANGED
@@ -1,4 +1,4 @@
1
- import { generateTheme } from './chunk-CFCKPKAF.js';
1
+ import { generateTheme } from './chunk-DZAJKHNW.js';
2
2
  import './chunk-FRWRLCYM.js';
3
3
  import { __spreadValues, __objRest } from './chunk-ORMEWXMH.js';
4
4
 
@@ -0,0 +1,51 @@
1
+ /* global figma */
2
+ // Exports one brand of the [RDS] Base Tokens file as a brand table for rdsThemeFromTable().
3
+ // Run inside the Base Tokens file with the Figma Plugin API (a plugin console or an agent with use_figma),
4
+ // after setting BRAND to the mode name of the `base` collection. The return value is the JSON to save.
5
+ //
6
+ // Each theme role is followed through the aliases of the `base` collection (in the brand's mode) down to a
7
+ // primitive of the [RDS] Primitives library, kept by name ("accyan/400") with the colour Figma resolves.
8
+ const BRAND = "rojao";
9
+
10
+ const cols = await figma.variables.getLocalVariableCollectionsAsync();
11
+ const base = cols.find((c) => c.name === "base");
12
+ const theme = cols.find((c) => c.name === "theme");
13
+ if (!base || !theme) throw new Error("open the [RDS] Base Tokens file");
14
+ const brandMode = base.modes.find((m) => m.name === BRAND);
15
+ if (!brandMode) throw new Error(`no brand "${BRAND}" in base: ${base.modes.map((m) => m.name).join(", ")}`);
16
+ const local = new Set(cols.map((c) => c.id));
17
+
18
+ const hex = (c) =>
19
+ "#" +
20
+ [c.r, c.g, c.b].map((x) => Math.round(x * 255).toString(16).padStart(2, "0")).join("") +
21
+ (c.a !== undefined && c.a < 1 ? Math.round(c.a * 255).toString(16).padStart(2, "0") : "");
22
+ const literal = (v, val) => (v.resolvedType === "COLOR" ? hex(val) : String(val));
23
+ const isAlias = (val) => val && typeof val === "object" && val.type === "VARIABLE_ALIAS";
24
+
25
+ const primitives = {};
26
+ async function resolve(v, themeMode) {
27
+ if (!local.has(v.variableCollectionId)) {
28
+ // A Primitives library variable: keep its name, record what it resolves to.
29
+ const val = Object.values(v.valuesByMode)[0];
30
+ if (isAlias(val)) return resolve(await figma.variables.getVariableByIdAsync(val.id), themeMode);
31
+ primitives[v.name] = literal(v, val);
32
+ return v.name;
33
+ }
34
+ const modeId = v.variableCollectionId === base.id ? brandMode.modeId
35
+ : v.variableCollectionId === theme.id ? themeMode : Object.keys(v.valuesByMode)[0];
36
+ const val = v.valuesByMode[modeId] ?? Object.values(v.valuesByMode)[0];
37
+ if (isAlias(val)) return resolve(await figma.variables.getVariableByIdAsync(val.id), themeMode);
38
+ return literal(v, val);
39
+ }
40
+
41
+ const modes = {};
42
+ for (const m of theme.modes) {
43
+ const roles = {};
44
+ for (const id of theme.variableIds) {
45
+ const v = await figma.variables.getVariableByIdAsync(id);
46
+ roles[v.name] = await resolve(v, m.modeId);
47
+ }
48
+ modes[m.name] = roles;
49
+ }
50
+ const sorted = Object.fromEntries(Object.entries(primitives).sort(([a], [b]) => a.localeCompare(b)));
51
+ return { $schema: "rds-brand-table/1", name: BRAND, primitives: sorted, modes };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rojaostudio/ds-core",
3
- "version": "1.1.0-next.0",
3
+ "version": "1.1.0-next.2",
4
4
  "description": "O motor do Rojão DS — tokens, derivação de tema e emissores. Sem React, sem Tailwind, sem CSS.",
5
5
  "license": "MIT",
6
6
  "author": "Rojão Studio (https://rojao.studio)",
@@ -54,7 +54,8 @@
54
54
  "files": [
55
55
  "LICENSE",
56
56
  "README.md",
57
- "dist"
57
+ "dist",
58
+ "figma/export-brand.js"
58
59
  ],
59
60
  "devDependencies": {
60
61
  "eslint": "^9.18.0",
@@ -68,7 +69,7 @@
68
69
  "publishConfig": {
69
70
  "registry": "https://registry.npmjs.org",
70
71
  "access": "public",
71
- "provenance": false
72
+ "provenance": true
72
73
  },
73
74
  "scripts": {
74
75
  "build": "tsup",