This page documents a preview surface. Its contract may still change.
CSS Modules token rotation
Compatibility notice for an upcoming CSS Modules token identity rotation.
The CSS Modules token producer now identifies each emitted class by its module
and raw class-name bytes. This rotation shipped in the preceding coordinated
extension and Rust train. The independently reviewed
linked-emission default follows on Rust crate-train 0.5.0; it does not reserve
or require an extension 6.0.0 release.
{
"schemaVersion": "1",
"product": "omena.css-modules-token-rotation",
"previousRotationIdentity": null,
"rotationIdentity": "omena.css-modules.module-and-class-hash.v1",
"coordinatedRelease": {
"extension": "5.4.0",
"rustCrateTrain": "0.4.0"
},
"breakClasses": [
{
"id": "persisted-emitted-token",
"warningChannel": "none"
},
{
"id": "global-to-scoped-dependency-class",
"warningChannel": "none"
},
{
"id": "handwritten-emitted-token-selector",
"warningChannel": "none"
},
{
"id": "stale-interface-manifest",
"warningChannel": "build-time"
},
{
"id": "mixed-token-identity-versions",
"warningChannel": "none"
},
{
"id": "collision-retained-declaration-removal",
"warningChannel": "none"
},
{
"id": "required-workspace-root",
"warningChannel": "build-time"
}
]
}The previous token format did not publish a rotation identity, so
previousRotationIdentity is null. The new identity names the byte-producing
algorithm independently of either release version and is the value tools and
operators can compare during a coordinated upgrade.
Emitted tokens are not a contract; typed classExports, typed valueExports,
family-tagged namedExports, and the generated .d.ts are. Same-named class
and ICSS value exports remain separate instead of collapsing into one binding.
Hand-writing an emitted token into markup, tests, or CSS is unsupported.
Consumers should read the generated interface rather than persisting or
constructing emitted names.
Compatibility classes
The seven entries in the contract block are separate compatibility classes, not seven names for one symptom:
persisted-emitted-token: downstream snapshots, visual baselines, and E2E selectors that stored old emitted tokens change. Omena has no warning channel for an opaque token copied into an external artifact.global-to-scoped-dependency-class: dependency classes that previously leaked as unscoped names are now module-scoped. Markup that directly used a leaked name such asclass="solo1"no longer selects that declaration.handwritten-emitted-token-selector: handwritten CSS and:globaloverrides that target an emitted token stop matching after the rotation.stale-interface-manifest: a checked-in--interface-filemanifest can describe the old token bytes. Repository builds detect local drift, but they cannot update a consumer's separately stored copy.mixed-token-identity-versions: an old prebuilt package and a newly built application can each contain valid CSS while disagreeing about the token named by the generated map.collision-retained-declaration-removal: a declaration retained only because two modules previously produced the same token is now removed when normal reachability is applied. This is a second-order byte change alongside the token rotation.required-workspace-root: bundler-host resolve requests now requireworkspaceRoot. Older request payloads that omit it fail deserialization; callers must send the same stable workspace boundary used to derive module identity.
Caller-supplied per-module class maps are part of the selected module context.
Strict verification, when explicitly enabled, validates emitted bytes against
that selected context. The default Descriptive profile continues to report
the census without turning it into an admission failure; Strict coverage is
therefore opt-in rather than evidence about every default build.
Module identity preserves a caller-visible path that is already inside
workspaceRoot. Referencing the same physical file through two different
root-internal paths therefore creates two module identities and two token sets.
The collision gate cannot detect this aliasing; consumers should resolve each
file through one stable workspace-relative path.
The number of consumers in each class is not measured.
Migration runbook
- Upgrade the extension and every Rust, NAPI, WASM, CLI, and build adapter used by one build together. Do not combine old prebuilt CSS with a new token map.
- Rebuild CSS Modules and regenerate
classExports,valueExports, typednamedExports, generated.d.tsfiles, and checked-in--interface-filemanifests. - Replace copied token strings in markup, snapshot tests, visual baselines, and E2E selectors with lookups through the generated interface.
- Audit handwritten CSS and
:globalrules for selectors copied from emitted output; rewrite them against a supported source-level contract. - Check dependency styles for class names that were used as globals. Import those names through the module interface instead.
- Invalidate cached and prebuilt CSS assets so all artifacts in a deployment
carry
omena.css-modules.module-and-class-hash.v1. - Review byte snapshots for declarations that disappear after collision-free reachability, then run the application's visual and E2E suites.
- Add
workspaceRootto every bundler-host resolve request. Use a stable, canonical workspace boundary and keep it identical across relocated builds; do not derive it from a temporary output directory.