Skip to content

Instantly share code, notes, and snippets.

@doeixd
Last active June 25, 2024 16:01
Show Gist options
  • Save doeixd/ade8376e2e4ca3eace19f429cf6cea40 to your computer and use it in GitHub Desktop.
Save doeixd/ade8376e2e4ca3eace19f429cf6cea40 to your computer and use it in GitHub Desktop.
Scoped CSS Code Golf: The following is a "one-line" snippet I have created to enable scoped style tags. It supports, :global, media queries, keyframes, and adding and removing style tags/the scoped attribute. It's pretty hacky, and there may be some unexpected edge cases with the regexes, but it works for me! I mostly created it for fun, and as …
window.addEventListener('DOMContentLoaded',(a,d=document,s='STYLE',p=(d.head.innerHTML+=`<${s} id='g'></${s}>`,'scoped'),f=(e,x,y,i=e?.dataset?.s??new Date%1000,b=((e.dataset.s=i,a='attributes',x='textContent'),e[a][p]))=>{(e?.parentElement??(i='html'))?.classList?.[b?'add':'remove'](i);e[x]=e?.[x]?.replace(/(?<=^([ \t]*):global\s?{\n?)[\S\s]*?(?=^\1})|(?<s>.s-\d+\s)/gm,(...m)=>(!m?.[5]?.s&&(g[x]+=m[0]),''))?.replace(/^(?<i>[ \t]*)(?![@:]|(?:to|from|\d+%))(?<s>(?:\S|(?<=\S)\s(?!{))*)(?=\s?{$)/gm,`$<i>${b?`.${i} `:''}$<s>`)},m=new MutationObserver(l=>l.map(r=>[...r?.addedNodes,r.target].map(n=>n?.tagName==s&&f(n)))).observe(d.documentElement,{[a]:1,childList:1,subtree:1,attributeFilter:[p]}))=>d.querySelectorAll(s+`[${p}]`).forEach(f))
@doeixd
Copy link
Author

doeixd commented Jul 15, 2022

Current code golfed version with whitespace:

// Hopefully this is *somewhat* readable...
window.addEventListener(
  'DOMContentLoaded',
  ( a,
    d = document,
    s = 'STYLE',
    p = (d.head.innerHTML+=`<${s} id="g"></${s}>`,'scoped'),
    f = (
      e, x, y,
      i = e?.dataset?.s ?? new Date%1000,
      b = ((e.dataset.s= i,a='attributes',x='textContent'), e[a][p])
    ) => {
      (e?.parentElement ?? (i = 'html'))?.classList?.[b ? 'add' : 'remove'](i);
      e[x] = e?.[x]
        ?.replace(
          /(?<=^([ \t]*):global\s?{\n?)[\S\s]*?(?=^\1})|(?<s>.s-\d+\s)/gm,
          (...m) => (!m?.[5]?.s && (g[x] += m[0]), '')
        )
        ?.replace(
          /^(?<i>[ \t]*)(?![@:]|(?:to|from|\d+%))(?<s>(?:\S|(?<=\S)\s(?!{))*)(?=\s?{$)/gm,
          `$<i>${b?`.${i} `:''}$<s>`
        )
    }, m = new MutationObserver(l =>
      l.map(r =>
        [...r?.addedNodes, r.target].map(
          n => n?.tagName == s && f(n)
        )
      )
  ).observe(d.documentElement, {
    [a]: 1,
    childList: 1,
    subtree: 1,
    attributeFilter: [p]
  })
  ) => d.querySelectorAll(s+`[${p}]`).map(f)
)

Readable Version:

const globalStylesTag = document.head.appendChild(
  document.createElement('style')
);

window.addEventListener('DOMContentLoaded', (_) =>
  document.querySelectorAll('style[scoped]').forEach(scopeStyles)
);

const scopedStylesMutObserver = new MutationObserver((mutationRecords) =>
  mutationRecords.forEach((record) =>
    [...record?.addedNodes, record.target].forEach((node) => {
      if (node?.tagName == 'STYLE') {
        scopeStyles(node);
      }
    })
  )
);

const observerOptions = {
  attributes: true,
  childList: true,
  subtree: true,
  attributeFilter: ['scoped'],
};

scopedStylesMutObserver.observe(document.documentElement, observerOptions);

function scopeStyles(styleElement) {
  const randomNumber = Math.floor(Math.random() * 1000);
  let scopedStyleId = `s-${randomNumber}`;

  const hasAlreadyBeenVisited = Boolean(styleElement?.dataset?.scopedStyleId);
  if (hasAlreadyBeenVisited) {
    scopedStyleId = styleElement.dataset.scopedStyleId;
  } else {
    styleElement.dataset.scopedStyleId = scopedStyleId;
  }

  const hasScopedAttribute = styleElement.hasAttribute('scoped');
  const hasParentElement = Boolean(styleElement?.parentElement);
  let parentScope = scopedStyleId;

  if (hasParentElement) {
    const operation = hasScopedAttribute ? 'add' : 'remove';
    styleElement.parentElement.classList[operation](scopedStyleId);
  } else {
    parentScope = 'html';
  }

  // Replace :global blocks with empty string and add the blocks to the global style tag.
  const indentationGroupRegex = String.raw`^([ \t]*)`;
  const globalBlockStartRegex = String.raw`:global\s?{\n?`;
  const anyCharLazyRegex = String.raw`[\S\s]*?`;
  const lookBehindIndentationRegex = String.raw`(?=^\1})`;
  const positiveLookBehindRegex = (regex) => `(?<=${regex})`;
  const replaceGlobalBlockRegex = new RegExp(
    positiveLookBehindRegex(indentationGroupRegex + globalBlockStartRegex) +
      anyCharLazyRegex +
      lookBehindIndentationRegex,
    'gm'
  );
  const addToGlobalStyleTag = (match) => {
    globalStylesTag.textContent += match;
    return '';
  };
  const contentWithGlobalsRemoved = styleElement?.textContent?.replace(
    replaceGlobalBlockRegex,
    addToGlobalStyleTag
  );

  let currentTextContent = contentWithGlobalsRemoved;
  //Remove Scoping Ids
  if (!hasScopedAttribute) {
    const scopedStyleIDRegex = new RegExp(String.raw`.s-\d+\s`, 'gm');
    const withScopedStyleIDsRemoved = currentTextContent.replace(
      scopedStyleIDRegex,
      ''
    );
    currentTextContent = withScopedStyleIDsRemoved;
  }
  //Add scoping Ids
  if (hasScopedAttribute && !hasAlreadyBeenVisited) {
    const namedIndentationGroupRegex = String.raw`^(?<indentation>[ \t]*)`;
    const specialStartingCharsRegex = String.raw`[@:]`;
    const keyframePropsRegex = String.raw`(?:to|from|\d+%)`;
    const negativeLookaheadRegex = (...regexs) =>
      String.raw`(?!${regexs.join('|')})`;
    const selectorRegex = String.raw`(?<selector>(?:\S|(?<=\S)\s(?!{))*)(?=\s?{$)`;
    const fullSelectorRegex = new RegExp(
      namedIndentationGroupRegex +
        negativeLookaheadRegex(specialStartingCharsRegex, keyframePropsRegex) +
        selectorRegex,
      'gm'
    );

    const withScopedStyleIDs = currentTextContent.replace(
      fullSelectorRegex,
      `$<indentation>.${parentScope} $<selector>`
    );
    currentTextContent = withScopedStyleIDs;
  }

  styleElement.textContent = currentTextContent;
}

@doeixd
Copy link
Author

doeixd commented Jul 15, 2022

Version 1:

  document.querySelectorAll('style[scoped]').forEach(el => {
    const id = 's-' + ~~(Math.random() * 1000000)
    el.parentElement.classList.add(id)
    el.textContent = el.textContent.replace(/(?<!@.*)(?:[^\s])(?<!(?<g>:.*))[\w:\(\)]+(?=\s?{)/g, `.${id} $&`)
  }) 

@doeixd
Copy link
Author

doeixd commented Jul 18, 2022

Example of functionality:

<div>
    This text is not blue
    <div>
        This text is blue.
        <!-- Because of the "scoped" attribute, the following styles only apply to the parent element and it's children. -->
        <style scoped>
            div {
                color: blue;
            }
        </style>
    </div>
</div>

Example of :global escape-hatch functionality:

<div>
    This text is red.
    <div>
        This text is also red.
        <style scoped>
            /* All styles defined within this block apply globally, and are not scoped */
            :global {            
                div {
                    color: red;
                }
            }
        </style>
    </div>
</div>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment