Skip to content

Commit 2d21d0f

Browse files
committed
brand - warn on invalid color name usage
1 parent e8cb90f commit 2d21d0f

File tree

3 files changed

+183
-13
lines changed

3 files changed

+183
-13
lines changed

src/core/brand/brand.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import {
1818
import { InternalError } from "../lib/error.ts";
1919

2020
import { join, relative } from "../../deno_ral/path.ts";
21+
import { warnOnce } from "../log.ts";
22+
import { isCssColorName } from "../css/color-names.ts";
2123

2224
// we can't programmatically convert typescript types to string arrays,
2325
// so we have to define this manually. They should match `BrandNamedThemeColor` in schema-types.ts
@@ -176,7 +178,7 @@ export class Brand {
176178
// - if the name is in the "palette" key, use that value as they key for a recursive call (so color names can be aliased or redefined away from scss defaults)
177179
// - if the name is a default color name, call getColor recursively (so defaults can use named values)
178180
// - otherwise, assume it's a color value and return it
179-
getColor(name: string): string {
181+
getColor(name: string, quiet = false): string {
180182
const seenValues = new Set<string>();
181183

182184
do {
@@ -198,6 +200,12 @@ export class Brand {
198200
) {
199201
name = this.data.color[name as BrandNamedThemeColor]!;
200202
} else {
203+
// if the name is not a default color name, assume it's a color value
204+
if (!isCssColorName(name) && !quiet) {
205+
warnOnce(
206+
`"${name}" is not a valid CSS color name.\nThis might cause SCSS compilation to fail, or the color to have no effect.`,
207+
);
208+
}
201209
return name;
202210
}
203211
} while (seenValues.size < 100); // 100 ought to be enough for anyone, with apologies to Bill Gates

src/core/css/color-names.ts

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
/*
2+
* color-names.ts
3+
*
4+
* Copyright (C) 2025 Posit Software, PBC
5+
*/
6+
7+
// https://www.w3.org/TR/css-color-3/#svg-color
8+
export const isCssColorName = (name: string): boolean => {
9+
if (name.startsWith("#")) return true;
10+
if (name.startsWith("rgb")) return true;
11+
if (name.startsWith("hsl")) return true;
12+
if (name.startsWith("hwb")) return true;
13+
14+
return [
15+
"aliceblue",
16+
"antiquewhite",
17+
"aqua",
18+
"aquamarine",
19+
"azure",
20+
"beige",
21+
"bisque",
22+
"black",
23+
"blanchedalmond",
24+
"blue",
25+
"blueviolet",
26+
"brown",
27+
"burlywood",
28+
"cadetblue",
29+
"chartreuse",
30+
"chocolate",
31+
"coral",
32+
"cornflowerblue",
33+
"cornsilk",
34+
"crimson",
35+
"cyan",
36+
"darkblue",
37+
"darkcyan",
38+
"darkgoldenrod",
39+
"darkgray",
40+
"darkgreen",
41+
"darkgrey",
42+
"darkkhaki",
43+
"darkmagenta",
44+
"darkolivegreen",
45+
"darkorange",
46+
"darkorchid",
47+
"darkred",
48+
"darksalmon",
49+
"darkseagreen",
50+
"darkslateblue",
51+
"darkslategray",
52+
"darkslategrey",
53+
"darkturquoise",
54+
"darkviolet",
55+
"deeppink",
56+
"deepskyblue",
57+
"dimgray",
58+
"dimgrey",
59+
"dodgerblue",
60+
"firebrick",
61+
"floralwhite",
62+
"forestgreen",
63+
"fuchsia",
64+
"gainsboro",
65+
"ghostwhite",
66+
"gold",
67+
"goldenrod",
68+
"gray",
69+
"green",
70+
"greenyellow",
71+
"grey",
72+
"honeydew",
73+
"hotpink",
74+
"indianred",
75+
"indigo",
76+
"ivory",
77+
"khaki",
78+
"lavender",
79+
"lavenderblush",
80+
"lawngreen",
81+
"lemonchiffon",
82+
"lightblue",
83+
"lightcoral",
84+
"lightcyan",
85+
"lightgoldenrodyellow",
86+
"lightgray",
87+
"lightgreen",
88+
"lightgrey",
89+
"lightpink",
90+
"lightsalmon",
91+
"lightseagreen",
92+
"lightskyblue",
93+
"lightslategray",
94+
"lightslategrey",
95+
"lightsteelblue",
96+
"lightyellow",
97+
"lime",
98+
"limegreen",
99+
"linen",
100+
"magenta",
101+
"maroon",
102+
"mediumaquamarine",
103+
"mediumblue",
104+
"mediumorchid",
105+
"mediumpurple",
106+
"mediumseagreen",
107+
"mediumslateblue",
108+
"mediumspringgreen",
109+
"mediumturquoise",
110+
"mediumvioletred",
111+
"midnightblue",
112+
"mintcream",
113+
"mistyrose",
114+
"moccasin",
115+
"navajowhite",
116+
"navy",
117+
"oldlace",
118+
"olive",
119+
"olivedrab",
120+
"orange",
121+
"orangered",
122+
"orchid",
123+
"palegoldenrod",
124+
"palegreen",
125+
"paleturquoise",
126+
"palevioletred",
127+
"papayawhip",
128+
"peachpuff",
129+
"peru",
130+
"pink",
131+
"plum",
132+
"powderblue",
133+
"purple",
134+
"red",
135+
"rosybrown",
136+
"royalblue",
137+
"saddlebrown",
138+
"salmon",
139+
"sandybrown",
140+
"seagreen",
141+
"seashell",
142+
"sienna",
143+
"silver",
144+
"skyblue",
145+
"slateblue",
146+
"slategray",
147+
"slategrey",
148+
"snow",
149+
"springgreen",
150+
"steelblue",
151+
"tan",
152+
"teal",
153+
"thistle",
154+
"tomato",
155+
"turquoise",
156+
"violet",
157+
"wheat",
158+
"white",
159+
"whitesmoke",
160+
"yellow",
161+
"yellowgreen",
162+
].includes(name);
163+
};

src/core/sass/brand.ts

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,7 @@ import {
2222
BrandFontWeight,
2323
} from "../../resources/types/schema-types.ts";
2424
import { Brand } from "../brand/brand.ts";
25-
import {
26-
darkModeDefault,
27-
} from "../../format/html/format-html-info.ts";
28-
25+
import { darkModeDefault } from "../../format/html/format-html-info.ts";
2926

3027
const defaultColorNameMap: Record<string, string> = {
3128
"link-color": "link",
@@ -81,8 +78,8 @@ export async function brandBootstrapSassBundles(
8178
dependency: "bootstrap",
8279
user: layers.light,
8380
dark: {
84-
user: layers.dark
85-
}
81+
user: layers.dark,
82+
},
8683
}];
8784
}
8885

@@ -187,7 +184,7 @@ const brandColorLayer = (
187184

188185
// format-specific name mapping
189186
for (const [key, value] of Object.entries(nameMap)) {
190-
const resolvedValue = brand.getColor(value);
187+
const resolvedValue = brand.getColor(value, true);
191188
if (resolvedValue !== value) {
192189
colorVariables.push(
193190
`$${key}: ${resolvedValue} !default;`,
@@ -579,7 +576,7 @@ export async function brandSassLayers(
579576
const brand = await project.resolveBrand(fileName);
580577
const sassLayers: LightDarkSassLayers = {
581578
light: [],
582-
dark: []
579+
dark: [],
583580
};
584581

585582
for (const mode of ["light", "dark"] as Array<"dark" | "light">) {
@@ -660,10 +657,12 @@ export async function brandSassFormatExtras(
660657
key: "brand",
661658
dependency: "bootstrap",
662659
user: htmlSassBundleLayers.light,
663-
dark: htmlSassBundleLayers.dark.length ? {
664-
user: htmlSassBundleLayers.dark,
665-
default: darkModeDefault(format.metadata)
666-
} : undefined
660+
dark: htmlSassBundleLayers.dark.length
661+
? {
662+
user: htmlSassBundleLayers.dark,
663+
default: darkModeDefault(format.metadata),
664+
}
665+
: undefined,
667666
},
668667
],
669668
},

0 commit comments

Comments
 (0)