Skip to content

Commit 4a6f64c

Browse files
authored
feat: add more CSS-wide keywords in font-family (#2135)
1 parent 6cf0323 commit 4a6f64c

File tree

3 files changed

+61
-16
lines changed

3 files changed

+61
-16
lines changed

CHANGELOG.md

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,26 @@
8282

8383
This was contributed by [@yisibl](https://github.com/yisibl).
8484

85+
* Avoid CSS cascade-dependent keywords in the `font-family` property ([#2135](https://github.com/evanw/esbuild/pull/2135))
86+
87+
In CSS, [`initial`](https://developer.mozilla.org/en-US/docs/Web/CSS/initial), [`inherit`](https://developer.mozilla.org/en-US/docs/Web/CSS/inherit), and [`unset`](https://developer.mozilla.org/en-US/docs/Web/CSS/unset) are [CSS-wide keywords](https://drafts.csswg.org/css-values-4/#css-wide-keywords) which means they have special behavior when they are specified as a property value. For example, while `font-family: 'Arial'` (as a string) and `font-family: Arial` (as an identifier) are the same, `font-family: 'inherit'` (as a string) uses the font family named `inherit` but `font-family: inherit` (as an identifier) inherits the font family from the parent element. This means esbuild must not unquote these CSS-wide keywords (and `default`, which is also reserved) during minification to avoid changing the meaning of the minified CSS.
88+
89+
The current draft of the new CSS Cascading and Inheritance Level 5 specification adds another concept called [cascade-dependent keywords](https://drafts.csswg.org/css-cascade-5/#defaulting-keywords) of which there are two: [`revert`](https://developer.mozilla.org/en-US/docs/Web/CSS/revert) and [`revert-layer`](https://developer.mozilla.org/en-US/docs/Web/CSS/revert-layer). This release of esbuild guards against unquoting these additional keywords as well to avoid accidentally breaking pages that use a font with the same name:
90+
91+
```css
92+
/* Original code */
93+
a { font-family: 'revert'; }
94+
b { font-family: 'revert-layer', 'Segoe UI', serif; }
95+
96+
/* Old output (with --minify) */
97+
a{font-family:revert}b{font-family:revert-layer,Segoe UI,serif}
98+
99+
/* New output (with --minify) */
100+
a{font-family:"revert"}b{font-family:"revert-layer",Segoe UI,serif}
101+
```
102+
103+
This fix was contributed by [@yisibl](https://github.com/yisibl).
104+
85105
## 0.14.30
86106

87107
* Change the context of TypeScript parameter decorators ([#2147](https://github.com/evanw/esbuild/issues/2147))
@@ -3811,15 +3831,15 @@ In addition to the breaking changes above, the following features are also inclu
38113831

38123832
* Minify the syntax `Infinity` to `1 / 0` ([#1385](https://github.com/evanw/esbuild/pull/1385))
38133833

3814-
The `--minify-syntax` flag (automatically enabled by `--minify`) will now minify the expression `Infinity` to `1 / 0`, which uses fewer bytes:
3834+
The `--minify-syntax` flag (automatically enabled by `--minify`) will now minify the expression `Infinity` to `1 / 0`, which uses fewer bytes:
38153835

3816-
```js
3817-
// Original code
3818-
const a = Infinity;
3836+
```js
3837+
// Original code
3838+
const a = Infinity;
38193839

3820-
// Output with "--minify-syntax"
3821-
const a = 1 / 0;
3822-
```
3840+
// Output with "--minify-syntax"
3841+
const a = 1 / 0;
3842+
```
38233843

38243844
This change was contributed by [@Gusted](https://github.com/Gusted).
38253845

internal/css_parser/css_decls_font_family.go

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,35 @@ import (
77
"github.com/evanw/esbuild/internal/css_lexer"
88
)
99

10-
// Specification: https://drafts.csswg.org/css-values-4/#common-keywords
11-
var wideKeywords = map[string]bool{
12-
"initial": true,
13-
"inherit": true,
14-
"unset": true,
10+
// These keywords usually require special handling when parsing.
11+
12+
// Declaring a property to have these values explicitly specifies a particular
13+
// defaulting behavior instead of setting the property to that identifier value.
14+
// As specified in CSS Values and Units Level 3, all CSS properties can accept
15+
// these values.
16+
//
17+
// For example, "font-family: 'inherit'" sets the font family to the font named
18+
// "inherit" while "font-family: inherit" sets the font family to the inherited
19+
// value.
20+
//
21+
// Note that other CSS specifications can define additional CSS-wide keywords,
22+
// which we should copy here whenever new ones are created so we can quote those
23+
// identifiers to avoid collisions with any newly-created CSS-wide keywords.
24+
var cssWideAndReservedKeywords = map[string]bool{
25+
// CSS Values and Units Level 3: https://drafts.csswg.org/css-values-3/#common-keywords
26+
"initial": true, // CSS-wide keyword
27+
"inherit": true, // CSS-wide keyword
28+
"unset": true, // CSS-wide keyword
29+
"default": true, // CSS reserved keyword
30+
31+
// CSS Cascading and Inheritance Level 5: https://drafts.csswg.org/css-cascade-5/#defaulting-keywords
32+
"revert": true, // Cascade-dependent keyword
33+
"revert-layer": true, // Cascade-dependent keyword
1534
}
1635

36+
// Font family names that happen to be the same as a keyword value must be
37+
// quoted to prevent confusion with the keywords with the same names. UAs must
38+
// not consider these keywords as matching the <family-name> type.
1739
// Specification: https://drafts.csswg.org/css-fonts/#generic-font-families
1840
var genericFamilyNames = map[string]bool{
1941
"serif": true,
@@ -118,10 +140,7 @@ func isValidCustomIdent(text string, predefinedKeywords map[string]bool) bool {
118140
if predefinedKeywords[loweredText] {
119141
return false
120142
}
121-
if wideKeywords[loweredText] {
122-
return false
123-
}
124-
if loweredText == "default" {
143+
if cssWideAndReservedKeywords[loweredText] {
125144
return false
126145
}
127146
if loweredText == "" {

internal/css_parser/css_parser_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1814,6 +1814,12 @@ func TestFontFamily(t *testing.T) {
18141814

18151815
expectPrintedMangleMinify(t, "a {font-family: 'aaa bbb', serif }", "a{font-family:aaa bbb,serif}")
18161816
expectPrintedMangleMinify(t, "a {font-family: 'aaa bbb', 'ccc ddd' }", "a{font-family:aaa bbb,ccc ddd}")
1817+
expectPrintedMangleMinify(t, "a {font-family: 'initial', serif;}", "a{font-family:\"initial\",serif}")
1818+
expectPrintedMangleMinify(t, "a {font-family: 'inherit', serif;}", "a{font-family:\"inherit\",serif}")
1819+
expectPrintedMangleMinify(t, "a {font-family: 'unset', serif;}", "a{font-family:\"unset\",serif}")
1820+
expectPrintedMangleMinify(t, "a {font-family: 'revert', serif;}", "a{font-family:\"revert\",serif}")
1821+
expectPrintedMangleMinify(t, "a {font-family: 'revert-layer', 'Segoe UI', serif;}", "a{font-family:\"revert-layer\",Segoe UI,serif}")
1822+
expectPrintedMangleMinify(t, "a {font-family: 'default', serif;}", "a{font-family:\"default\",serif}")
18171823
}
18181824

18191825
func TestFont(t *testing.T) {

0 commit comments

Comments
 (0)