Skip to content

Feature Proposal: Implement Component CSS Encapsulation via Class Suffixing #110

@thibaultzanini

Description

@thibaultzanini

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

  1. Parsing: When a component load its CSS, we access the browser's parsed CSS Object Model in order to find all class selectors.
  2. 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)
  3. Rewriting: We replaces all class selectors .classname with .classname_{hash}. Maybe using a Regex or a light
  4. 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

  1. Identification: The framework identifies if the component has encapsulation enabled. cssEncapulation
  2. 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:
    1. Require developers to use the generated Style Map directly (e.g., this.classList.add(this.styles.active)).
    2. 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.

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

  1. Are CSS scoping and CSS layering still necessary with component class suffixing? Probably not...

Metadata

Metadata

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions