-
Notifications
You must be signed in to change notification settings - Fork 2
Description
Component CSS Encapsulation via Class Suffixing
This proposal suggests implementing true CSS encapsulation for Montage components by adopting a Class Suffixing strategy (similar to CSS Modules). This involves automatically renaming CSS classes in both the stylesheet and the HTML template at runtime (and build time) to ensure that styles defined in one component do not leak into others or get overridden by global styles.
Proposed Design
Concept
We will introduce a hashing mechanism based on the Component's moduleId. When a component loads, its CSS classes are suffixed with a unique hash generated from that module ID.
Input
.container {
background: blue;
}
.label {
color: white;
}The HTML template is similarly transformed during the serialization/parsing phase.
The transformation differs between development and production: in development we perform at runtime suffixing and mapping for fast iteration and debugging, while in production the classes are suffixed at build time to remove runtime overhead and maximize performance.
Implementation
The CSS Processor
During the CSS loading process
- Parsing: When a component load its CSS, we access the browser's parsed CSS Object Model in order to find all class selectors.
- Hashing: We generate a short hash based on the component's unique moduleId. We need a fast and synchronous hashing function that turns a long string into a unique short unique alphanumeric suffix (e.g.
929489c9ac145) - Rewriting: We replaces all class selectors
.classnamewith.classname_{hash}. Maybe using a Regex or a light - Mapping: We generate a "Style Map" object that will be set on the component: { container: "container_xy7z", label: "label_xy7z" }.
The Template Processor
During the Template loading process
- Identification: The framework identifies if the component has encapsulation enabled.
cssEncapulation - Rewriting: It traverses the DOM Elements in the template, For every element with a class attribute, it looks up the class in the Style Map and replaces the static class name with the suffixed version.
NOTE: Hashing can be performed lazily. For example, only when a class attribute is encountered during template processing.
Javascript API
Static class strings in JS (e.g., this.classList.add('active')) will no longer match transformed class names. We need to provide convenience helpers.
- Two approaches:
- Require developers to use the generated Style Map directly (e.g.,
this.classList.add(this.styles.active)). - Provide transparent compatibility by extending built-in APIs that map original class names to suffixed names at runtime (so
this.classList.add('active')continues to work). This will be particularly helpful for FRB bindings.
- Require developers to use the generated Style Map directly (e.g.,
Build (Mop)
We also need to update mop to generate builds with templates and CSS files already suffixed to avoid runtime hashing and improve performance.
Opt-in
To preserve backward compatibility, this feature is opt-in and can be enabled globally for the app or selectively per component.
{
"my-component": {
"values": {
"element": { "#": "my-component" },
"cssEncapulation": true
}
}
}Interrogations
- Are CSS scoping and CSS layering still necessary with component class suffixing? Probably not...