diff --git a/src/core/sass/brand.ts b/src/core/sass/brand.ts index d4581b32f92..9077d7289aa 100644 --- a/src/core/sass/brand.ts +++ b/src/core/sass/brand.ts @@ -184,11 +184,24 @@ const brandColorBundle = ( "/* color variables from _brand.yml */", '// quarto-scss-analysis-annotation { "action": "push", "origin": "_brand.yml color" }', ]; + const colorCssVariables: string[] = [ + "/* color CSS variables from _brand.yml */", + '// quarto-scss-analysis-annotation { "action": "push", "origin": "_brand.yml color" }', + ":root {", + ]; + + // Create `brand-` prefixed Sass and CSS variables from color.palette for (const colorKey of Object.keys(brand.data?.color?.palette ?? {})) { + const colorVar = colorKey.replace(/[^a-zA-Z0-9_-]+/, "-"); colorVariables.push( - `$${colorKey}: ${brand.getColor(colorKey)} !default;`, + `$brand-${colorVar}: ${brand.getColor(colorKey)} !default;`, ); + colorCssVariables.push( + ` --brand-${colorVar}: ${brand.getColor(colorKey)};`, + ) } + + // Map theme colors directly to Sass variables for (const colorKey of Object.keys(brand.data.color ?? {})) { if (colorKey === "palette") { continue; @@ -197,6 +210,7 @@ const brandColorBundle = ( `$${colorKey}: ${brand.getColor(colorKey)} !default;`, ); } + // format-specific name mapping for (const [key, value] of Object.entries(nameMap)) { const resolvedValue = brand.getColor(value); @@ -208,6 +222,7 @@ const brandColorBundle = ( } // const colorEntries = Object.keys(brand.color); colorVariables.push('// quarto-scss-analysis-annotation { "action": "pop" }'); + colorCssVariables.push("}", '// quarto-scss-analysis-annotation { "action": "pop" }'); const colorBundle: SassBundleLayers = { key, // dependency: "bootstrap", @@ -216,12 +231,91 @@ const brandColorBundle = ( uses: "", functions: "", mixins: "", - rules: "", + rules: colorCssVariables.join("\n"), }, }; return colorBundle; }; +const brandBootstrapBundle = ( + brand: Brand, + key: string +): SassBundleLayers => { + // Bootstrap Variables from brand.defaults.bootstrap + const brandBootstrap = (brand?.data?.defaults?.bootstrap as unknown as Record< + string, + Record + >); + + const bsVariables: string[] = [ + "/* Bootstrap variables from _brand.yml */", + '// quarto-scss-analysis-annotation { "action": "push", "origin": "_brand.yml defaults.bootstrap" }', + ]; + for (const bsVar of Object.keys(brandBootstrap)) { + if (bsVar === "version") { + continue; + } + bsVariables.push( + `$${bsVar}: ${brandBootstrap[bsVar]} !default;`, + ); + } + bsVariables.push('// quarto-scss-analysis-annotation { "action": "pop" }'); + + // Bootstrap Colors from color.palette + let bootstrapColorVariables: string[] = []; + if (Number(brandBootstrap?.version ?? 5) === 5) { + // https://getbootstrap.com/docs/5.3/customize/color/#color-sass-maps + bootstrapColorVariables = [ + "blue", + "indigo", + "purple", + "pink", + "red", + "orange", + "yellow", + "green", + "teal", + "cyan", + "black", + "white", + "gray", + "gray-dark" + ] + } + + const bsColors: string[] = [ + "/* Bootstrap color variables from _brand.yml */", + '// quarto-scss-analysis-annotation { "action": "push", "origin": "_brand.yml color.palette" }', + ]; + + if (bootstrapColorVariables.length > 0) { + for (const colorKey of Object.keys(brand.data?.color?.palette ?? {})) { + if (!bootstrapColorVariables.includes(colorKey)) { + continue; + } + + bsColors.push( + `$${colorKey}: ${brand.getColor(colorKey)} !default;`, + ); + } + } + + bsColors.push('// quarto-scss-analysis-annotation { "action": "pop" }'); + + const bsBundle: SassBundleLayers = { + key, + // dependency: "bootstrap", + quarto: { + defaults: bsColors.join("\n") + "\n" + bsVariables.join("\n"), + uses: "", + functions: "", + mixins: "", + rules: "", + }, + }; + return bsBundle; +}; + const brandTypographyBundle = ( brand: Brand, key: string, @@ -446,7 +540,7 @@ const brandTypographyBundle = ( return typographyBundle; }; -export async function brandBootstrapSassBundleLayers( +export async function brandSassBundleLayers( fileName: string | undefined, project: ProjectContext, key: string, @@ -466,12 +560,33 @@ export async function brandBootstrapSassBundleLayers( return sassBundles; } +export async function brandBootstrapSassBundleLayers( + fileName: string | undefined, + project: ProjectContext, + key: string, + nameMap: Record = {}, +): Promise { + const brand = await project.resolveBrand(fileName); + const sassBundles = await brandSassBundleLayers(fileName, project, key, nameMap); + + if (brand?.data?.defaults?.bootstrap) { + const bsBundle = brandBootstrapBundle(brand, key); + if (bsBundle) { + // Add bsBundle to the beginning of the array so that defaults appear + // *after* the rest of the brand variables. + sassBundles.unshift(bsBundle); + } + } + + return sassBundles; +} + export async function brandRevealSassBundleLayers( input: string | undefined, _format: Format, project: ProjectContext, ): Promise { - return brandBootstrapSassBundleLayers( + return brandSassBundleLayers( input, project, "reveal-theme", diff --git a/src/resources/editor/tools/vs-code.mjs b/src/resources/editor/tools/vs-code.mjs index 69e48a165d0..b5d731181d0 100644 --- a/src/resources/editor/tools/vs-code.mjs +++ b/src/resources/editor/tools/vs-code.mjs @@ -21771,11 +21771,6 @@ var require_yaml_intelligence_resources = __commonJS({ "A link or path to the brand\u2019s medium-sized logo, or a link or path to\nboth the light and dark versions.", "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.", "Names of customizeable logos", - "Source path or source path with layout options for logo", - "X-Y positioning of logo", - "Padding of logo", - "Width of logo", - "Source path of logo", "The brand\u2019s custom color palette and theme.", "The brand\u2019s custom color palette. Any number of colors can be\ndefined, each color having a custom name.", "The foreground color, used for text.", @@ -24123,12 +24118,12 @@ var require_yaml_intelligence_resources = __commonJS({ mermaid: "%%" }, "handlers/mermaid/schema.yml": { - _internalId: 192336, + _internalId: 192600, type: "object", description: "be an object", properties: { "mermaid-format": { - _internalId: 192328, + _internalId: 192592, type: "enum", enum: [ "png", @@ -24144,7 +24139,7 @@ var require_yaml_intelligence_resources = __commonJS({ exhaustiveCompletions: true }, theme: { - _internalId: 192335, + _internalId: 192599, type: "anyOf", anyOf: [ { diff --git a/src/resources/editor/tools/yaml/web-worker.js b/src/resources/editor/tools/yaml/web-worker.js index d8a034e1621..f9bdff655fe 100644 --- a/src/resources/editor/tools/yaml/web-worker.js +++ b/src/resources/editor/tools/yaml/web-worker.js @@ -21772,11 +21772,6 @@ try { "A link or path to the brand\u2019s medium-sized logo, or a link or path to\nboth the light and dark versions.", "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.", "Names of customizeable logos", - "Source path or source path with layout options for logo", - "X-Y positioning of logo", - "Padding of logo", - "Width of logo", - "Source path of logo", "The brand\u2019s custom color palette and theme.", "The brand\u2019s custom color palette. Any number of colors can be\ndefined, each color having a custom name.", "The foreground color, used for text.", @@ -24124,12 +24119,12 @@ try { mermaid: "%%" }, "handlers/mermaid/schema.yml": { - _internalId: 192336, + _internalId: 192600, type: "object", description: "be an object", properties: { "mermaid-format": { - _internalId: 192328, + _internalId: 192592, type: "enum", enum: [ "png", @@ -24145,7 +24140,7 @@ try { exhaustiveCompletions: true }, theme: { - _internalId: 192335, + _internalId: 192599, type: "anyOf", anyOf: [ { diff --git a/src/resources/editor/tools/yaml/yaml-intelligence-resources.json b/src/resources/editor/tools/yaml/yaml-intelligence-resources.json index 4473adb75b3..bc16c3e86b0 100644 --- a/src/resources/editor/tools/yaml/yaml-intelligence-resources.json +++ b/src/resources/editor/tools/yaml/yaml-intelligence-resources.json @@ -14743,11 +14743,6 @@ "A link or path to the brand’s medium-sized logo, or a link or path to\nboth the light and dark versions.", "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.", "Names of customizeable logos", - "Source path or source path with layout options for logo", - "X-Y positioning of logo", - "Padding of logo", - "Width of logo", - "Source path of logo", "The brand’s custom color palette and theme.", "The brand’s custom color palette. Any number of colors can be\ndefined, each color having a custom name.", "The foreground color, used for text.", @@ -17095,12 +17090,12 @@ "mermaid": "%%" }, "handlers/mermaid/schema.yml": { - "_internalId": 192336, + "_internalId": 192600, "type": "object", "description": "be an object", "properties": { "mermaid-format": { - "_internalId": 192328, + "_internalId": 192592, "type": "enum", "enum": [ "png", @@ -17116,7 +17111,7 @@ "exhaustiveCompletions": true }, "theme": { - "_internalId": 192335, + "_internalId": 192599, "type": "anyOf", "anyOf": [ {