Origin saw that earlyBirdCamp was using tailwindCSS and took a look

What is tailwindcss

If you open tailwindCSS github repository, probably will be a face mask, how CSS framework a bunch of JS, and CSS files are also strange atrule? It can be said that tailwindcss on the use of postcss really on a level, I did not expect to play this way! By using JS configuration, postCSs-js and postCSs-nested to parse our configuration, generate nodes, and then finally use postCSS to generate CSS. You can even use puregecss to crop the generated CSS, or in more familiar terms, We can tree shaking our CSS, which sounds really advanced. Here will not be detailed, probably pick a few more characteristic to talk about.

The classic tailwind. CSS

Atrule is a node of the AST in postCSS. It represents the @ node in CSS. The params of this node are followed by the params of this node. Go to astExplorer and the API documentation for postCSS

@tailwind base;

@tailwind components;

@tailwind utilities;

Copy the code

In tailwind CSS, such a classic node would do two things: @tailwind base; In this case, when the postCSS plugin encounters the atrule, it recognizes that params is a base, generates the content of the pluginBase node into the CSS, and fixes the source so that the Sourcemap does not fail. So if you look down here, you can see, now I’m going to insert before atRule, and I’m going to remove this atRule

css.walkAtRules('tailwind', atRule => {
      if (atRule.params === 'preflight') {
        // prettier-ignore
        throw atRule.error("' @tailwind preflight 'is not a valid at-rule in tailwind V1.0, use' @tailwind base 'instead.", { word: 'preflight'})}if (atRule.params === 'base') {
        atRule.before(updateSource(pluginBase, atRule.source))
        atRule.remove()
      }

      if (atRule.params === 'components') {
        atRule.before(postcss.comment({ text: 'tailwind start components' }))
        atRule.before(updateSource(pluginComponents, atRule.source))
        atRule.after(postcss.comment({ text: 'tailwind end components' }))
        atRule.remove()
      }

      if (atRule.params === 'utilities') {
        atRule.before(postcss.comment({ text: 'tailwind start utilities' }))
        atRule.before(updateSource(pluginUtilities, atRule.source))
        atRule.after(postcss.comment({ text: 'tailwind end utilities' }))
        atRule.remove()
      }

      if (atRule.params === 'screens') {
        includesScreensExplicitly = true
        atRule.before(postcss.comment({ text: 'tailwind start screens' }))
        atRule.after(postcss.comment({ text: 'tailwind end screens'}}})))if(! includesScreensExplicitly) { css.append([ postcss.comment({ text:'tailwind start screens' }),
        postcss.atRule({ name: 'tailwind', params: 'screens' }),
        postcss.comment({ text: 'tailwind end screens'}}),])Copy the code

To fix the sourcemap pointer, see the following function to make all newly generated nodes point to the source of the original atRule, perfect ~

function updateSource(nodes, source) {
  return _.tap(Array.isArray(nodes) ? postcss.root({ nodes }) : nodes, tree => {
    tree.walk(node => (node.source = source))})}Copy the code

One more screen atRule

Instead of writing a raw media query that duplicates that value like this:

@media (min-width: 640px) {
  /* ... */
}
Copy the code

you can use the @screen directive and reference the breakpoint by name:

@screen sm {
  /* ... */
}
Copy the code

The above English, copied from the document, is a common media query. We can write to CSS, but what if we need to change it later? Cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper cheaper

import _ from 'lodash'
import buildMediaQuery from '.. /util/buildMediaQuery'

export default function({ theme }) {
  return function(css) {
    css.walkAtRules('screen', atRule => {
      const screen = atRule.params

      if(! _.has(theme.screens, screen)) { throw atRule.error(`No \`${screen}\` screen found.`)
      }

      atRule.name = 'media'
      atRule.params = buildMediaQuery(theme.screens[screen])
    })
  }
}

Copy the code

Write about tailwindcss plugin

Tailwindcss plug-in writing method, specific how to write, here is not specific expansion, here about the principle. In fact, the main function of plug-ins is to generate a preliminary CSS configuration of JS CSS we wrote, and then according to the configuration file, a secondary processing, and finally generate the actual CSS. Take a built-in zIndex plug-in as an example, mainly this plug-in also wrote test cases, combined with the document, It was a very inconvenient introduction

First let’s see how this plugin is written

import _ from 'lodash'
import prefixNegativeModifiers from '.. /util/prefixNegativeModifiers'

export default function() {
  return function({ addUtilities, e, theme, variants }) {
    const utilities = _.fromPairs(
      _.map(theme('zIndex'), (value, modifier) => {
        return[`.${e(prefixNegativeModifiers('z', modifier))}`,
          {
            'z-index': value,
          },
        ]
      })
    )

    addUtilities(utilities, variants('zIndex'))}}Copy the code

Arrunez (‘zIndex’) will pay its way into addUtilities, which will later lead to the development of zIndex. And finally, @tailwind Utilities; “, and see what will be generated in the test case. It will look something like this. To put it more simply, we will have the @responsive code generated by our utilities bundled together to generate our CSS. Then substituteResponsiveAtRules this will handle this atRule, secondary to produce the final CSS

expect(addedUtilities).toEqual([
    {
      utilities: {
        '.-z-20': { 'z-index': '-' },
        '.-z-10': { 'z-index': '- 10' },
        '.z-10': { 'z-index': '10' },
        '.z-20': { 'z-index': '20' },
      },
      variants: ['responsive'],},])Copy the code

One last word

Tailwindcss has a lot of gameplay, specific can go to document mining, here is just a brief introduction to the principle of its implementation, it gives me a shock is that I did not expect postcss or CSS can also play this way, imagination is too big, ps: in order to understand this product, basically the main rely on the library of the document took a look, : escape)