Skip to content

Commit 7194553

Browse files
authored
Merge pull request #11108 from quarto-dev/brand/bootstrap-vars
feat(brand): Handling of Bootstrap defaults and brand colors in HTML formats
2 parents 61b2b87 + b298016 commit 7194553

File tree

4 files changed

+128
-28
lines changed

4 files changed

+128
-28
lines changed

src/core/sass/brand.ts

Lines changed: 119 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -184,11 +184,24 @@ const brandColorBundle = (
184184
"/* color variables from _brand.yml */",
185185
'// quarto-scss-analysis-annotation { "action": "push", "origin": "_brand.yml color" }',
186186
];
187+
const colorCssVariables: string[] = [
188+
"/* color CSS variables from _brand.yml */",
189+
'// quarto-scss-analysis-annotation { "action": "push", "origin": "_brand.yml color" }',
190+
":root {",
191+
];
192+
193+
// Create `brand-` prefixed Sass and CSS variables from color.palette
187194
for (const colorKey of Object.keys(brand.data?.color?.palette ?? {})) {
195+
const colorVar = colorKey.replace(/[^a-zA-Z0-9_-]+/, "-");
188196
colorVariables.push(
189-
`$${colorKey}: ${brand.getColor(colorKey)} !default;`,
197+
`$brand-${colorVar}: ${brand.getColor(colorKey)} !default;`,
190198
);
199+
colorCssVariables.push(
200+
` --brand-${colorVar}: ${brand.getColor(colorKey)};`,
201+
)
191202
}
203+
204+
// Map theme colors directly to Sass variables
192205
for (const colorKey of Object.keys(brand.data.color ?? {})) {
193206
if (colorKey === "palette") {
194207
continue;
@@ -197,6 +210,7 @@ const brandColorBundle = (
197210
`$${colorKey}: ${brand.getColor(colorKey)} !default;`,
198211
);
199212
}
213+
200214
// format-specific name mapping
201215
for (const [key, value] of Object.entries(nameMap)) {
202216
const resolvedValue = brand.getColor(value);
@@ -208,6 +222,7 @@ const brandColorBundle = (
208222
}
209223
// const colorEntries = Object.keys(brand.color);
210224
colorVariables.push('// quarto-scss-analysis-annotation { "action": "pop" }');
225+
colorCssVariables.push("}", '// quarto-scss-analysis-annotation { "action": "pop" }');
211226
const colorBundle: SassBundleLayers = {
212227
key,
213228
// dependency: "bootstrap",
@@ -216,12 +231,91 @@ const brandColorBundle = (
216231
uses: "",
217232
functions: "",
218233
mixins: "",
219-
rules: "",
234+
rules: colorCssVariables.join("\n"),
220235
},
221236
};
222237
return colorBundle;
223238
};
224239

240+
const brandBootstrapBundle = (
241+
brand: Brand,
242+
key: string
243+
): SassBundleLayers => {
244+
// Bootstrap Variables from brand.defaults.bootstrap
245+
const brandBootstrap = (brand?.data?.defaults?.bootstrap as unknown as Record<
246+
string,
247+
Record<string, string | boolean | number | null>
248+
>);
249+
250+
const bsVariables: string[] = [
251+
"/* Bootstrap variables from _brand.yml */",
252+
'// quarto-scss-analysis-annotation { "action": "push", "origin": "_brand.yml defaults.bootstrap" }',
253+
];
254+
for (const bsVar of Object.keys(brandBootstrap)) {
255+
if (bsVar === "version") {
256+
continue;
257+
}
258+
bsVariables.push(
259+
`$${bsVar}: ${brandBootstrap[bsVar]} !default;`,
260+
);
261+
}
262+
bsVariables.push('// quarto-scss-analysis-annotation { "action": "pop" }');
263+
264+
// Bootstrap Colors from color.palette
265+
let bootstrapColorVariables: string[] = [];
266+
if (Number(brandBootstrap?.version ?? 5) === 5) {
267+
// https://getbootstrap.com/docs/5.3/customize/color/#color-sass-maps
268+
bootstrapColorVariables = [
269+
"blue",
270+
"indigo",
271+
"purple",
272+
"pink",
273+
"red",
274+
"orange",
275+
"yellow",
276+
"green",
277+
"teal",
278+
"cyan",
279+
"black",
280+
"white",
281+
"gray",
282+
"gray-dark"
283+
]
284+
}
285+
286+
const bsColors: string[] = [
287+
"/* Bootstrap color variables from _brand.yml */",
288+
'// quarto-scss-analysis-annotation { "action": "push", "origin": "_brand.yml color.palette" }',
289+
];
290+
291+
if (bootstrapColorVariables.length > 0) {
292+
for (const colorKey of Object.keys(brand.data?.color?.palette ?? {})) {
293+
if (!bootstrapColorVariables.includes(colorKey)) {
294+
continue;
295+
}
296+
297+
bsColors.push(
298+
`$${colorKey}: ${brand.getColor(colorKey)} !default;`,
299+
);
300+
}
301+
}
302+
303+
bsColors.push('// quarto-scss-analysis-annotation { "action": "pop" }');
304+
305+
const bsBundle: SassBundleLayers = {
306+
key,
307+
// dependency: "bootstrap",
308+
quarto: {
309+
defaults: bsColors.join("\n") + "\n" + bsVariables.join("\n"),
310+
uses: "",
311+
functions: "",
312+
mixins: "",
313+
rules: "",
314+
},
315+
};
316+
return bsBundle;
317+
};
318+
225319
const brandTypographyBundle = (
226320
brand: Brand,
227321
key: string,
@@ -450,7 +544,7 @@ const brandTypographyBundle = (
450544
return typographyBundle;
451545
};
452546

453-
export async function brandBootstrapSassBundleLayers(
547+
export async function brandSassBundleLayers(
454548
fileName: string | undefined,
455549
project: ProjectContext,
456550
key: string,
@@ -470,12 +564,33 @@ export async function brandBootstrapSassBundleLayers(
470564
return sassBundles;
471565
}
472566

567+
export async function brandBootstrapSassBundleLayers(
568+
fileName: string | undefined,
569+
project: ProjectContext,
570+
key: string,
571+
nameMap: Record<string, string> = {},
572+
): Promise<SassBundleLayers[]> {
573+
const brand = await project.resolveBrand(fileName);
574+
const sassBundles = await brandSassBundleLayers(fileName, project, key, nameMap);
575+
576+
if (brand?.data?.defaults?.bootstrap) {
577+
const bsBundle = brandBootstrapBundle(brand, key);
578+
if (bsBundle) {
579+
// Add bsBundle to the beginning of the array so that defaults appear
580+
// *after* the rest of the brand variables.
581+
sassBundles.unshift(bsBundle);
582+
}
583+
}
584+
585+
return sassBundles;
586+
}
587+
473588
export async function brandRevealSassBundleLayers(
474589
input: string | undefined,
475590
_format: Format,
476591
project: ProjectContext,
477592
): Promise<SassBundleLayers[]> {
478-
return brandBootstrapSassBundleLayers(
593+
return brandSassBundleLayers(
479594
input,
480595
project,
481596
"reveal-theme",

src/resources/editor/tools/vs-code.mjs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21771,11 +21771,6 @@ var require_yaml_intelligence_resources = __commonJS({
2177121771
"A link or path to the brand\u2019s medium-sized logo, or a link or path to\nboth the light and dark versions.",
2177221772
"A link or path to the brand\u2019s large- or full-sized logo, or a link or\npath to both the light and dark versions.",
2177321773
"Names of customizeable logos",
21774-
"Source path or source path with layout options for logo",
21775-
"X-Y positioning of logo",
21776-
"Padding of logo",
21777-
"Width of logo",
21778-
"Source path of logo",
2177921774
"The brand\u2019s custom color palette and theme.",
2178021775
"The brand\u2019s custom color palette. Any number of colors can be\ndefined, each color having a custom name.",
2178121776
"The foreground color, used for text.",
@@ -24123,12 +24118,12 @@ var require_yaml_intelligence_resources = __commonJS({
2412324118
mermaid: "%%"
2412424119
},
2412524120
"handlers/mermaid/schema.yml": {
24126-
_internalId: 192336,
24121+
_internalId: 192600,
2412724122
type: "object",
2412824123
description: "be an object",
2412924124
properties: {
2413024125
"mermaid-format": {
24131-
_internalId: 192328,
24126+
_internalId: 192592,
2413224127
type: "enum",
2413324128
enum: [
2413424129
"png",
@@ -24144,7 +24139,7 @@ var require_yaml_intelligence_resources = __commonJS({
2414424139
exhaustiveCompletions: true
2414524140
},
2414624141
theme: {
24147-
_internalId: 192335,
24142+
_internalId: 192599,
2414824143
type: "anyOf",
2414924144
anyOf: [
2415024145
{

src/resources/editor/tools/yaml/web-worker.js

Lines changed: 3 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/resources/editor/tools/yaml/yaml-intelligence-resources.json

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14743,11 +14743,6 @@
1474314743
"A link or path to the brand’s medium-sized logo, or a link or path to\nboth the light and dark versions.",
1474414744
"A link or path to the brand’s large- or full-sized logo, or a link or\npath to both the light and dark versions.",
1474514745
"Names of customizeable logos",
14746-
"Source path or source path with layout options for logo",
14747-
"X-Y positioning of logo",
14748-
"Padding of logo",
14749-
"Width of logo",
14750-
"Source path of logo",
1475114746
"The brand’s custom color palette and theme.",
1475214747
"The brand’s custom color palette. Any number of colors can be\ndefined, each color having a custom name.",
1475314748
"The foreground color, used for text.",
@@ -17095,12 +17090,12 @@
1709517090
"mermaid": "%%"
1709617091
},
1709717092
"handlers/mermaid/schema.yml": {
17098-
"_internalId": 192336,
17093+
"_internalId": 192600,
1709917094
"type": "object",
1710017095
"description": "be an object",
1710117096
"properties": {
1710217097
"mermaid-format": {
17103-
"_internalId": 192328,
17098+
"_internalId": 192592,
1710417099
"type": "enum",
1710517100
"enum": [
1710617101
"png",
@@ -17116,7 +17111,7 @@
1711617111
"exhaustiveCompletions": true
1711717112
},
1711817113
"theme": {
17119-
"_internalId": 192335,
17114+
"_internalId": 192599,
1712017115
"type": "anyOf",
1712117116
"anyOf": [
1712217117
{

0 commit comments

Comments
 (0)