Skip to content

Special And Escaped Sequences

Anton edited this page Feb 10, 2020 · 1 revision

TrapCSS does not work well with unusual selectors, like the ones used by the popular Tailwind CSS framework:

class attributes can look like this:

<div class="px-6 pt-6 overflow-y-auto text-base lg:text-sm lg:py-12 lg:pl-6 lg:pr-8 sticky?lg:h-(screen-16)"></div>
<div class="px-2 -mx-2 py-1 transition-fast relative block hover:translate-r-2px hover:text-gray-900 text-gray-600 font-medium"></div>

...and the CSS looks like this:

.sticky\?lg\:h-\(screen-16\){}
.lg\:text-sm{
  font-size: small;
}
.lg\:focus\:text-green-700:focus{}

Ouch.

The solution is to temporarily replace the escaped characters in the HTML and CSS with some unique strings which match /[\w-]/. This allows TrapCSS's tokenizer to consider the classname as one contiguous thing. After processing, we simply reverse the operation.

// remap
let css2 = css
  .replace(/\\:/gm, '__0')
  .replace(/\\\//gm, '__1')
  .replace(/\\\?/gm, '__2')
  .replace(/\\\(/gm, '__3')
  .replace(/\\\)/gm, '__4')

let html2 = html.replace(/class=["'][^"']*["']/gm, m =>
  m
    .replace(/:/gm, '__0')
    .replace(/\//gm, '__1')
    .replace(/\?/gm, '__2')
    .replace(/\(/gm, '__3')
    .replace(/\)/gm, '__4')
)

let res = trapcss({
  css: css2,
  html: html2,
})

// undo
res.css = res.css
  .replace(/__0/gm, '\\:')
  .replace(/__1/gm, '\\/')
  .replace(/__2/gm, '\\?')
  .replace(/__3/gm, '\\(')
  .replace(/__4/gm, '\\)')

console.log(res.css)

This performant work-around allows TrapCSS to process Tailwind without issues \o/ and is easily adaptable to support other "interesting" cases. One thing to keep in mind is that shouldDrop() will be called with selectors containing the temp replacements rather than original selectors, so make sure to account for this if shouldDrop() is used to test against some whitelist.

.lg\:text-sm{font-size: small;}