Skip to content

Full static extraction to CSS file while keeping all dynamic parts intact. #579

@kof

Description

@kof

The idea is to optimize it in a way that shifts preprocessing runtime overhead to the build stage, while keeping all dynamic parts working. It includes 2 stages: one is babel plugin, another is webpack plugin.

With full extraction,:

  • There will be no static styles at runtime that need to be parsed and injected (only dynamic)
  • No double loading of static styles in JS build + CSS build.

With babel plugin only there will be no runtime processing of static styles, only dynamic. Current state is that jss core without plugins with styles object vs preprocessed version of the same object results in 50% performance boost.

Exmple

// source js
const styles = {
  static: {
    color: 'green'
  },
  mixed: {
    color: 'red',
    margin: (props) => props.spacing
  }
}

createStyleSheet(styles).attach()

// generated js
const styles = {
  '@raw': `
    .static-0-0-1 {
      color: green; 
    }
    .mixed-0-0-2 {
      color: red;
    }
  `,
  mixed: {
    margin: (props) => props.spacing
  }
}

const {classes} = createStyleSheet(styles, {
  classes: {
    static: 'static-0-0-1', 
    mixed: 'mixed-0-0-2'
  }
}).attach()

Todo babel plugin

  • Identify injectSheet(styles, options), createStyleSheet(styles, options) calls, make it customizable for different function names.
  • allow to pass jss config to the babel plugin
  • extract static styles object literal
  • extract static styles from the reference
  • extract any value from reference
  • extract sheet options if provided
  • extract sheet options from reference
  • extract sheet options nested properties from reference
  • create jss with plugins => createStyleSheet(styles, options).toString()
  • insert static css as a @raw rule into the styles declaration
  • remove original static styles
  • pass classes map from static sheet as options
  • math expressions
  • function call results
  • css preprocessing pipeline (with postcss)
  • autoprefixer
  • babel like theming configuration over file system in any directory????

Todo core

  • implement option classes to createStyleSheet
  • implement@raw plugin, add it to default preset
  • make a bench comparing @raw with equivalent style objects
  • docs
  • blogpost?

Todo webpack plugin

  • identify @raw rule
  • extract css
  • remove the rule
  • provide the css to webpack so that other loaders can use it (tbd how)

Future enhancements

  • Think of potential solution to the problem: {padding: (props) => props.spacing, paddingLeft: 10} after compilation paddingLeft will be overwritten by padding since it will have higher source order specificity
  • If static CSS is used without critical CSS over SSR, dynamic styles are not part of the static bundle and styling is incomplete. Think of a strategy to warn/require default values for dynamic styles.
  • a demo app with webpack/postcss/css-modules/autoprefixer
  • When there are no dynamic styles, remove all jss runtime code from the module
  • When function values/rules are not using props and can be statically extracted (see linaria)
  • Separate entry point jss/static for a reduced version of jss which does not include any plugins etc logic, since it is all preprocessed (unless we can treeshake it???)
  • Remove unused styles or warn when any detected, see this and the article for e.g.

Some inspiration can be taken from

https://github.com/4Catalyzer/css-literal-loader
https://github.com/callstack-io/linaria
https://www.npmjs.com/package/extract-jss-webpack-plugin

Metadata

Metadata

Assignees

No one assigned

    Labels

    complexity:highBest brains need to talk about it.feature requestThis will safe many lifes!importantThe thing you do when you wake up!perf

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions