Code Monkey home page Code Monkey logo

postcss-extract-media-query's Introduction

postcss-extract-media-query

Build Status

If page speed is important to you chances are high you're already doing code splitting. If your CSS is built mobile-first (in particular if using a framework such as Bootstrap or Foundation) chances are also high you're loading more CSS than the current viewport actually needs.

It would be much better if a mobile user doesn't need to load desktop specific CSS, wouldn't it?

That's the use case I've written this PostCSS plugin for! It lets you extract all @media rules from your CSS and emit them as separate files which you can load as <link rel="stylesheet" media="screen and (min-width: 1024px)" href="desktop.css"> or as dynamic import.

Before

  • example.css
.foo {
  color: red;
}
@media screen and (min-width: 1024px) {
  .foo {
    color: green;
  }
}
.bar {
  font-size: 1rem;
}
@media screen and (min-width: 1024px) {
  .bar {
    font-size: 2rem;
  }
}

After

  • example.css
.foo {
  color: red;
}
.bar {
  font-size: 1rem;
}
  • example-desktop.css
@media screen and (min-width: 1024px) {
  .foo {
    color: green;
  }
  .bar {
    font-size: 2rem;
  }
}

Installation

  • npm
npm install postcss postcss-extract-media-query --save-dev
  • yarn
yarn add postcss postcss-extract-media-query --dev

Usage

Simply add the plugin to your PostCSS config. If you're not familiar with using PostCSS you should read the official PostCSS documentation first.

You can find complete examples here.

Options

option default
output.path path.join(__dirname, '..')
output.name '[name]-[query].[ext]'
queries {}
extractAll true
stats true
entry null

output

By default the plugin will emit the extracted CSS files to your root folder. If you want to change this you have to define an absolute path for output.path.

Apart from that you can customize the emited filenames by using output.name. [name] is the filename of the original CSS file, [query] the key of the extracted media query and [ext] the orignal file extension (mostly css). Those three placeholders get replaced by the plugin later.

โš ๏ธ by emiting files itself the plugin breaks out of your bundler / task runner context meaning all your other loaders / pipes won't get applied to the extracted files!

'postcss-extract-media-query': {
    output: {
        path: path.join(__dirname, 'dist'), // emit to 'dist' folder in root
        name: '[name]-[query].[ext]' // pattern of emited files
    }
}

queries

By default the params of the extracted media query is converted to kebab case and taken as key (e.g. screen-and-min-width-1024-px). You can change this by defining a certain name for a certain match. Make sure it exactly matches the params (see example below).

'postcss-extract-media-query': {
    queries: {
        'screen and (min-width: 1024px)': 'desktop'
    }
}

extractAll

By default the plugin extracts all media queries into separate files. If you want it to only extract the ones you've defined a certain name for (see queries option) you have to set this option false. This ignores all media queries that don't have a custom name defined.

'postcss-extract-media-query': {
    extractAll: false
}

stats

By default the plugin displays in your terminal / command prompt which files have been emited. If you don't want to see it just set this option false.

'postcss-extract-media-query': {
    stats: true
}

entry

By default the plugin uses the from value from the options of the loader or of the options you define in postcss().process(css, { from: ... }). Usually you don't need to change it but if you have to (e.g. when using the plugin standalone) you can define an absolute file path as entry.

'postcss-extract-media-query': {
    entry: path.join(__dirname, 'some/path/example.css')
}

config

By default the plugin looks for a postcss.config.js file in your project's root (read node-app-root-path to understand how root is determined) and tries to apply all subsequent PostCSS plugins to the extracted CSS.

In case this lookup doesn't suite you it's possible to specify the config path yourself.

'postcss-extract-media-query': {
    config: path.join(__dirname, 'some/path/postcss.config.js')
}

It's also possible to pass the config as object to avoid any file resolution.

'postcss-extract-media-query': {
    config: {
        plugins: {
            'postcss-extract-media-query': {}
            'cssnano': {}
        }
    }
}

Migration

coming from 2.x

PostCSS has been updated to 8.x (which is the minimum required version now) and is no longer packaged with this plugin but has become a peer dependency. What does this mean for you? If you're using npm >= v7 it's automatically installed, otherwise you need to install it yourself.

