feat: add createVar() API for SSR-safe unique CSS custom properties#849
Draft
layershifter wants to merge 18 commits into
Draft
feat: add createVar() API for SSR-safe unique CSS custom properties#849layershifter wants to merge 18 commits into
layershifter wants to merge 18 commits into
Conversation
…ties Addresses fluentui#17923. Design doc only, no implementation yet. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Scoped to @griffel/core and @griffel/react. Transform, ESLint rule, and jest serializer deferred to follow-up plans. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…unused registry dual-index
…reateVar integration
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ify JSDoc Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
📊 Bundle size reportUnchanged fixtures
|
…e bundle size resolveStyleRules.ts statically imported resolveVarsInStyles (which imports createVar), so every makeStyles consumer pulled in the walker + placeholder registry even without using createVar. Bundle-size regression was ~1.4 kB minified / ~447 B gzipped on the core makeStyles fixture. Split the placeholder registry into its own module and switch to a registered-hook pattern: createVar's module-load side effect registers resolveVarsInStyles with resolveStyleRules. Consumers who never import createVar never pull in the walker — the hot path cost is one null-check. Local monosize (makeStyles runtime): - before: 26.309 kB / 9.265 kB (+1.383 kB / +447 B vs baseline) - after: 25.038 kB / 8.861 kB (+112 B / +43 B vs baseline) Tests: 448 (@griffel/core) + 18 (@griffel/react) still pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Adds a new
createVar()API (addresses fluentui#17923) for generating unique CSS custom property names that are safe under SSR and work without any build-time transform.makeStylesblock it's defined in — deterministic across processes, no counter state leaks between server and client.--foonames and between libraries are prevented via a--fv-<hash>-<idx>naming scheme.makeStyles([colorVar]: 'blue'), insidevar(...)values, and in component inline styles (style={{ [colorVar]: 'red' }}).Design & plan docs
docs/superpowers/specs/2026-04-18-createVar-design.mddocs/superpowers/plans/2026-04-18-createVar-core-runtime.mdHow it works
createVar()returns a brandedGriffelVarwhoseSymbol.toPrimitive/toStringemits a per-call placeholder (--__g_var_p<n>__) until it's resolved.makeStylespass that contains the var as a key,resolveVarsInStyleshashes the block and rewrites every placeholder (in keys, string values, and array fallbacks) to a stable--fv-<hash>-<idx>name. That resolved name is then cached on the ref — subsequent coercions (including inline style usage) return it directly. First definer wins.resolveStyleRulesForSlotswarns if any placeholder makes it into emitted CSS (e.g. a var used only in inline styles with no definingmakeStyles).Packages touched
@griffel/style-types— newGriffelVarbranded type@griffel/core—createVar, placeholder registry,resolveVarsInStyles,resolveStyleRulesintegration, dev-mode leak detector, public exports@griffel/react— re-exportscreateVar/GriffelVarScope (this PR)
Core runtime only. The following are intentionally out of scope and will be follow-ups:
create-var-at-module-scope)Draft status
Draft because the follow-ups above are expected before a real release, and I'd like reviewer input on naming / public API surface (
createVar,GriffelVar,--fv-prefix) before locking it in.Test plan
yarn workspace @griffel/core test— 448 tests pass (includes new unit, integration, and SSR-equivalence suites forcreateVar/resolveVarsInStyles)yarn workspace @griffel/react test— 18 tests pass (includes newcreateVarsmoke test throughrenderToString)resolveStyleRuleshot path when no placeholders are present (fast path asserted inresolveVarsInStyles.test.tsvia same-ref return)🤖 Generated with Claude Code