0.2.1 • Published 2 months ago

posthtml-css-inline v0.2.1

Weekly downloads
-
License
MIT
Repository
github
Last release
2 months ago

Version Build License Downloads

This is a work in progress, it's not ready for production yet.

TODO:

  • Support <link rel="stylesheet"> tags
  • Remove inlined selectors from HTML elements
  • Support PostCSS plugins
  • Support complex selectors *
  • Safelist (selectors that should not be inlined)
  • Skip inlining on marked tags
  • Juice-compatible options
    • excludedProperties
    • resolveCSSVariables
    • applyHeightAttributes
    • applyWidthAttributes
    • applyAttributesTableElements

* This needs to be implemented in PostHTML or posthtml-match-helper first.


About

This plugin will inline CSS from <style> and <link rel="stylesheet"> tags into HTML style attributes.

The CSS will be parsed with PostCSS, so you can use PostCSS plugins to transform the CSS before it's inlined.

Use cases:

  • HTML emails
  • Embedding HTML in 3rd party websites

Given something like this:

<style>
  div {
    color: red;
  }

  @media (max-width: 600px) {
    .text-sm {
      font-size: 16px;
    }
  }
</style>
<div class="text-sm">Test</div>

... it will output:

<style>
  div {
    color: red;
  }

  @media (max-width: 600px) {
    .text-sm {
      font-size: 16px;
    }
  }
</style>
<div class="text-sm" style="color: red;">Test</div>

Install

$ npm i posthtml posthtml-css-inline

Usage

To use the plugin, simply pass it to PostHTML:

import posthtml from'posthtml'
import inlineCss from'posthtml-css-inline'

posthtml([
  inlineCss(options)
])
  .process('your HTML')
  .then(result => console.log(result.html))

Options

You may configure how inlining works by passing an options object to the plugin.

OptionTypeDefaultDescription
processLinkTagsbooleanfalseProcess <link rel="stylesheet"> tags.
preserveImportantbooleanfalsePreserve !important in the inlined CSS value.
removeInlinedSelectorsbooleanfalseRemove selectors that were successfully inlined from both the <style> tag and from the HTML body.
postcssobject{}Object to configure PostCSS.
safelistarray[]Array of selectors that should not be inlined.
excludedPropertiesarray[]Array of CSS properties that should not be inlined.

Attributes

You may configure how inlining works on a per-element basis.

AttributeDescription
no-inlineSkip inlining on this element.

processLinkTags

Type: boolean\ Default: false

Whether to process <link rel="stylesheet"> tags.

The plugin will fetch the CSS from the URL in the href attribute, and replace the <link> tag with a <style> tag containing the CSS. This <style> tag will then be inlined into the HTML.

import posthtml from'posthtml'
import inlineCss from'posthtml-css-inline'

posthtml([
  inlineCss({
    processLinkTags: true
  })
])
  .process(`
    <link rel="stylesheet" href="public/styles.css">

    <p class="text-sm">small text</p>
  `)
  .then(result => result.html)
/* public/styles.css */
.text-sm {
  font-size: 12px;
}

Result:

<p class="text-sm" style="font-size: 12px">small text</p>

preserveImportant

Type: boolean\ Default: false

Whether to preserve !important in the inlined CSS value.

For example this:

import posthtml from'posthtml'
import inlineCss from'posthtml-css-inline'

posthtml([
  inlineCss({
    preserveImportant: true
  })
])
  .process(`
    <style>
      .text-sm {
        font-size: 12px;
      }

      div {
        font-size: 14px !important;
      }
    </style>

    <p class="text-sm">small text</p>
  `)
  .then(result => result.html)

... will output this:

<p class="text-sm" style="font-size: 14px !important">small text</p>

removeInlinedSelectors

Type: boolean\ Default: false

Whether to remove selectors that were successfully inlined from both the <style> tag and from the HTML body.

If a selector that has been inlined is also present inside an at-rule such as @media, it will not be removed from the HTML body.

