Skip to content
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

feature: add support for adding prefix to CSS to scope down the styling #453

Open
ymyqwe opened this issue Oct 11, 2023 · 1 comment
Open
Labels
💅 enhancement New feature or request

Comments

@ymyqwe
Copy link

ymyqwe commented Oct 11, 2023

We have applications overlap in one page.

<html>
  <App-a>
  <!-- inserting griffel.css -->
  </App-a>
  <App-b>
  <!-- inserting <style data-make-styles-bucket="d"></style> -->
  </App-b>
</html>

Since the hashed className .fqdk4by may both existing in griffel.css and data-make-styles-bucket. They may cause global pollution or something else. So we need to add prefix for className to make CSS scoped.

Maybe solution like selectorPrefix https://fela.js.org/docs/latest/advanced/renderer-configuration

@layershifter layershifter added the 💅 enhancement New feature or request label Oct 11, 2023
@layershifter
Copy link
Member

layershifter commented Dec 11, 2023

This problem exists when there is an app with enabled CSS extraction and another app that uses runtime insertion on the same page (example: https://stackblitz.com/edit/stackblitz-starters-6gpxe7), i.e. multi bundle scenario.

Basically:

  • App1 (runtime insertion) + App2 (runtime insertion) = 😐 (both bundles will insert rules, but rules will not collide)
  • App1 (runtime insertion) + App2 (CSS extraction) = 💣 (blows up due order of CSS)

Workaround time

This is tricky, there are two ways:

  • Change classes prefix Not an option as Fluent compiles styles, classes can't be changed in runtime
  • Increase specificity on a renderer that does runtime insertion

The second option is more realistic as we just need to modify CSS rules. This can be done with createEnhancedRenderer() available below.

createEnhancedRenderer()
import { createDOMRenderer } from '@griffel/core';
import type { CreateDOMRendererOptions, GriffelRenderer } from '@griffel/core';
import { serialize, compile, stringify } from 'stylis';

const ENHANCE_CACHE: Record<string, string> = {};

export function enhanceCSSRule(className: string, cssRule: string) {
  if (!ENHANCE_CACHE[className + cssRule]) {
    ENHANCE_CACHE[className + cssRule] = serialize(compile(`.${className} {${cssRule} }`), stringify);
  }

  return ENHANCE_CACHE[className + cssRule];
}

export const createEnhancedRenderer = (
  document: Document | undefined,
  options: CreateDOMRendererOptions & { specificityClassName: string },
): GriffelRenderer => {
  const { specificityClassName } = options;
  const originalRenderer = createDOMRenderer(document, options);

  return {
    ...originalRenderer,
    insertCSSRules(cssRules) {
      const rules = Object.fromEntries(
        Object.entries(cssRules).map(([bucketName, cssRulesInBucket]) => {
          return [
            bucketName,
            cssRulesInBucket.map(cssEntry => {
              if (typeof cssEntry === 'string') {
                return enhanceCSSRule(specificityClassName, cssEntry);
              }

              return [enhanceCSSRule(specificityClassName, cssEntry[0]), cssEntry[1]];
            }),
          ];
        }),
      );

      originalRenderer.insertCSSRules(rules);
    },
  };
};

Then it could be used like that:

 const renderer = createEnhancedRenderer(document, {
  specificityClassName: 'foo',
  // ❗ This is mandatory, runtime styles should be inserted before CSS stylesheet
  insertionPoint: document.head.firstChild,
});

<RendererProvider renderer={renderer}>
  <div className="foo">{/* children */}</div>
</RendererProvider>

All together: https://stackblitz.com/edit/vitejs-vite-d11ccd

Proper solution

We should stop compiling styles on library styles i.e. Fluent should ship not optimized styles, so we can process them in runtime/build time. This requires wider discussion as then by default, Fluent will become slower and will require AOT processing.

After that, we can implement the option mentioned in this feature request & consider API for processors/plugins.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
💅 enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants