This project is currently private. If you want to follow the journey, sign up for my newsletter. You can also follow me on Twitter or star this project on GitHub.
An Atomic CSS-in-JS library with zero runtime overhead and CSS deduplication. Built by Austin Gil.
Before transpiling you pass CSS rules to the css()
function, which returns a string of classes. The prtcls/styles.css
file is empty:
// your JavaScript file
import { css } from 'prtcls'
const btnRed = css(`
padding: 10px;
background: red;
`)
const btnBlue = css(`
padding: 10px;
background: blue;
`)
/* prtcls/styles.css */
After transpiling, the library's footprint is removed and replaced with just the static string of classes. The rules are injected into prtcls/styles.css
without any duplication:
// your JavaScript file
const btnRed = 'p_10px bg_red'
const btnBlue = 'p_10px bg_blue'
/* prtcls/styles.css */
.p_10px {
padding: 10px;
}
.bg_red {
background: red;
}
.bg_blue {
background: blue;
}
The library uses plain old CSS which has a lot of benefits. Developers that already know CSS won't need to learn anything new. If you don't know CSS, you'll learn it, and that will be valuable whether you use this library or not. You can copy and paste CSS to and from any other project. And because it's CSS, you don't have to work within limited confines.
The library handles generating classes for you. That means you don't have to come up with the "best" class name for some div
, you don't have to adopt complicated naming conventions, and you don't need to remember a long list of classes. TypeScript support also provides autocomplete suggestions for CSS rules or available design tokens you've defined.
Runtime JavaScript is one of the biggest deteriorators to performance. This project lets you define your styles in JavaScript then replaces them static string of atomic CSS classes at build time. Your styles are added to a CSS file that only includes the styles you use, meaning there is no unused CSS in the final bundle, you don't depend on a purge step, and you can avoid flash of unstyled content.
By keeping styles with components, the maintenance process becomes much simpler. No more searching through a /css
directory looking for the files you need to modify. If you ever remove a component from a project, the unique CSS for that component will also be removed, reducing the final build size without manually removing styles.
With the configuration file you can provide design tokens that can be referenced in your CSS. These design tokens allow you to define variables or collections of styles to reuse or extend. For example, maybe you want to maintain a limited set of spacing options to choose from. Or maybe you want to define the default styles for buttons, then extend them for different variations.
Atomic classes let you to reuse the same styles throughout your application without duplicating CSS rules. As a result, your application can grow without increasing your CSS file size. Atomic styles also remove the need for scoped CSS because you can add or remove styles ad hoc. You can edit styles without worrying about breaking other parts of your application.
The library accepts any CSS as a JavaScript style object or a string. At build time the library gets removed and those styles get turned into a string of corresponding classes based on their media query, pseudo selector, property, and value. The styles for the generated classes get injected into your static CSS files.
Currently Particles CSS works as a Vite plugin. Please let me know if you'd like to see other tools supported.
Add the Vite plugin to your Vite config file (.vite.config.js
):
import Prtcls from 'prtcls/dist/vite-plugin-prtcls'
export default {
plugins: [
Prtcls()
]
}
If you want the benefits of TypeScript, using the Object or callback function returning an Object is the recommended approach.
const classList = css({
color: 'purple',
padding: '.5rem',
})
const classList = css(`
color: purple;
padding: .5rem;
`)
const classList = css((t) => {
return {
padding: '10px'
color: t.colors.primary,
}
})
const classList = css((t) => {
return `
padding: 10px;
color: ${t.colors.primary};
`
})
For more details on the callback function parameters, see the configuration section below.
You can target the following pseudoclasses and pseudoselectors with the syntax &:{variant}
:
- before
- after
- hover
- focus
- focus-within
- focus-visible
- active
- disabled
- visited
- checked
- first-child
- last-child
- required
- valid
- invalid
- readonly
const classList1 = css({
color: 'purple',
'&:hover': {
color: 'green',
}
})
const classList2 = css(`
color: purple;
&:hover {
color: green;
}
`)
To target media queries, nest the styles your element needs within the media query block. You can even nest variants within media queries:
const classList1 = css({
color: 'purple',
'@media only screen and (min-width: 576px) and (max-width: 768px)': {
color: 'green',
'&:hover': {
color: 'red',
},
},
})
const classList2 = css(`
color: purple;
@media only screen and (min-width: 576px) and (max-width: 768px): {
color: green;
&:hover {
color: red;
}
}
`)
Note that it's recommended to reference media queries as design tokens to maintain consistency.
To take advantage of the design tokens feature, you will need to create a configuration file. In your project root, add a file called .prtcls.config.js
. It should export an object with a tokens
property. Anything in this object will be available to the callback function syntax.
IMPORTANT: This file must use the CommonJS exports syntax
Assuming your config file looked something like this:
module.exports = {
tokens: {
size: {
8: '.5rem'
}
}
}
You could use a callback function like this:
const classList = css((t) => {
return {
padding: t.size[8],
}
})
The callback function uses TypeScript to provide intellisense for whatever you put in there.
- Framework agnostic.
- Supports pseudo-classes and pseudo-elements
- Supports media queries without duplication.
- Define reusable variables, design tokens, or components.
- Define styles with a JS Style Object, a string of CSS, or a callback function** that returns either.
- TypeScript support for config file.
- Runtime class generator.
- Nested selectors.
- Plugin system.
- Prefix support.
- Support for custom class name generators.
- CSS syntax highlighting plugin.
- Handling shorthand and longhand properties smartly.
** Callback functions are used to inject config options, but cannot depend on any runtime variables (yet).