@rojaostudio/ds-core 1.1.0-next.6 → 1.1.0-next.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-UD53AUQQ.js → chunk-6XIFYDSU.js} +158 -32
- package/dist/generate.d.ts +51 -7
- package/dist/generate.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/themes.js +1 -1
- package/package.json +1 -1
|
@@ -705,6 +705,27 @@ function ramp(c, custom) {
|
|
|
705
705
|
return { base: hex, scale: buildScale(hex) };
|
|
706
706
|
}
|
|
707
707
|
var on = (bg) => onColor(bg);
|
|
708
|
+
var AA = 4.5;
|
|
709
|
+
var NON_TEXT = 3;
|
|
710
|
+
var readsOn = (fg, bgs, min = AA) => bgs.every((bg) => contrastRatio(fg, bg) >= min);
|
|
711
|
+
function readableFrom(base, scale, bgs, toward, fallback, min = AA) {
|
|
712
|
+
var _a;
|
|
713
|
+
if (readsOn(base, bgs, min)) return base;
|
|
714
|
+
const lb = relativeLuminance(base);
|
|
715
|
+
const steps = SCALE_STEPS.map((s2) => scale[s2]).filter((c) => !!c && isHex(c)).filter((c) => toward === "darker" ? relativeLuminance(c) < lb : relativeLuminance(c) > lb).sort((a, b) => Math.abs(relativeLuminance(a) - lb) - Math.abs(relativeLuminance(b) - lb));
|
|
716
|
+
const step2 = steps.find((c) => readsOn(c, bgs, min));
|
|
717
|
+
if (step2) return step2;
|
|
718
|
+
const [h, s, l0] = hexToHsl((_a = steps[steps.length - 1]) != null ? _a : base);
|
|
719
|
+
for (let l = l0; toward === "darker" ? l >= 0 : l <= 100; l += toward === "darker" ? -2 : 2) {
|
|
720
|
+
const c = hslToHex(h, s, l);
|
|
721
|
+
if (readsOn(c, bgs, min)) return c;
|
|
722
|
+
}
|
|
723
|
+
return fallback;
|
|
724
|
+
}
|
|
725
|
+
var defaultWarn = (m) => {
|
|
726
|
+
var _a;
|
|
727
|
+
return (_a = globalThis.console) == null ? void 0 : _a.warn(m);
|
|
728
|
+
};
|
|
708
729
|
var ROLES = [
|
|
709
730
|
["colors/primary/light", "d", "b"],
|
|
710
731
|
["colors/primary/default", "d", "b"],
|
|
@@ -808,12 +829,16 @@ var ROLES = [
|
|
|
808
829
|
["colors/state/info-strong", "d", "d"],
|
|
809
830
|
["colors/state/neutral-strong", "d", "d"],
|
|
810
831
|
["text/on/primary-subtle", "d", "b"],
|
|
811
|
-
["surface/tint/subtle", "d", "b"]
|
|
832
|
+
["surface/tint/subtle", "d", "b"],
|
|
833
|
+
["border/error", "d", "d"]
|
|
812
834
|
];
|
|
835
|
+
var awayFrom = (bg) => contrastRatio(bg, BLACK) >= contrastRatio(bg, WHITE) ? "darker" : "lighter";
|
|
813
836
|
var TINT_DEFAULT = 200;
|
|
814
837
|
var lighterStep = (s) => s - 100 ;
|
|
815
838
|
var roleVar = (role) => `--${role.replaceAll("/", "-")}`;
|
|
816
|
-
function generateRdsTheme(def) {
|
|
839
|
+
function generateRdsTheme(def, opts = {}) {
|
|
840
|
+
var _a, _b;
|
|
841
|
+
const warn = (_a = opts.warn) != null ? _a : defaultWarn;
|
|
817
842
|
const b = def.brand;
|
|
818
843
|
const own = def.palettes;
|
|
819
844
|
const P = ramp(b.primary, own);
|
|
@@ -823,7 +848,15 @@ function generateRdsTheme(def) {
|
|
|
823
848
|
const L = palette("blue");
|
|
824
849
|
const red = palette("red"), green = palette("green"), orange = palette("orange");
|
|
825
850
|
const teal = palette("teal"), purple = palette("purple");
|
|
826
|
-
const
|
|
851
|
+
const lightBgs = [WHITE, N[100]];
|
|
852
|
+
let heading;
|
|
853
|
+
if (b.heading) {
|
|
854
|
+
heading = ramp(b.heading, own).base;
|
|
855
|
+
if (!readsOn(heading, lightBgs)) {
|
|
856
|
+
const worst = Math.min(...lightBgs.map((bg) => contrastRatio(heading, bg)));
|
|
857
|
+
warn(`generateRdsTheme(${def.name}): heading ${heading} is ${worst.toFixed(2)}:1 on surface/card or surface/page (AA asks 4.5:1). Kept as given.`);
|
|
858
|
+
}
|
|
859
|
+
} else heading = readableFrom(P.base, P.scale, lightBgs, "darker", BLACK);
|
|
827
860
|
const invert = b.invert ? ramp(b.invert, own).base : WHITE;
|
|
828
861
|
const l = {
|
|
829
862
|
"colors/primary/light": P.scale[500],
|
|
@@ -860,13 +893,12 @@ function generateRdsTheme(def) {
|
|
|
860
893
|
"border/strong-hover": N[600],
|
|
861
894
|
"surface/tint/default": A.scale[TINT_DEFAULT],
|
|
862
895
|
"surface/tint/strong": A.scale[300],
|
|
863
|
-
"text/on/tint": BLACK,
|
|
864
896
|
// Light selection fill (pressed Toggle, choice tile): one step lighter than the default tint
|
|
865
897
|
// (200 → 100, 100 → 50).
|
|
866
898
|
"surface/tint/subtle": A.scale[lighterStep(TINT_DEFAULT)],
|
|
867
899
|
"surface/action/default": L[100],
|
|
868
900
|
"surface/action/strong": L[200],
|
|
869
|
-
"text/on/action-tonal": P.scale[700],
|
|
901
|
+
"text/on/action-tonal": readableFrom(P.scale[700], P.scale, [L[100]], "darker", BLACK),
|
|
870
902
|
"surface/lift/action": P.scale[50],
|
|
871
903
|
"surface/lift/action-strong": L[100],
|
|
872
904
|
"surface/band/action": L[100],
|
|
@@ -901,8 +933,7 @@ function generateRdsTheme(def) {
|
|
|
901
933
|
"text/warning": orange[700],
|
|
902
934
|
"surface/neutral": N[100],
|
|
903
935
|
"text/neutral": N[800],
|
|
904
|
-
"chart/series/
|
|
905
|
-
"chart/series/2": orange[500],
|
|
936
|
+
"chart/series/2": orange[600],
|
|
906
937
|
"chart/series/3": teal[500],
|
|
907
938
|
"chart/series/4": purple[500],
|
|
908
939
|
"chart/series/5": green[500],
|
|
@@ -927,8 +958,11 @@ function generateRdsTheme(def) {
|
|
|
927
958
|
["colors/state/neutral", "text/on/neutral"],
|
|
928
959
|
["surface/cover", "text/on/cover"],
|
|
929
960
|
["surface/lift/action", "text/on/lift-action"],
|
|
930
|
-
["colors/primary/dark", "text/on/primary-strong"]
|
|
961
|
+
["colors/primary/dark", "text/on/primary-strong"],
|
|
962
|
+
["surface/tint/default", "text/on/tint"]
|
|
931
963
|
]) l[text] = on(l[fill]);
|
|
964
|
+
l["border/error"] = readableFrom(l["colors/state/error"], red, [l["surface/card"]], awayFrom(l["surface/card"]), BLACK, NON_TEXT);
|
|
965
|
+
l["chart/series/1"] = readableFrom(P.scale[600], P.scale, [l["surface/card"]], awayFrom(l["surface/card"]), BLACK, NON_TEXT);
|
|
932
966
|
l["logo/primary"] = l["colors/primary/default"];
|
|
933
967
|
l["logo/signature"] = l["colors/primary/default"];
|
|
934
968
|
l["logo/accent"] = l["colors/accent/logo"];
|
|
@@ -945,7 +979,7 @@ function generateRdsTheme(def) {
|
|
|
945
979
|
"border/default": N[800],
|
|
946
980
|
"border/strong": N[500],
|
|
947
981
|
"border/strong-hover": N[400],
|
|
948
|
-
"text/heading": N[50],
|
|
982
|
+
"text/heading": readableFrom(N[50], N, [N[900]], "lighter", WHITE),
|
|
949
983
|
"text/body": N[100],
|
|
950
984
|
"text/muted": N[300],
|
|
951
985
|
"text/subtle": N[400],
|
|
@@ -972,15 +1006,13 @@ function generateRdsTheme(def) {
|
|
|
972
1006
|
"colors/accent/mark": A.scale[300],
|
|
973
1007
|
"surface/tint/default": A.scale[900],
|
|
974
1008
|
"surface/tint/strong": A.scale[800],
|
|
975
|
-
"text/on/tint": WHITE,
|
|
976
1009
|
// No step below 900 in dark: subtle is the default tint.
|
|
977
1010
|
"surface/tint/subtle": A.scale[900],
|
|
978
1011
|
"surface/action/default": L[900],
|
|
979
1012
|
"surface/action/strong": L[800],
|
|
980
|
-
"text/on/action-tonal": P.scale[200],
|
|
1013
|
+
"text/on/action-tonal": readableFrom(P.scale[200], P.scale, [L[900]], "lighter", WHITE),
|
|
981
1014
|
"surface/lift/action": P.scale[800],
|
|
982
1015
|
"surface/lift/action-strong": P.scale[700],
|
|
983
|
-
"text/on/lift-action": WHITE,
|
|
984
1016
|
"colors/state/error-strong": red[400],
|
|
985
1017
|
"colors/state/success-strong": green[300],
|
|
986
1018
|
"colors/state/warning-strong": orange[400],
|
|
@@ -995,7 +1027,6 @@ function generateRdsTheme(def) {
|
|
|
995
1027
|
"text/warning": orange[300],
|
|
996
1028
|
"surface/neutral": N[800],
|
|
997
1029
|
"text/neutral": N[200],
|
|
998
|
-
"chart/series/1": P.scale[400],
|
|
999
1030
|
"chart/series/2": orange[400],
|
|
1000
1031
|
"chart/series/3": teal[400],
|
|
1001
1032
|
"chart/series/4": purple[400],
|
|
@@ -1014,12 +1045,20 @@ function generateRdsTheme(def) {
|
|
|
1014
1045
|
["colors/secondary/default", "text/on/secondary"],
|
|
1015
1046
|
["colors/accent/default", "text/on/accent"],
|
|
1016
1047
|
["colors/accent/mark", "text/on/accent-mark"],
|
|
1017
|
-
["colors/primary/dark", "text/on/primary-strong"]
|
|
1048
|
+
["colors/primary/dark", "text/on/primary-strong"],
|
|
1049
|
+
["surface/tint/default", "text/on/tint"],
|
|
1050
|
+
["surface/lift/action", "text/on/lift-action"]
|
|
1018
1051
|
]) d[text] = on(d[fill]);
|
|
1052
|
+
d["border/error"] = readableFrom(d["colors/state/error-strong"], red, [d["surface/card"]], awayFrom(d["surface/card"]), WHITE, NON_TEXT);
|
|
1053
|
+
d["chart/series/1"] = readableFrom(P.scale[400], P.scale, [d["surface/card"]], awayFrom(d["surface/card"]), WHITE, NON_TEXT);
|
|
1019
1054
|
const plate = l["colors/primary/default"];
|
|
1020
1055
|
const ink = on(plate);
|
|
1021
1056
|
const inkA = ink === WHITE ? white : black;
|
|
1022
1057
|
const antiInk = ink === WHITE ? black : white;
|
|
1058
|
+
const lp = relativeLuminance(plate);
|
|
1059
|
+
const isPlate = (c) => c.toLowerCase() === plate.toLowerCase();
|
|
1060
|
+
const plateCard = P.scale[800] && !isPlate(P.scale[800]) && contrastRatio(ink, P.scale[800]) >= AA ? P.scale[800] : (_b = SCALE_STEPS.map((st) => P.scale[st]).filter((c) => !!c && isHex(c) && !isPlate(c)).sort((a, b2) => Math.abs(relativeLuminance(a) - lp) - Math.abs(relativeLuminance(b2) - lp)).find((c) => contrastRatio(ink, c) >= AA)) != null ? _b : plate;
|
|
1061
|
+
const onPrimaryStrong = contrastRatio(plate, P.scale[100]) >= AA ? plate : on(P.scale[100]);
|
|
1023
1062
|
const br = {
|
|
1024
1063
|
"colors/primary/light": P.scale[300],
|
|
1025
1064
|
"colors/primary/default": ink,
|
|
@@ -1033,7 +1072,7 @@ function generateRdsTheme(def) {
|
|
|
1033
1072
|
"text/body": ink,
|
|
1034
1073
|
"text/link": contrastRatio(l["colors/accent/invert"], plate) >= 4.5 ? l["colors/accent/invert"] : ink,
|
|
1035
1074
|
"surface/page": plate,
|
|
1036
|
-
"surface/card":
|
|
1075
|
+
"surface/card": plateCard,
|
|
1037
1076
|
"surface/panel": plate,
|
|
1038
1077
|
"border/default": inkA(20),
|
|
1039
1078
|
"surface/tint/default": inkA(10),
|
|
@@ -1055,7 +1094,7 @@ function generateRdsTheme(def) {
|
|
|
1055
1094
|
"surface/lift/action": inkA(10),
|
|
1056
1095
|
"text/on/lift-action": ink,
|
|
1057
1096
|
"surface/lift/action-strong": inkA(20),
|
|
1058
|
-
"text/on/primary-strong":
|
|
1097
|
+
"text/on/primary-strong": onPrimaryStrong,
|
|
1059
1098
|
"text/subtle": inkA(60),
|
|
1060
1099
|
"surface/muted": inkA(10),
|
|
1061
1100
|
"surface/muted-strong": inkA(20),
|
|
@@ -1063,11 +1102,12 @@ function generateRdsTheme(def) {
|
|
|
1063
1102
|
"border/strong-hover": inkA(80),
|
|
1064
1103
|
"text/on/primary-subtle": inkA(30)
|
|
1065
1104
|
};
|
|
1105
|
+
const brandError = readableFrom(red[300], red, [plateCard], awayFrom(plateCard), ink, NON_TEXT);
|
|
1066
1106
|
const out = { light: {}, dark: {}, brand: {} };
|
|
1067
1107
|
const pick = (src, role) => {
|
|
1068
|
-
var
|
|
1108
|
+
var _a2;
|
|
1069
1109
|
const table = src === "l" ? l : src === "d" ? d : br;
|
|
1070
|
-
const v = (
|
|
1110
|
+
const v = (_a2 = table[role]) != null ? _a2 : l[role];
|
|
1071
1111
|
if (v === void 0) throw new Error(`rdsTheme: no value for "${role}"`);
|
|
1072
1112
|
return v;
|
|
1073
1113
|
};
|
|
@@ -1076,25 +1116,58 @@ function generateRdsTheme(def) {
|
|
|
1076
1116
|
out.dark[roleVar(role)] = pick(darkSrc, role);
|
|
1077
1117
|
out.brand[roleVar(role)] = pick(brandSrc, role);
|
|
1078
1118
|
}
|
|
1119
|
+
out.brand[roleVar("border/error")] = brandError;
|
|
1079
1120
|
return out;
|
|
1080
1121
|
}
|
|
1122
|
+
var RDS_SCOPE_SELECTORS = [
|
|
1123
|
+
":root",
|
|
1124
|
+
".ds-scope",
|
|
1125
|
+
"[data-rds-scope]",
|
|
1126
|
+
".dark",
|
|
1127
|
+
"[data-rds-mode]",
|
|
1128
|
+
".ds-plate",
|
|
1129
|
+
"[data-rds-plate]"
|
|
1130
|
+
];
|
|
1131
|
+
var RDS_TOKEN_SCOPE = RDS_SCOPE_SELECTORS.join(", ");
|
|
1132
|
+
var split = (list) => list.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1133
|
+
var rootAnchored = (sel) => /^(:root|html)(?![\w-])/i.test(sel);
|
|
1134
|
+
var subject = (sel) => {
|
|
1135
|
+
var _a;
|
|
1136
|
+
return (_a = sel.trim().split(/\s*[>+~]\s*|\s+/).pop()) != null ? _a : sel;
|
|
1137
|
+
};
|
|
1138
|
+
var COVERING = /\.(ds-scope|dark|ds-plate)(?![\w-])|\[\s*data-rds-(scope|mode|plate)\s*([~|^$*]?=[^\]]*)?\]/;
|
|
1139
|
+
var covered = (sel) => {
|
|
1140
|
+
const s = subject(sel);
|
|
1141
|
+
return /^(:root|html)(?![\w-])/i.test(s) || COVERING.test(s);
|
|
1142
|
+
};
|
|
1081
1143
|
function emitRdsCss(theme, opts = {}) {
|
|
1082
1144
|
var _a, _b, _c;
|
|
1083
|
-
const
|
|
1084
|
-
const
|
|
1085
|
-
const
|
|
1086
|
-
const block2 = (
|
|
1145
|
+
const scopes = split((_a = opts.scope) != null ? _a : ":root, .ds-scope, [data-rds-scope]");
|
|
1146
|
+
const darks = split((_b = opts.dark) != null ? _b : '.dark, [data-rds-mode="dark"]');
|
|
1147
|
+
const plates = split((_c = opts.plate) != null ? _c : ".ds-plate, [data-rds-plate]");
|
|
1148
|
+
const block2 = (sels, map, only) => {
|
|
1087
1149
|
const lines = Object.entries(map).filter(([k, v]) => !only || only[k] !== v).map(([k, v]) => ` ${k}: ${k === "--type-font-mono" ? `"${v}", monospace` : v};`);
|
|
1088
|
-
return `${
|
|
1150
|
+
return `${sels.join(", ")} {
|
|
1089
1151
|
${lines.join("\n")}
|
|
1090
1152
|
}`;
|
|
1091
1153
|
};
|
|
1092
|
-
const
|
|
1093
|
-
|
|
1154
|
+
const darkSels = scopes.flatMap(
|
|
1155
|
+
(s) => darks.flatMap((d) => {
|
|
1156
|
+
if (rootAnchored(d)) return s === ":root" ? [d] : [`${d} ${s}`];
|
|
1157
|
+
return s === ":root" ? [`:root${d}`, d] : [`${s}${d}`, `${d} ${s}`];
|
|
1158
|
+
})
|
|
1159
|
+
);
|
|
1160
|
+
if (!opts.allowUncovered) {
|
|
1161
|
+
const bad = [...new Set([...scopes, ...darkSels, ...plates].filter((sel) => !covered(sel)))];
|
|
1162
|
+
if (bad.length)
|
|
1163
|
+
throw new Error(
|
|
1164
|
+
`emitRdsCss: ${bad.map((b) => `"${b}"`).join(", ")} ${bad.length > 1 ? "are" : "is"} not covered by the component tokens of @rojaostudio/ds, redeclared only on ${RDS_TOKEN_SCOPE}. The components inside would keep the colours resolved at :root. Anchor the selector at the root (':root[data-theme="dark"]', as next-themes sets it on <html>), or add the attribute the tokens cover: data-rds-scope on a scope ('.my-scope[data-rds-scope]'), data-rds-mode on a dark element ('[data-rds-mode="dark"]'), data-rds-plate on a plate. Pass allowUncovered: true only for a theme without the components.`
|
|
1165
|
+
);
|
|
1166
|
+
}
|
|
1094
1167
|
return [
|
|
1095
|
-
block2(
|
|
1096
|
-
block2(
|
|
1097
|
-
block2(
|
|
1168
|
+
block2(scopes, __spreadValues(__spreadValues({}, theme.light), theme.vars)),
|
|
1169
|
+
block2(darkSels, theme.dark, theme.light),
|
|
1170
|
+
block2(plates, theme.brand, theme.light)
|
|
1098
1171
|
].join("\n\n") + "\n";
|
|
1099
1172
|
}
|
|
1100
1173
|
function rdsContrast(theme, mode, fg, bg) {
|
|
@@ -1102,11 +1175,48 @@ function rdsContrast(theme, mode, fg, bg) {
|
|
|
1102
1175
|
if (!/^#[0-9a-f]{6}$/i.test(a) || !/^#[0-9a-f]{6}$/i.test(c)) return null;
|
|
1103
1176
|
return contrastRatio(a, c);
|
|
1104
1177
|
}
|
|
1178
|
+
var RDS_CONTRAST_PAIRS = [
|
|
1179
|
+
["text/heading", "surface/page"],
|
|
1180
|
+
["text/heading", "surface/card"],
|
|
1181
|
+
["text/body", "surface/page"],
|
|
1182
|
+
["text/body", "surface/card"],
|
|
1183
|
+
["text/muted", "surface/card"],
|
|
1184
|
+
["text/link", "surface/card"],
|
|
1185
|
+
["text/on/primary", "colors/primary/default"],
|
|
1186
|
+
["text/on/secondary", "colors/secondary/default"],
|
|
1187
|
+
["text/on/accent", "colors/accent/default"],
|
|
1188
|
+
["text/on/accent-mark", "colors/accent/mark"],
|
|
1189
|
+
["text/on/tint", "surface/tint/default"],
|
|
1190
|
+
["text/on/action-tonal", "surface/action/default"],
|
|
1191
|
+
["text/on/lift-action", "surface/lift/action"],
|
|
1192
|
+
["text/on/primary-strong", "colors/primary/dark"],
|
|
1193
|
+
["text/on/cover", "surface/cover"],
|
|
1194
|
+
["text/on/error", "colors/state/error"],
|
|
1195
|
+
["text/on/success", "colors/state/success"],
|
|
1196
|
+
["text/on/info", "colors/state/info"],
|
|
1197
|
+
["text/on/warning", "colors/state/warning"],
|
|
1198
|
+
["text/on/neutral", "colors/state/neutral"],
|
|
1199
|
+
["text/error", "surface/error"]
|
|
1200
|
+
];
|
|
1201
|
+
function rdsContrastReport(theme) {
|
|
1202
|
+
const out = [];
|
|
1203
|
+
for (const mode of ["light", "dark", "brand"])
|
|
1204
|
+
for (const [fg, bg] of RDS_CONTRAST_PAIRS) {
|
|
1205
|
+
const ratio = rdsContrast(theme, mode, fg, bg);
|
|
1206
|
+
if (ratio !== null && ratio < AA) out.push({ mode, fg, bg, ratio: Math.round(ratio * 100) / 100 });
|
|
1207
|
+
}
|
|
1208
|
+
return out;
|
|
1209
|
+
}
|
|
1105
1210
|
var isColourRef = (v) => /^[a-z][a-z0-9-]*\/[a-z]?\d+$/i.test(v);
|
|
1106
|
-
|
|
1107
|
-
|
|
1211
|
+
var ADDED_ROLES = {
|
|
1212
|
+
// Figma: light → base colors/state/error, dark → base dark/colors/state/error-strong, brand → base dark/text/error.
|
|
1213
|
+
"border/error": { light: "colors/state/error", dark: "colors/state/error-strong", brand: "text/error" }
|
|
1214
|
+
};
|
|
1215
|
+
function rdsThemeFromTable(table, opts = {}) {
|
|
1216
|
+
var _a, _b, _c;
|
|
1108
1217
|
const out = { light: {}, dark: {}, brand: {} };
|
|
1109
1218
|
const problems = [];
|
|
1219
|
+
const derived = /* @__PURE__ */ new Set();
|
|
1110
1220
|
for (const mode of ["light", "dark", "brand"]) {
|
|
1111
1221
|
const roles = (_a = table.modes) == null ? void 0 : _a[mode];
|
|
1112
1222
|
if (!roles) {
|
|
@@ -1114,7 +1224,12 @@ function rdsThemeFromTable(table) {
|
|
|
1114
1224
|
continue;
|
|
1115
1225
|
}
|
|
1116
1226
|
for (const [role] of ROLES) {
|
|
1117
|
-
|
|
1227
|
+
let ref2 = roles[role];
|
|
1228
|
+
const added = (_b = ADDED_ROLES[role]) == null ? void 0 : _b[mode];
|
|
1229
|
+
if (ref2 === void 0 && added && roles[added] !== void 0) {
|
|
1230
|
+
ref2 = roles[added];
|
|
1231
|
+
derived.add(role);
|
|
1232
|
+
}
|
|
1118
1233
|
if (ref2 === void 0) problems.push(`${mode}: role "${role}" is missing`);
|
|
1119
1234
|
else if (isColourRef(ref2)) {
|
|
1120
1235
|
const value = table.primitives[ref2];
|
|
@@ -1138,6 +1253,17 @@ function rdsThemeFromTable(table) {
|
|
|
1138
1253
|
}
|
|
1139
1254
|
if (problems.length) throw new Error(`rdsThemeFromTable(${table.name}):
|
|
1140
1255
|
${problems.join("\n ")}`);
|
|
1256
|
+
const warn = (_c = opts.warn) != null ? _c : defaultWarn;
|
|
1257
|
+
if (derived.size)
|
|
1258
|
+
warn(
|
|
1259
|
+
`rdsThemeFromTable(${table.name}): the table was exported before ${[...derived].map((r) => `"${r}"`).join(", ")} existed in the [RDS] theme. Taken from the tokens Figma aliases it to (` + [...derived].map((r) => Object.entries(ADDED_ROLES[r]).map(([m, src]) => `${m}: ${src}`).join(", ")).join("; ") + `). Export the table again with figma/export-brand.js.`
|
|
1260
|
+
);
|
|
1261
|
+
const fails = rdsContrastReport(out);
|
|
1262
|
+
if (fails.length)
|
|
1263
|
+
warn(
|
|
1264
|
+
`rdsThemeFromTable(${table.name}): ${fails.length} text pair(s) below 4.5:1
|
|
1265
|
+
` + fails.map((f) => `${f.mode}: ${f.fg} on ${f.bg} ${f.ratio}:1`).join("\n ")
|
|
1266
|
+
);
|
|
1141
1267
|
return out;
|
|
1142
1268
|
}
|
|
1143
1269
|
|
|
@@ -1365,4 +1491,4 @@ Brand's own variables (same value in every mode): ${own.map((v) => `\`${v}\``).j
|
|
|
1365
1491
|
`;
|
|
1366
1492
|
}
|
|
1367
1493
|
|
|
1368
|
-
export { ROLES, SCALE_STEPS, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, emitRdsCss, generateRdsTheme, generateTheme, hexToHsl, hslToHex, isHex, isNeutralBrand, onColor, rdsContrast, rdsThemeFromTable, refToHex, relativeLuminance, resolveTheme, roleVar };
|
|
1494
|
+
export { RDS_CONTRAST_PAIRS, RDS_SCOPE_SELECTORS, RDS_TOKEN_SCOPE, ROLES, SCALE_STEPS, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, emitRdsCss, generateRdsTheme, generateTheme, hexToHsl, hslToHex, isHex, isNeutralBrand, onColor, rdsContrast, rdsContrastReport, rdsThemeFromTable, refToHex, relativeLuminance, resolveTheme, roleVar };
|
package/dist/generate.d.ts
CHANGED
|
@@ -7,6 +7,10 @@ type RdsTheme = Record<RdsMode, Record<string, string>> & {
|
|
|
7
7
|
/** The brand's own variables (Figma `brand` collection, `<brand>/<name>`), one value for every mode. */
|
|
8
8
|
vars?: Record<string, string>;
|
|
9
9
|
};
|
|
10
|
+
type RdsThemeOptions = {
|
|
11
|
+
/** Where the warnings go (an explicit BrandDef.heading that fails AA). Default console.warn. */
|
|
12
|
+
warn?: (message: string) => void;
|
|
13
|
+
};
|
|
10
14
|
/**
|
|
11
15
|
* Roles of the [RDS] theme collection, in Figma order: [name, dark source, brand source].
|
|
12
16
|
* The light mode always reads the plain base token. In dark and brand, Figma points either to the
|
|
@@ -15,22 +19,57 @@ type RdsTheme = Record<RdsMode, Record<string, string>> & {
|
|
|
15
19
|
declare const ROLES: ReadonlyArray<readonly [string, "l" | "d", "l" | "d" | "b"]>;
|
|
16
20
|
/** CSS custom property of a role: the Figma path with hyphens (the [RDS] codeSyntax rule). */
|
|
17
21
|
declare const roleVar: (role: string) => string;
|
|
18
|
-
declare function generateRdsTheme(def: BrandDef): RdsTheme;
|
|
22
|
+
declare function generateRdsTheme(def: BrandDef, opts?: RdsThemeOptions): RdsTheme;
|
|
23
|
+
/**
|
|
24
|
+
* The selectors on which @rojaostudio/ds redeclares its component tokens (styles/rds/components.css and
|
|
25
|
+
* foundation.css). A component token holds `var(--theme-role)`, and a custom property is resolved on the element
|
|
26
|
+
* where it is declared: a theme scope that none of these match would repaint the roles but not the components
|
|
27
|
+
* inside it, which keep the colours resolved at :root. The attributes are the generic way in: put
|
|
28
|
+
* `data-rds-scope` on a scope element, `data-rds-mode="dark"` on a dark one, `data-rds-plate` on a plate.
|
|
29
|
+
*/
|
|
30
|
+
declare const RDS_SCOPE_SELECTORS: readonly [":root", ".ds-scope", "[data-rds-scope]", ".dark", "[data-rds-mode]", ".ds-plate", "[data-rds-plate]"];
|
|
31
|
+
/** RDS_SCOPE_SELECTORS as one selector list, as the component token layer is emitted. */
|
|
32
|
+
declare const RDS_TOKEN_SCOPE: string;
|
|
19
33
|
type RdsCssOptions = {
|
|
20
|
-
/** Scope of the light mode. Default `:root, .ds-scope`. */
|
|
34
|
+
/** Scope of the light mode. Default `:root, .ds-scope, [data-rds-scope]`. */
|
|
21
35
|
scope?: string;
|
|
22
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* Selector of the dark mode, combined with each scope. Default `.dark, [data-rds-mode="dark"]`. A selector
|
|
38
|
+
* anchored at the root (`:root[data-theme="dark"]`, `html.dark`) is used as is: the theme switches on <html>,
|
|
39
|
+
* as next-themes does.
|
|
40
|
+
*/
|
|
23
41
|
dark?: string;
|
|
24
|
-
/** Selector of the brand plate (a section painted with the primary). Default `.ds-plate`. */
|
|
42
|
+
/** Selector of the brand plate (a section painted with the primary). Default `.ds-plate, [data-rds-plate]`. */
|
|
25
43
|
plate?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Skip the check that every selector is covered by the component tokens of @rojaostudio/ds (RDS_SCOPE_SELECTORS).
|
|
46
|
+
* Only for a theme read by your own CSS, without the components.
|
|
47
|
+
*/
|
|
48
|
+
allowUncovered?: boolean;
|
|
26
49
|
};
|
|
27
50
|
/**
|
|
28
51
|
* The theme as CSS. Dark and plate only carry what differs from light: they inherit the rest
|
|
29
52
|
* through the cascade, like the [RDS] modes that point back to the plain token.
|
|
53
|
+
*
|
|
54
|
+
* Throws when a selector would leave the components of @rojaostudio/ds on the root colours (see
|
|
55
|
+
* RDS_SCOPE_SELECTORS), unless `allowUncovered`.
|
|
30
56
|
*/
|
|
31
57
|
declare function emitRdsCss(theme: RdsTheme, opts?: RdsCssOptions): string;
|
|
32
58
|
/** Contrast of a role pair in one mode (alpha colours are not measured: they depend on what is below). */
|
|
33
59
|
declare function rdsContrast(theme: RdsTheme, mode: RdsMode, fg: string, bg: string): number | null;
|
|
60
|
+
/**
|
|
61
|
+
* The main text pairs of the theme: [text role, the surface it sits on]. Measured in every mode; a pair with an
|
|
62
|
+
* alpha colour is skipped (its contrast depends on what is below).
|
|
63
|
+
*/
|
|
64
|
+
declare const RDS_CONTRAST_PAIRS: ReadonlyArray<readonly [string, string]>;
|
|
65
|
+
type RdsContrastFailure = {
|
|
66
|
+
mode: RdsMode;
|
|
67
|
+
fg: string;
|
|
68
|
+
bg: string;
|
|
69
|
+
ratio: number;
|
|
70
|
+
};
|
|
71
|
+
/** The pairs of RDS_CONTRAST_PAIRS that fail WCAG AA (4.5:1), mode by mode. Empty means every pair passes. */
|
|
72
|
+
declare function rdsContrastReport(theme: RdsTheme): RdsContrastFailure[];
|
|
34
73
|
/**
|
|
35
74
|
* A brand as drawn in the [RDS] Base Tokens file: for each theme mode, every role points to a primitive of the
|
|
36
75
|
* [RDS] Primitives library by name ("accyan/400"), and `primitives` holds the colour Figma resolves for each.
|
|
@@ -44,8 +83,13 @@ type RdsBrandTable = {
|
|
|
44
83
|
/** The brand's own variables of the `brand` collection, by Figma path ("marca/ciano") → primitive or value. */
|
|
45
84
|
vars?: Record<string, string>;
|
|
46
85
|
};
|
|
47
|
-
/**
|
|
48
|
-
|
|
86
|
+
/**
|
|
87
|
+
* The theme of a brand table. Fails on a missing role or an unknown primitive, listing them all. A table exported
|
|
88
|
+
* before a role of ADDED_ROLES existed is the exception: the role is taken from the token Figma aliases it to, and
|
|
89
|
+
* `opts.warn` asks to export the table again. The contrast of the main text pairs (rdsContrastReport) is only
|
|
90
|
+
* reported, through `opts.warn`: the table is the brand as drawn in Figma, kept one to one even where a pair fails.
|
|
91
|
+
*/
|
|
92
|
+
declare function rdsThemeFromTable(table: RdsBrandTable, opts?: RdsThemeOptions): RdsTheme;
|
|
49
93
|
|
|
50
94
|
/**
|
|
51
95
|
* emitCss.ts — formata o output do generateTheme num theme.css legível.
|
|
@@ -149,4 +193,4 @@ declare function isNeutralBrand(def: {
|
|
|
149
193
|
palettes?: PaletteDefs;
|
|
150
194
|
}): boolean;
|
|
151
195
|
|
|
152
|
-
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 };
|
|
196
|
+
export { BrandDef, type ClaudeMdOptions, type ClaudeMdTarget, RDS_CONTRAST_PAIRS, ROLES as RDS_ROLES, RDS_SCOPE_SELECTORS, RDS_TOKEN_SCOPE, type RdsBrandTable, type RdsContrastFailure, type RdsCssOptions, type RdsMode, type RdsTheme, type RdsThemeOptions, SCALE_STEPS, type Scale, type ScaleStep, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, emitRdsCss, generateRdsTheme, hexToHsl, hslToHex, isHex, isNeutralBrand, onColor, rdsContrast, rdsContrastReport, 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, rdsThemeFromTable, refToHex, relativeLuminance, resolveTheme, roleVar } from './chunk-
|
|
1
|
+
export { RDS_CONTRAST_PAIRS, ROLES as RDS_ROLES, RDS_SCOPE_SELECTORS, RDS_TOKEN_SCOPE, SCALE_STEPS, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, emitRdsCss, generateRdsTheme, generateTheme, hexToHsl, hslToHex, isHex, isNeutralBrand, onColor, rdsContrast, rdsContrastReport, rdsThemeFromTable, refToHex, relativeLuminance, resolveTheme, roleVar } from './chunk-6XIFYDSU.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, 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';
|
|
2
|
+
export { ClaudeMdOptions, ClaudeMdTarget, RDS_CONTRAST_PAIRS, RDS_ROLES, RDS_SCOPE_SELECTORS, RDS_TOKEN_SCOPE, RdsBrandTable, RdsContrastFailure, RdsCssOptions, RdsMode, RdsTheme, RdsThemeOptions, SCALE_STEPS, Scale, ScaleStep, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, emitRdsCss, generateRdsTheme, hexToHsl, hslToHex, isHex, isNeutralBrand, onColor, rdsContrast, rdsContrastReport, 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-PV6GFEAM.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-
|
|
2
|
+
export { RDS_CONTRAST_PAIRS, ROLES as RDS_ROLES, RDS_SCOPE_SELECTORS, RDS_TOKEN_SCOPE, SCALE_STEPS, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, emitRdsCss, generateRdsTheme, generateTheme, hexToHsl, hslToHex, isHex, isNeutralBrand, onColor, rdsContrast, rdsContrastReport, rdsThemeFromTable, refToHex, relativeLuminance, resolveTheme, roleVar } from './chunk-6XIFYDSU.js';
|
|
3
3
|
export { primitives, tokens } from './chunk-FRWRLCYM.js';
|
|
4
4
|
import './chunk-ORMEWXMH.js';
|
package/dist/themes.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rojaostudio/ds-core",
|
|
3
|
-
"version": "1.1.0-next.
|
|
3
|
+
"version": "1.1.0-next.8",
|
|
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)",
|