Skip to content

Instantly share code, notes, and snippets.

@barneycarroll
Created July 17, 2024 14:08
Show Gist options
  • Save barneycarroll/8f49070bab171034d82fadd8615d7c8c to your computer and use it in GitHub Desktop.
Save barneycarroll/8f49070bab171034d82fadd8615d7c8c to your computer and use it in GitHub Desktop.

Revisions

  1. barneycarroll created this gist Jul 17, 2024.
    58 changes: 58 additions & 0 deletions @scope.polyfill.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,58 @@
    /** Return a unique selector for a specific element. */
    const getUniqueSelector = (/** @type {Element} */ element) => {
    /** Unique selector for this element */
    let selector = ''

    /** @type {Element} */
    let parent

    while (parent = element.parentElement) {
    /** @type {number} Nth-child order of the element. */
    const nthChild = Array.prototype.indexOf.call(parent.children, element) + 1

    selector = ` > :nth-child(${nthChild})${selector}`

    element = parent
    }

    return ':root' + selector
    }

    let supportMap = new WeakMap()

    let polyfillScope = (document) => {
    for (let sheet of document.styleSheets) {
    for (let index = 0; index < sheet.cssRules.length; ++index) {
    let rule = sheet.cssRules[index]
    if (rule.type === 12) {
    let { conditionText } = rule
    let match = conditionText.match(/^\(polyfill @scope \((.+) to\((.+)\)\)\)$/)
    if (match) {
    if (supportMap.has(rule)) continue
    let [ , from, to ] = match
    let mediaRule = sheet.cssRules[sheet.insertRule(`@media ${`@scope ${from} to(${to})`.replace(/[^\w]/g, '\\$&')},all` + rule.cssText.slice('@supports '.length + rule.conditionText.length), index++)]
    supportMap.set(rule, mediaRule)
    supportMap.set(mediaRule, rule)
    let fromElements = Array.from(document.querySelectorAll(from))
    for (let innerRule of mediaRule.cssRules) {
    if (innerRule.type === 1) {
    let narrElements = fromElements.map(
    from => Array.from(
    from.querySelectorAll(':is(' + innerRule.selectorText + '):not(:scope :is(' + to + ') *)')
    )
    ).flat()
    let selectors = narrElements.map(
    element => getUniqueSelector(element)
    )
    innerRule.selectorText += ':where(' + selectors + ')'
    }
    }
    }
    }
    }
    }
    }

    let observer = new MutationObserver(() => polyfillScope(document));
    observer.observe(document.documentElement, { childList: true, subtree: true });
    polyfillScope(document);