npm install postcss --save-dev

coming from 1.x

Both options, combine and minimize, have been removed in v2 because the plugin parses your postcss.config.js now and applies all subsequent plugins to the extracted files as well.

So if you have used them you simply need to install appropriate PostCSS plugins (see below for example) and add them to your PostCSS config.

npm install postcss postcss-combine-media-query cssnano --save-dev
plugins: {
    'postcss-combine-media-query': {},
    'postcss-extract-media-query': {},
    'cssnano': {},
}

plugin authors

If you're using this plugin via the api (e.g. for your own plugin) you should note it has changed from sync to async in v2. This was necessary in the course of going with promises. I'm not going to keep support of the sync api because it would make the code more complex than necessary and it's officially recommended to use async. Please check the tests to see how it has to be done now!

Webpack User?

If you're using webpack you should use media-query-plugin which is built for webpack only and thus comes with several advantages such as applying all other loaders you've defined and hash support for caching.

Credits

If this plugin is helpful to you it'll be great when you give me a star on github and share it. Keeps me motivated to continue the development.

postcss-extract-media-query's People

Contributors

dependabot[bot] avatar faebeee avatar sassninja avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

postcss-extract-media-query's Issues

Unknown error, different PostCSS versions

I tried to implement postcss-extract-media-query inside webpack but i keep running into this error,

Unknown error from PostCSS plugin. Your current PostCSS version is 7.0.35, but postcss-extract-media-query uses 6.0.23. Perhaps this is the source of the error below.

Followed by,

ERROR in ./assets/scss/app.scss friendly-errors 22:07:18 Module build failed (from ./node_modules/postcss-loader/src/index.js): friendly-errors 22:07:18 TypeError: Cannot read property 'syntax' of undefined at /Volumes/SSD/Projects/Purvis/Frontend/assets/scss/app.scss:369:3 at Root.toString (/Volumes/SSD/Projects/Purvis/Frontend/node_modules/postcss-syntax/patch-postcss.js:38:67) at /Volumes/SSD/Projects/Purvis/Frontend/node_modules/postcss-extract-media-query/index.js:81:63 at /Volumes/SSD/Projects/Purvis/Frontend/node_modules/postcss/lib/container.js:314:16 at /Volumes/SSD/Projects/Purvis/Frontend/node_modules/postcss/lib/container.js:139:18 at Root.each (/Volumes/SSD/Projects/Purvis/Frontend/node_modules/postcss/lib/container.js:105:16) at Root.walk (/Volumes/SSD/Projects/Purvis/Frontend/node_modules/postcss/lib/container.js:135:17) at Root.walkAtRules (/Volumes/SSD/Projects/Purvis/Frontend/node_modules/postcss/lib/container.js:312:17) at /Volumes/SSD/Projects/Purvis/Frontend/node_modules/postcss-extract-media-query/index.js:75:18 at LazyResult.run (/Volumes/SSD/Projects/Purvis/Frontend/node_modules/postcss/lib/lazy-result.js:288:14) at LazyResult.run (/Volumes/SSD/Projects/Purvis/Frontend/node_modules/postcss-syntax/patch-postcss.js:30:73) at LazyResult.asyncTick (/Volumes/SSD/Projects/Purvis/Frontend/node_modules/postcss/lib/lazy-result.js:212:26) at LazyResult.asyncTick (/Volumes/SSD/Projects/Purvis/Frontend/node_modules/postcss/lib/lazy-result.js:225:14) at /Volumes/SSD/Projects/Purvis/Frontend/node_modules/postcss/lib/lazy-result.js:217:17

Last time i tried it everything seemed to work fine (a month or 2 ago) but i didn't finished it (had some PurgeCSS fixes first). This week i wanted to squeeze some extra performance out of my NuxtJS project (especially for mobile) so i picked this one up again but no luck because of the above error.

Probably i'm doing something wrong but i couldn't find anything in the issues (or closed ones) over here.

Related webpack config,

