or import it from a JavaScript file:
import './index.css';
CSS assets can contain dependencies referenced by @import
syntax, as well as references to images, fonts, etc. via the url()
function.
@import
The @import
at-rule can be used to inline another CSS file into the same CSS bundle as the containing file. This means that at runtime a separate network request will not be needed to load the dependency.
@import 'other.css';
Referenced files should be relative to the containing CSS file. You can also use absolute and tilde specifiers. To import a CSS file from npm, use the npm:
scheme.
@import 'npm:bootstrap/bootstrap.css';
When the @parcel/resolver-glob
plugin is enabled, you can also use globs to import multiple CSS files at once. See Glob specifiers for more details.
@import "./components/*.css";
url()
The url()
function can be used to reference a file, for example a background image or font. The referenced file will be processed by Parcel, and the URL reference will be rewritten to point to the output filename.
body {
background: url(images/background.png);
}
Referenced files should be relative to the containing CSS file. You can also use absolute and tilde specifiers. The data-url:
scheme can also be used to inline a file as a data URL. See Bundle inlining for more details.
.logo {
background: url('data-url:./logo.png');
}
By default, CSS imported from JavaScript is global. If two CSS files define the same class names, ids, custom properties, @keyframes
, etc., they will potentially clash and overwrite each other. To solve this, Parcel supports CSS modules.
CSS modules treat the classes defined in each file as unique. Each class name or identifier is renamed to include a unique hash, and a mapping is exported to JavaScript to allow referencing them.
To use CSS modules, create a file with the .module.css
extension, and import it from a JavaScript file with a namespace import. Then, you can access each of the classes defined in the CSS file as an export from the module.
import * as classes from './styles.module.css';
document.body.className = classes.body;
.body {
background: skyblue;
}
The .body
class will be renamed to something unique to avoid selector clashes with other CSS files.
CSS modules also work with other languages that compile to CSS, such as SASS, Less, or Stylus. Name your file using the corresponding file extension, such as .module.scss
, .module.less
, or .module.styl
.
Using CSS modules also has the benefit of making dependencies on specific class names explicit in your code. This enables unused CSS classes to be automatically removed.
As you can see in the above example, only the .button
class is used, so the unused .cta
class is removed from the compiled CSS file.
This also works with other unused CSS rules such as @keyframes
and @counter-style
, as well as CSS custom properties (when the dashedIdents
option is enabled).
Note: Tree shaking only works when you reference classes using either a namespace or named import. Tree shaking does not work with default imports.
import styles from './styles.module.css';
should be replaced with:
import * as styles from './styles.module.css';
By default, class names, id selectors, and the names of @keyframes
, @counter-style
, and CSS grid lines and areas are scoped to the module they are defined in. Scoping for CSS variables and other <dashed-ident>
names can also be enabled using the dashedIdents
configuration option in your project root package.json
.
{
"@parcel/transformer-css": {
"cssModules": {
"dashedIdents": true
}
}
}
When enabled, CSS variables will be renamed so they don't conflict with variable names defined in other files. Referencing a variable uses the standard var()
syntax, which Parcel will update to match the locally scoped variable name.
You can also reference variables defined in other files using the from
keyword:
.button {
background: var(--accent-color from "./vars.module.css");
}
:root {
--accent-color: hotpink;
}
Global variables may be referenced using the from global
syntax, however, they currently must be defined in a non-CSS module file.
@import "vars.css";
.button {
color: var(--color from global);
}
:root {
--color: purple;
}
The same syntax also applies to other CSS values that use the <dashed-ident>
syntax. For example, the @font-palette-values rule and font-palette property use the <dashed-ident>
syntax to define and refer to custom font color palettes, and will be scoped and referenced the same way as CSS variables.
By default, Parcel prepends the hash of the filename to each class name and identifier in a CSS file. You can configure this naming pattern using the "pattern"
option in your project root package.json
. This accepts a string with placeholders that will be filled in by Parcel, allowing you to add custom prefixes or adjust the naming convention for scoped classes.
{
"@parcel/transformer-css": {
"cssModules": {
"pattern": "my-company-[name]-[hash]-[local]"
}
}
}
The following placeholders are currently supported:
[name]
- The base name of the file, without the extension.[hash]
- A hash of the full file path.[local]
- The original class name or identifier.Note: CSS grid line names can be ambiguous due to automatic postfixing done by the browser, which generates line names ending with -start
and -end
for each grid template area. When using CSS grid, your "pattern"
configuration must end with the [local]
placeholder so that these references work correctly.
.grid {
grid-template-areas: "nav main";
}
.nav {
grid-column-start: nav-start;
}
{
"@parcel/transformer-css": {
"cssModules": {
// ❌ [local] must be at the end so that
// auto-generated grid line names work
"pattern": "[local]-[hash]"
// ✅ do this instead
"pattern": "[hash]-[local]"
}
}
}
By default, CSS modules are only enabled for files whose name ends with .module.css
. All other CSS files are treated as global CSS by default. However, this can be overridden to enable CSS modules for all source files (i.e. not in node_modules
) by configuring @parcel/transformer-css
in your project root package.json
.
{
"@parcel/transformer-css": {
"cssModules": true
}
}
When using a configuration object with other options, use the "global"
option instead.
{
"@parcel/transformer-css": {
"cssModules": {
"global": true,
// ...
}
}
}
Note: In prior versions of Parcel, postcss-modules
was used to implement CSS module support. Enabling CSS modules globally occurred in your project's PostCSS config file. This plugin can now be removed from your PostCSS config if you enable CSS modules as described above.
If this was the only PostCSS plugin you used, you can remove your PostCSS config entirely. This can improve build performance significantly. You may see a warning about this if you are not using any postcss-modules
config options.
Parcel includes support for transpiling modern CSS syntax to support older browsers out of the box, including vendor prefixing and syntax lowering. In addition, PostCSS is supported to enable custom CSS transformations.
By default Parcel does not perform any transpilation of CSS syntax for older browsers. This means that if you write your code using modern syntax or without vendor prefixes, that’s what Parcel will output. You can declare your app’s supported browsers using the browserslist
field in your package.json. When this field is declared, Parcel will transpile your code accordingly to ensure compatibility with your supported browsers.
{
"browserslist": "> 0.5%, last 2 versions, not dead"
}
See the Targets docs for more details on how to configure this.
Based on your configured browser targets, Parcel automatically adds vendor prefixed fallbacks for many CSS features. For example, when using the image-set()
function, Parcel will output a fallback -webkit-image-set()
value as well, since Chrome does not yet support the unprefixed value.
.logo {
background: image-set(url(logo.png) 2x, url(logo.png) 1x);
}
compiles to:
.logo {
background: -webkit-image-set(url(logo.png) 2x, url(logo.png) 1x);
background: image-set("logo.png" 2x, "logo.png");
}
In addition, if your CSS source code (or more likely a library) includes unnecessary vendor prefixes, Parcel CSS will automatically remove them to reduce bundle sizes. For example, when compiling for modern browsers, prefixed versions of the transition
property will be removed, since the unprefixed version is supported by all browsers.
.button {
-webkit-transition: background 200ms;
-moz-transition: background 200ms;
transition: background 200ms;
}
becomes:
.button {
transition: background .2s;
}
Parcel automatically compiles many modern CSS syntax features to more compatible output that is supported in your target browsers.
The following features are supported:
color-mix()
functionmargin-inline-start
@media (width <= 100px)
or @media (100px < width < 500px)
place-items
and place-content
clamp()
functionred 40% 80%
)overflow
shorthanddisplay
property (e.g. inline flex
)Parcel can also be configured to compile several draft specs that are not yet available natively in any browser. Because these are drafts and the syntax can still change, they must be enabled manually in your project.
The CSS Nesting draft spec enables style rules to be nested, with the selectors of the child rules extending the parent selector in some way. This is very commonly supported by CSS pre-processors like SASS, but with this spec, it will eventually be supported natively in browsers. Parcel compiles this syntax to un-nested style rules that are supported in all browsers today.
Because nesting is a draft, it is not enabled by default. To use it, enable it by configuring @parcel/transformer-css
in your project root package.json
file.
{
"@parcel/transformer-css": {
"drafts": {
"nesting": true
}
}
}
Once enabled, any CSS file in your project can use directly nested style rules or the @nest
at rule.
Directly nested style rules must be prefixed with the &
nesting selector. This indicates where the parent selector will be substituted. For example:
.foo {
color: blue;
& > .bar { color: red; }
}
is equivalent to:
.foo { color: blue; }
.foo > .bar { color: red; }
The @nest rule allows nesting where the parent selector is substituted somewhere other than at the start.
.foo {
color: red;
@nest .parent & {
color: blue;
}
}
is equivalent to:
.foo { color: red; }
.parent .foo { color: blue; }
Conditional rules such as @media
may also be nested within a style rule, without repeating the selector. For example:
.foo {
display: grid;
@media (orientation: landscape) {
grid-auto-flow: column;
}
}
is equivalent to:
.foo { display: grid; }
@media (orientation: landscape) {
.foo {
grid-auto-flow: column;
}
}
Support for custom media queries is included in the Media Queries Level 5 draft spec. This allows you to define media queries that are reused in multiple places within a CSS file. Parcel CSS will perform this substitution ahead of time when this feature is enabled.
For example:
@custom-media --modern (color), (hover);
@media (--modern) and (width > 1024px) {
.a { color: green; }
}
is equivalent to:
@media ((color) or (hover)) and (width > 1024px) {
.a { color: green; }
}
Because custom media queries are a draft, they are not enabled by default. To use them, enable the customMedia
feature by configuring @parcel/transformer-css
in your project root package.json
file.
{
"@parcel/transformer-css": {
"drafts": {
"customMedia": true
}
}
}
Parcel supports replacing CSS pseudo classes such as :focus-visible
with normal CSS classes that can be applied using JavaScript. This makes it possible to polyfill these pseudo classes for older browsers.
Pseudo class mappings can be configured in your project root package.json
file:
{
"@parcel/transformer-css": {
"pseudoClasses": {
"focusVisible": "focus-visible"
}
}
}
The above configuration will result in the :focus-visible
pseudo class in all selectors being replaced with the .focus-visible
class. This enables you to use a JavaScript polyfill, which will apply the .focus-visible
class as appropriate.
The following pseudo classes may be configured as shown above:
hover
– corresponds to the :hover
pseudo classactive
– corresponds to the :active
pseudo classfocus
– corresponds to the :focus
pseudo classfocusVisible
– corresponds to the :focus-visible
pseudo classfocusWithin
– corresponds to the :focus-within
pseudo classPostCSS is a tool for transforming CSS with plugins. While Parcel supports equivalent functionality to many common PostCSS plugins such as autoprefixer and postcss-preset-env out of the box as described above, PostCSS is useful for more custom CSS transformations such as non-standard syntax additions. It is also used by popular CSS frameworks such as Tailwind.
You can use PostCSS with Parcel by creating a configuration file using one of these names: .postcssrc
, .postcssrc.json
, .postcssrc.js
, .postcssrc.mjs
, .postcssrc.cjs
, postcss.config.js
, postcss.config.mjs
, or postcss.config.cjs
.
First, install the postcss plugins you wish to use into your app:
yarn add tailwindcss --dev
Then, create a .postcssrc
. Plugins are specified in the plugins
object as keys, and options are defined using object values. If there are no options for a plugin, just set it to true
instead.
If your plugins require additional configuration, create those files as well. For example, with Tailwind, you need a tailwind.config.js
.
{
"plugins": {
"tailwindcss": true
}
}
module.exports = {
content: ["./src/*.{html,js}"],
theme: {
extend: {},
},
variants: {},
plugins: [],
};
Parcel includes equivalents of autoprefixer
and postcss-preset-env
automatically when a browserslist
is specified in your package.json
. These are implemented in Rust and are significantly faster than PostCSS. If these are the only transforms you need in your project, then you may not need PostCSS at all.
If you have an existing project with a PostCSS config containing only the above plugins, you may be able to remove it entirely. If you are using additional plugins, you can remove autoprefixer
and postcss-preset-env
while keeping only the custom plugins. This can significantly improve build performance since Parcel’s builtin transpiler is much faster than PostCSS.
See above for more details about Parcel’s builtin transpilation support.
By default, Parcel transforms each CSS file with PostCSS independently. However, some PostCSS plugins (e.g. postcss-custom-properties
) potentially need to access declarations from other @import
ed CSS assets.
In these cases, you can use postcss-import
to run PostCSS over the whole bundle at once instead. postcss-url
should also be used to ensure url()
references are resolved correctly when imported files are inlined.
{
"plugins": {
"postcss-import": true,
"postcss-url": true,
"postcss-custom-properties": true
}
}
@import "./config/index.css";
html {
background-color: var(--varColor);
}
.icon {
width: 50px;
height: 50px;
background-image: var(--varIcon);
}
:root {
--varColor: red;
--varIcon: url("../icon.svg");
}
In production mode, Parcel includes optimizations to reduce the file size of your code. See Production for more details about how this works.
In production mode, Parcel automatically minifies your code to reduce the file sizes of your bundles. By default, Parcel uses lightningcss to perform CSS minification.
Note: In prior versions, Parcel used cssnano for minification. If your project contains a cssnano config file such as .cssnanorc
or cssnano.config.json
, you may see a warning that it is no longer applied after upgrading Parcel.
In most cases, you can simply remove the cssnano config file and allow Parcel to handle minification. However, if you do rely on certain settings in this configuration and want to continue using cssnano instead of lightningcss
for minification, you can configure Parcel to use @parcel/optimizer-cssnano
instead.
{
"extends": "@parcel/config-default",
"optimizers": {
"*.css": ["@parcel/optimizer-cssnano"]
}
}