Skip to content

Commit 7027d8b

Browse files
authored
Merge pull request #14071 from quarto-dev/fix/issue-14065
2 parents 1f5904f + 1dc70f8 commit 7027d8b

8 files changed

Lines changed: 74 additions & 1 deletion

File tree

news/changelog-1.9.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ All changes included in 1.9:
5151
- ([#13825](https://github.com/quarto-dev/quarto-cli/issues/13825)): Fix `column: margin` not working with `renderings: [light, dark]` option. Column classes are now preserved when applying theme classes to cell outputs.
5252
- ([#13883](https://github.com/quarto-dev/quarto-cli/issues/13883)): Fix unequal top/bottom spacing in simple untitled callouts.
5353
- ([#13900](https://github.com/quarto-dev/quarto-cli/issues/13900)): Warn when `renderings` cell option contains duplicate names. Previously, duplicate names like `[dark, light, dark, light]` would silently use only the last output for each name.
54+
- ([#14065](https://github.com/quarto-dev/quarto-cli/issues/14065)): Fix `SCSSParsingError` when custom SCSS themes contain non-ASCII characters in selectors (e.g., `#présentation`).
5455

5556
### `typst`
5657

src/core/sass/add-css-vars.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ import { getVariableDependencies } from "./analyzer/get-dependencies.ts";
1616

1717
const { getSassAst } = makeParserModule(parse);
1818

19+
// Reverse the _u<hex>_ encoding applied in parse.ts so that
20+
// variable names emitted into the CSS vars block match the
21+
// original SCSS source that Dart Sass compiles against.
22+
// Non-ASCII codepoints are valid in CSS custom property names since they
23+
// follow the <ident> production (see spec references in parse.ts).
24+
const decodeScssName = (name: string) =>
25+
name.replace(/_u([0-9a-f]+)_/g, (_, hex: string) =>
26+
String.fromCodePoint(parseInt(hex, 16))
27+
);
28+
1929
export class SCSSParsingError extends Error {
2030
constructor(message: string) {
2131
super(`SCSS Parsing Error: ${message}`);
@@ -38,7 +48,8 @@ export const cssVarsBlock = (scssSource: string) => {
3848
for (const [dep, _] of deps) {
3949
const decl = ast.get(dep);
4050
if (decl.valueType === "color") {
41-
output.push(`--quarto-scss-export-${dep}: #{$${dep}};`);
51+
const originalName = decodeScssName(dep);
52+
output.push(`--quarto-scss-export-${originalName}: #{$${originalName}};`);
4253
}
4354
}
4455
output.push("}");

src/core/sass/analyzer/parse.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,21 @@ export const makeParserModule = (
4141
"$1: $2",
4242
);
4343

44+
// scss-parser's tokenizer only handles ASCII identifier characters.
45+
// Non-ASCII codepoints are valid in both CSS and SCSS identifiers:
46+
// - CSS Syntax L3 §4.2 defines "ident code point" as including any
47+
// codepoint >= U+0080 (https://www.w3.org/TR/css-syntax-3/#ident-code-point)
48+
// - CSS2 grammar includes `nonascii` in `nmstart`/`nmchar` productions
49+
// (https://www.w3.org/TR/CSS2/grammar.html#scanner)
50+
// - Sass inherits CSS's <ident-token> grammar for identifiers
51+
// (https://github.com/sass/sass/blob/main/spec/syntax.md)
52+
// Dart Sass handles them correctly, so we encode here as ASCII
53+
// placeholders for analysis only, then decode in add-css-vars.ts.
54+
contents = contents.replaceAll(
55+
/[^\x00-\x7F]/g,
56+
(ch) => `_u${ch.codePointAt(0)!.toString(16)}_`,
57+
);
58+
4459
// This is relatively painful, because unfortunately the error message of scss-parser
4560
// is not helpful.
4661

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
/.quarto/
2+
3+
**/*.quarto_ipynb
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/*-- scss:defaults --*/
2+
$présentation-bg: #ff0000;
3+
4+
/*-- scss:rules --*/
5+
#test-unicode-var {
6+
background-color: $présentation-bg;
7+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/*-- scss:rules --*/
2+
3+
#présentation p {
4+
line-height: 2;
5+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
title: "Unicode SCSS Variable Test"
3+
format:
4+
html:
5+
theme:
6+
- default
7+
- custom-14065-var.scss
8+
_quarto:
9+
tests:
10+
html:
11+
ensureCssRegexMatches:
12+
- ['#test-unicode-var', '--quarto-scss-export-présentation-bg']
13+
---
14+
15+
Content with a unicode-named color variable in SCSS.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
title: "Unicode SCSS Test"
3+
format:
4+
html:
5+
theme:
6+
- default
7+
- custom-14065.scss
8+
_quarto:
9+
tests:
10+
html:
11+
ensureCssRegexMatches:
12+
- ['#présentation p', '--quarto-scss-export-']
13+
---
14+
15+
## Présentation
16+
17+
Unicode characters in headings become CSS selectors.

0 commit comments

Comments
 (0)