build: { // analyze: true, extend(config, ctx) {}, extractCSS: true, postcss: { plugins: { '@fullhuman/postcss-purgecss': { content: [ './pages/**/*.vue', './layouts/**/*.vue', './components/*.vue', './components/*/*.vue' // Subfolders also ], safelist: { standard: [ /body/, /btn/, /collapse/, /collapsed/, /form-control/, /input-group/, /page-item/, /page-link/, /pagination/, /tooltip/ ], greedy: [/custom-select/] } }, 'postcss-extract-media-query': { extractAll: true } } } }

Keep the media in same file

I am using Preact with custom postcss.config.js. I want my styles to stay in same file or in the same output (inline CSS). How to do that.

extractAll not working

Hey,

I'm trying to only extract media queries for one size, but I can't seem to get the extractAll option to work.

Using your gulp example as a starting point here is my postcss.config.js

image

I'm still getting an example-screen-and-min-width-px.css

image

Any help would be much appreciated, awesome plugin btw!

@supports queries in @media queries get removed

Hi,

@supports queries in with @media queries are not found in the output.

If using @supports queries only (for the mobile stylesheet) the output is fine, but combined with @media queries, they disappear.

Could you please have a look at that?

Proposal: attach processing to OnceExit hook

Right now processing exported files is done by reapplying plugins to extracted files, but I could be possible just to rely on the order of execution, and thus putting postcss-extract-media-query as the last plugin in stack, after processing by other plugins.

Attaching to OnceExit postcss hook (instead of Once) ensures that the plugin code is executed at the end.

module.exports = {
  plugins = [
    require('cssnano'),
    require('postcss-extract-media-query')({
      output: { path: './' }
    })
  ]
}

Config above would firstly minify our css file, then extract it to separate files, with processing applied. At the moment it is possible only by attaching additional config to in plugin settings, which specifies that we want to minify our files.

The change, I mention would take care of such processing mostly out of the box, but it would possibly be a breaking change, because it would require a correct order of declaring plugins in postcss.config.js

media css files generated but default.css missing

Hi,

I am running postcss-extract-media-query as a standalone node js instance, and all my media query css files are created.

I dont see the 'default.css' file with the leftovers on my file system.

hereby my code:

const options = {
    output: {
        path: path.join(__dirname), // emit to 'dist' folder in root
        name: '[name]-[query].[ext]' // pattern of emited files
    }
};


fs.readFile('raw_css.css', 'utf8', function (err, data) {
    if (err) {
        return console.log(err);
    }

    postcss([extractmq(options)]).process(data).then(result => {
            console.log('new css file written')
        });      
    })

can you give me advice?

btw, since I would like to futher process the results, how can I store the results in an object instead of file system? I don't use webpack, I extract css from an url and parse in your code.

Thanks a lot!

Sass files in dist folder (Using Vite example)

Apologies if this has already come up but I did not find an issue related to this.

Here are the steps to re-create, I am using the Vite example from this repo.

  1. install sass: npm i sass
  2. create .scss file in /src
  3. Import .scss file in example.js
  4. run: npm run start
  5. Dist/ folder contains .scss file - this should be a .css file instead

README file suggests this approach has a bigger performance impact than it actually does

In the README file we say:

It would be much better if a mobile user doesn't need to load desktop specific CSS, wouldn't it?

But the media attribute of a stylesheet doesn't actually prevent it from downloading on non-matching media queries:

https://blog.tomayac.com/2018/11/08/why-browsers-download-stylesheets-with-non-matching-media-queries-180513/

Instead, what happens is the CSS file is given a lower download priority.

It would be good if the README was updated to provide a more accurate description of the performance benefits that the approach of media query splitting actually gives.

Multiple media queries into one file.

Here is my config
const path = require('path');

    'postcss-extract-media-query': {
        output: {
            path: path.join(__dirname, 'css')
        },
        queries: {
            'only screen and (min-width:768px) and (max-width:958px), only screen and (min-width:480px) and (max-width:700px)': 'desktop',
            'only screen and (max-width:480px)': 'mobile'
        },
        whitelist: true,
        combine: true
    }

If i have ONLY one media query per file. it works great. ex only screen and (min-width:768px) and (max-width:958px) or only screen and (min-width:480px) and (max-width:700px).

But when i try to use them both, to be placed into desktop.css this dosen't work. What could be the problem? Thank you.

Duplicated a media rules in css

my postcss configuration in laravel mix:

postCss: [
    require('postcss-extract-media-query')({
		output: {
		  path: path.join(__dirname, '/public/css'), 
		  name: '[query].css'
		},
		queries: {
		  'only screen and (min-width: 1023px)': 'desktop',
		  'only screen and (min-width: 1460px)': 'desktop',
		  'only screen and (min-width: 1800px)': 'desktop',
		  'only screen and (min-width: 1870px)': 'desktop',
		  'only screen and (min-width: 2100px)': 'desktop',
		  'only screen and (min-width: 2400px)': 'desktop'
		},
		extractAll: false
    }),
  ],

Created desktop.css with all queries but thes repeating. The queries save in desktop.css first time and then second time the same way after first time. 'postcss-combine-duplicated-selectors' and '"cssnano": {}' don't helped me
And is strange that console output three times:

[extracted media query] desktop.css
[extracted media query] desktop.css
[extracted media query] desktop.css

P.S. I want to do pull request in documentation, but wait for an answer(if code have mistakes). I spent more than an hour until I realized how it works

File naming gets confused if source file names have more than one dot in them

I've found that if my input files have more than one dot, the output file name is mixed up.

This is my config:

{
  output: {
    path: './theme/assets/',
    name: '[name]--[query].[ext]',
  },
  queries: {
    'prefers-reduced-motion: reduce': 'nomotion',
    'all and (-ms-high-contrast: none), (-ms-high-contrast: active)': 'ie'
  },
  whitelist: true
}

If either of those queries are triggered on the file _components.order-form.css, the resulting file name is _components--ie.order-form (notice the file extension is missing). The output I'm expecting here would be _components.order-form--ie.css.

Possibility to process single file

Scenario: I don't want to extract the media queries of (lazy-loaded) component-specific css, I only want to do this on my main stylesheet. Is this possible? Thanks.

Webpack & entrypoints.json

Hello,

I am using Webpack 5.0 and Webpack Encore from Symfony framework.

I noticed the generation of outgoing media stylesheets is done before Webpack setup its output directory.
So these media files get deleted, if put inside the same directory as Webpack output.

Additionally, there is no entrypoints.json. Would it be easy to do ?
For instance, I basically define an "app" entry and I get "app-mobile.css".
So at the moment the loading of this file is hardcoded, but I need to make it more dynamic.

Thank you for the hard work. This plugin is great for optimization !

Invalid PostCSS Plugin found

Hi, I'm trying to extract some media queries from my css bundles, but unfortunately plugin is not working.

ERROR in ./node_modules/css-loader!./node_modules/postcss-loader/src!./node_modules/jscrollpane/style/jquery.jscrollpane.css
    Module build failed: TypeError: Invalid PostCSS Plugin found at: plugins[0]


Here is my postcss.config.js

const path = require('path');

module.exports = {
    plugins: [
        {
            'postcss-extract-media-query': {
                output: {
                    path: path.join(__dirname, 'dist'),
                    name: '[name]-[query].[ext]'
                },
                queries: {
                    'screen and (min-width: 1024px)': 'desktop'
                }
            }
        }
    ]
}

Postcss works fine without this plugin. I'm using webpack 3 with it.
Part of my package.json

    "postcss": "^7.0.14",
    "postcss-extract-media-query": "^1.2.0",
    "postcss-loader": "^3.0.0",

Do you have any idea why it's not working?

cssnano and sourcemaps

did someone tried to create correct sourcemaps after the extraction? and my 2nd question is: is it possible to run cssnano to minify all extracted files directly without any temporary folder? there is an option for that i know, but cssnano do a lot more than just minify.

thx!! great plugin!!

Rules get applied every time a new file is processed

Hey!

I have a simple gulp process:

`gulp.task('css', () => {

const plugins = [
	extractMediaQuery({
		output: {
			path: dirs.dest + '/components',
		}
	})
];
return gulp.src('./dist/components/*.css')
	.pipe(postcss(plugins))
	.pipe(gulp.dest('./dist/components'));

});`

The CSS is already process from sass to css in a step before. So i have various css files in my dist/components folder.
No i want to run extractMediaQuery on it. This works.

BUT there is a major drawback:

The first file, that has extractable stuff in it, gets extracted fine. But on the next file, that has some media queries, the rules from the first extraction are applied again. And so on and so on.

So my first media css file is 318byte. The second one then too, even if the main css file doesn't have media queries. Next one is content + 318byte and so on and so on,

Not sure what i am doing wrong. Why is postcss applying the same stuff over and over again till the last file?

Here are as an example the first process file and the last process file.

@media screen and (min-width:768px){.badge .rating{padding:14px 0}} @media screen and (min-width:768px){.badge .text .image img{width:23px}} @media screen and (min-width:768px){.badge .text .rating-in-words .big{font-size:18px}} @media screen and (min-width:768px){.badge .text .rating-in-words .small{font-size:12px}}

@media screen and (min-width:768px){.badge .rating{padding:14px 0}} @media screen and (min-width:768px){.badge .text .image img{width:23px}} @media screen and (min-width:768px){.badge .text .rating-in-words .big{font-size:18px}} @media screen and (min-width:768px){.badge .text .rating-in-words .small{font-size:12px}} ......redacted by me.... @media screen and (min-width:768px){.toc{position:-webkit-sticky;position:sticky;top:88px}}

As you can see, the first four lines are reapplied.

Any help on that?

Possibility to leave original file intact?

Hi. Firstly thank you for making this plugin.

Is it possible when extracting media queries to leave the original file in tact with all the media queries in it? Im trying to extract all the media queries for mobile into their own file (to help serve to an amp page) but i want to still have the original file (with all media queries) available to serve to the desktop as normal.

At the minute the original file gets transformed to a file missing all the mobile media queries.

Plugin + Foundation setup

Hello,

This may be what I've been looking for. Here's what I'm trying to accomplish, to see if you think this may fit my needs. I'm currently using Foundation framework, and I can't seem to figure out how to get this to work within Foundation. Do you have any guidance or experience integrating this into Foundation and getting it to extract the media queries Foundation creates? For example, can it extract all non-media query styles into its own css file, and each media query into their own css files?

Thanks in advance!

Can this be used to only combine media queries?

I see that this plugin is able to combine similar media queries. Is there a way to only use this feature without extracting the queries to their own file?

I used to use the mq-packer plugin for postcss to combine similar media queries. Unfortunately that plugin is deprecated and no longer maintained.

Is it possible to use several entries?

Hello. In readme you mentioned that it's possible to use custom entry for specific css file. Is it possible to use several entries for several files? Can we customize each output for every entry ( if several entries are possible)?
Thanks.

Not creating css file for all other styles

I can only get the plugin to export files for the queries i enter, the css file with the queries removed no longer generates.

my config:

const path = require('path');

module.exports = {
  plugins: {
    "postcss-flexbugs-fixes":{},
    "postcss-preset-env":{  "stage": 3},
    'postcss-extract-media-query': {
      stats:true,
      entry:  path.join(__dirname, 'styles/global.css'),
      output: {
        path: path.join(__dirname, 'dist'), // emit to 'dist' folder in root
      },
      queries: {
        'print': "print",
        '(min-width:640px)': 'mobile',
        '(min-width:768px)': 'tablet',
        '(min-width:1280px)': 'desktop',
        '(min-width:1536px)': 'xl-desktop',
      },
      extractAll: false,
      config: path.join(__dirname, 'postcss.config.js')

    },
  },
};

generated files:

www/dist/global-desktop.css
www/dist/global-mobile.cs
www/dist/global-print.css
www/dist/global-tablet.css
www/dist/global-xl-desktop.css

notice there is not "global.css"

extractAll query mismatch

Specifying extractAll to false and using this query in postcss.config.js

queries: {
        "screen and (min-width: 1024px)": "desktop"
} 

against this css rule (base.css):

@media screen and (min-width: 1024px)

doesn't result in expected extraction of files base.css & base-desktop.css

Even though there is no visual indication of the strings not being equal we can confirm that they're not by comparing them:

if (opts.output.path) {
root.walkAtRules('media', atRule => {
    
const query = atRule.params;
const queryname = opts.queries[query] || (opts.extractAll && _.kebabCase(query));

const test = 'screen and (min-width: 1024px)';
console.log(query.localeCompare(test)); // = -1

Removing the space between min-width: and 1024px seems to do the trick:

queries: {
        "screen and (min-width:1024px)": "desktop"
} 

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.