import posthtml from'posthtml'
import inlineCss from'posthtml-css-inline'

posthtml([
  inlineCss({
    removeInlinedSelectors: true
  })
])
  .process(`
    <style>
      .text-sm {
        font-size: 12px;
      }

      @media (min-width: 640px) {
        .text-sm {
          font-size: 16px !important;
        }
      }
    </style>

    <p class="text-sm">small text</p>
  `)
  .then(result => result.html)

Result:

<style>
  @media (min-width: 640px) {
    .text-sm {
      font-size: 16px !important;
    }
  }
</style>

<p class="text-sm" style="font-size: 12px">small text</p>

postcss

Type: object\ Default: {}

You may configure PostCSS and use PostCSS plugins to transform the CSS before it's inlined.

import posthtml from'posthtml'
import inlineCss from'posthtml-css-inline'
// Imaginary PostCSS plugin that removes !important
import removeImportant from'remove-important-plugin'

posthtml([
  inlineCss({
    postcss: {
      plugins: [
        removeImportant
      ]
    }
  })
])
  .process(`
    <style>
      .text-sm {
        font-size: 12px !important;
      }

      @media (min-width: 640px) {
        .text-sm {
          font-size: 16px !important;
        }
      }
    </style>

    <p class="text-sm">small text</p>
  `)
  .then(result => result.html)

Result:

<style>
  .text-sm {
    font-size: 12px;
  }

  @media (min-width: 640px) {
    .text-sm {
      font-size: 16px;
    }
  }
</style>

<p class="text-sm" style="font-size: 12px">small text</p>

safelist

Type: array\ Default: []

Array of selectors that should not be inlined.

!NOTE
The CSS * selector is not supported when inlining, you don't need to worry about safelisting it.

import posthtml from'posthtml'
import inlineCss from'posthtml-css-inline'

posthtml([
  inlineCss({
    safelist: ['body', '.flex']
  })
])
  .process(`
    <style>
      .flex {
        display: flex;
      }

      body {
        color: blue;
      }

      p {
        color: red;
      }
    </style>

    <body>
      <p class="flex">small text</p>
    </body>
  `)
  .then(result => result.html)

Result:

<body>
  <p class="flex" style="color: red">small text</p>
</body>

excludedProperties

Type: array\ Default: []

Array of CSS properties that should not be inlined.

import posthtml from'posthtml'
import inlineCss from'posthtml-css-inline'

posthtml([
  inlineCss({
    excludedProperties: ['color', 'display']
  })
])
  .process(`
    <style>
      p {
        color: red;
        display: flex;
        font-size: 12px;
      }
    </style>

    <p>text</p>
  `)
  .then(result => result.html)

Result:

<style>
  p {
    color: red;
    display: flex;
    font-size: 12px;
  }
</style>

<p style="font-size: 12px">text</p>

no-inline

You may use the no-inline attribute on an element to prevent CSS inlining.

  • when used on a <style> or <link> tag, the CSS inside the tag will not be inlined
  • when used on any other tag, the inliner will not inline CSS on that tag

You may use any of the following attributes to achieve this on a <style> or <link> tag:

  • no-inline
  • data-embed
  • embed
  • prevent-inline
  • skip-inline

Likewise, you may use any of the following attributes to achieve this on any other tag:

  • no-inline
  • prevent-inline
  • skip-inline

The attribute will be removed from the tag in the resulting HTML.

import posthtml from'posthtml'
import inlineCss from'posthtml-css-inline'

posthtml([
  inlineCss()
])
  .process(`
    <style no-inline>
      p {
        font-size: 12px;
      }
    </style>
    <style>
      div {
        color: blue;
      }
    </style>

    <p>small text</p>
    <div no-inline>b</div>
  `)
  .then(result => result.html)

Result:

<style>
  p {
    font-size: 12px;
  }
</style>
<style>
  div {
    color: blue;
  }
</style>

<p>small text</p>
<div>b</div>