Skip to content

css: Improve type-checking performance of CSSVarFunction #1557

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 22, 2025

Conversation

askoufis
Copy link
Contributor

@askoufis askoufis commented Mar 5, 2025

Fixes #1556.

Simplifying the CSS var function fallback string literal type from

`var(--${string}, ${string | number})`

to

`var(--${string}, ${string})`

drastically improves type-checking in certain cases. For example, when passing a template literal that interpolates a CSS variable into a style property multiple times:

// example.css.ts
import { style, createVar } from "@vanilla-extract/css";

const px = createVar();

export const mask = style({
  mask: `
    repeating-linear-gradient(45deg, #0005 0 calc(1 * ${px}), black calc(2 * ${px}) calc(3 * ${px}), #0005 calc(4 * ${px}) calc(5 * ${px})) no-repeat,
    repeating-linear-gradient(-45deg, #0005 0 calc(1 * ${px}), black calc(2 * ${px}) calc(3 * ${px}), #0005 calc(4 * ${px}) calc(5 * ${px})) no-repeat
  `,
});

This doesn't actually affect the values this type can accept because ${number} is a subset of ${string}, and therefore ${string | number} === ${string}.

With this change, a trivial app containing the above example.css.ts file has its type check time drop from ~2.8s to ~0.15s (along with much fewer type instantiations and lower memory usage).

Before
Files:                          54
Lines of Library:            38764
Lines of Definitions:        26605
Lines of TypeScript:            11
Lines of JavaScript:             0
Lines of JSON:                   0
Lines of Other:                  0
Identifiers:                 59814
Symbols:                     40105
Types:                       95560
Instantiations:             362610
Memory used:               625044K
Assignability cache size:   885751
Identity cache size:             0
Subtype cache size:              0
Strict subtype cache size:       0
I/O Read time:               0.03s
Parse time:                  0.14s
ResolveModule time:          0.03s
ResolveTypeReference time:   0.00s
ResolveLibrary time:         0.01s
Program time:                0.22s
Bind time:                   0.06s
Check time:                  2.83s
printTime time:              0.00s
Emit time:                   0.00s
Total time:                  3.11s
After
Files:                         54
Lines of Library:           38764
Lines of Definitions:       26605
Lines of TypeScript:           11
Lines of JavaScript:            0
Lines of JSON:                  0
Lines of Other:                 0
Identifiers:                59814
Symbols:                    40105
Types:                       9032
Instantiations:             12412
Memory used:               91996K
Assignability cache size:   13325
Identity cache size:            0
Subtype cache size:             0
Strict subtype cache size:      0
I/O Read time:              0.02s
Parse time:                 0.15s
ResolveModule time:         0.03s
ResolveTypeReference time:  0.00s
ResolveLibrary time:        0.01s
Program time:               0.22s
Bind time:                  0.06s
Check time:                 0.15s
printTime time:             0.00s
Emit time:                  0.00s
Total time:                 0.43s

Benchmarks

I've created a benchmarks private package to add benchmarking scripts to. Currently the only script is one that benchmarks the number of instantiations between using a CSS var with a narrow type vs a broad (string) type.

Here are the results of the benchmark before and after making the change in this PR:

Before
🏌️  broad mask
⛳ Result: 8005 instantiations
🎯 Baseline: 8003 instantiations
📊 Delta: +0.02%

🏌️ narrow mask
⛳ Result: 362293 instantiations
🎯 Baseline: 8003 instantiations
📈 'narrow mask' exceeded baseline by 4426.96% (threshold is 20%).

❌ 'narrow mask' exceeded baseline by 4426.96% (threshold is 20%).

After
🏌️  broad mask
⛳ Result: 8003 instantiations
🎯 Baseline: 8003 instantiations
📊 Delta: 0.00%

🏌️ narrow mask
⛳ Result: 12095 instantiations
🎯 Baseline: 8003 instantiations
📈 'narrow mask' exceeded baseline by 51.13% (threshold is 20%).

❌ 'narrow mask' exceeded baseline by 51.13% (threshold is 20%).

Before the change in this PR, the narrow type resulted in ~360k instantiations while the broad type resulted in ~8k instantiations (44x difference). With this change, the narrow type now only creates ~12k instantiations (1.5x difference).

Copy link

changeset-bot bot commented Mar 5, 2025

🦋 Changeset detected

Latest commit: 1b11aa3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 12 packages
Name Type
@vanilla-extract/css Patch
@vanilla-extract/private Patch
@vanilla-extract/compiler Patch
@vanilla-extract/dynamic Patch
@vanilla-extract/integration Patch
@vanilla-extract/rollup-plugin Patch
@vanilla-extract/vite-plugin Patch
@vanilla-extract/esbuild-plugin Patch
@vanilla-extract/jest-transform Patch
@vanilla-extract/parcel-transformer Patch
@vanilla-extract/webpack-plugin Patch
@vanilla-extract/next-plugin Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@askoufis askoufis merged commit 134117d into master Mar 22, 2025
12 checks passed
@askoufis askoufis deleted the css-var-type-perf branch March 22, 2025 12:45
@jojojojojoj5564656465465
Copy link
Contributor

const nbCards = createVar({
  syntax: '<integer>',
  initialValue: '10',
  inherits: false,
})
const jfk = calc.divide('60rem', nbCards)

Why it's not working ?

@askoufis
Copy link
Contributor Author

const nbCards = createVar({
  syntax: '<integer>',
  initialValue: '10',
  inherits: false,
})
const jfk = calc.divide('60rem', nbCards)

Why it's not working ?

Please don't revive old PRs unless they're related to your issue. I'd prefer you make a separate issue with a minimal reproduction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Mask with template literal string very slow to compile
2 participants