Skip to Content
  • Website
Codsen
  • Home
  • Open Source
  • Articles
  • About

prevOpen Source→generate-atomic-cssnext

generate-atomic-css3.2.0

Generate Atomic CSS

Downloads per monthChangelogMIT Licenselibera manifesto
  • the top
  • Installation
  • Quick Take
  • Examples
  • PURPOSE
  • API — GENATOMIC()
  • API — DEFAULTS
  • API — VERSION
  • API — HEADSANDTAIL…
  • API — EXTRACTFROMT…
  • IDEA
  • CONFIG
  • Changelog

Installation

Quick Take

Examples

  • Generate from a separate configuration string
  • Wrap generated content in marker comments
  • Extract a configured generation range
  • Retain the generator configuration
  • Access canonical marker names
  • Leave CSS without generator placeholders unchanged
  • Align generated declarations
  • Observe generation progress in a custom interval

Purpose

When you code responsive email templates (without any frameworks) you need to apply some CSS to some HTML tag. That’s two locations:

  1. create a new style in <head> styles;
  2. add that class onto an HTML tag.

What if you could skip the first step?

  1. generate all the possible CSS styles, one per style, for example, .mt1 { margin-top: 1px; } .mt2 { margin-top: 2px; },
  2. inject those into HTML
  3. finally, in automated build step, remove all unused atomic CSS classes

In such case, email template CSS styling would be reduced to memorising the class names and applying them onto HTML tags (second step only).

This program generates a string which comprises of atomic CSS.

API — genAtomic()

The main function genAtomic() is imported like this:

It’s a function which takes two input arguments:

Input argumentTypeObligatoryDescription
str
Type: String
Obligatory: yes
strStringyesExisting atomic CSS string
opts
Type: Plain object
Obligatory: no
optsPlain objectnoOptional Options Object

The optional options object has the following shape:

It’s a plain object which goes into second input argument of the main function, genAtomic(). Here are all the keys and their values:

KeyTypeDefaultDescription
includeConfig
Type: boolean
Default: true
includeConfigbooleantrueShould config be repeated, wrapped with GENERATE-ATOMIC-CSS-CONFIG-STARTS and GENERATE-ATOMIC-CSS-CONFIG-ENDS? Enabling this enables includeHeadsAndTails as well (if not enabled already).
includeHeadsAndTails
Type: boolean
Default: true
includeHeadsAndTailsbooleantrueShould the generated CSS be wrapped with GENERATE-ATOMIC-CSS-CONFIG-STARTS and GENERATE-ATOMIC-CSS-CONFIG-ENDS?
pad
Type: boolean
Default: true
padbooleantrueShould the numbers be padded
configOverride
Type: null (off) or string
Default: null
configOverridenull (off) or stringnullThis is override, you can hard-set the config from outside. Handy when input contains old/wrong config.
reportProgressFunc
Type: function or null
Default: null
reportProgressFuncfunction or nullnullHandy in worker setups, if you provide a function, it will be called for each percentage done from reportProgressFuncFrom to reportProgressFuncTo, then finally, with the result.
reportProgressFuncFrom
Type: natural number
Default: 0
reportProgressFuncFromnatural number0reportProgressFunc() will ping unique percentage progress once per each percent, from 0 to 100 (%). You can skew the starting percentage so counting starts not from zero but from this.
reportProgressFuncTo
Type: natural number
Default: 100
reportProgressFuncTonatural number100reportProgressFunc() will ping unique percentage progress once per each percent, from 0 to 100 (%). You can skew the starting percentage so counting starts not from zero but from this.

Here are all defaults in one place for copying:

The function returns a plain object (marked as type Res):

API — defaults

You can import defaults:

It's a plain object:

The main function calculates the options to be used by merging the options you passed with these defaults.

API — version

You can import version:

API — headsAndTails

It’s a plain object, its main purpose is to serve as a single source of truth for heads and tails names:

{
  CONFIGHEAD: "GENERATE-ATOMIC-CSS-CONFIG-STARTS",
  CONFIGTAIL: "GENERATE-ATOMIC-CSS-CONFIG-ENDS",
  CONTENTHEAD: "GENERATE-ATOMIC-CSS-CONTENT-STARTS",
  CONTENTTAIL: "GENERATE-ATOMIC-CSS-CONTENT-ENDS"
}

For example,

import { genAtomic, version, headsAndTails, extractFromToSource } from "generate-atomic-css";
console.log(`headsAndTails.CONTENTTAIL = ${headsAndTails.CONTENTTAIL}`);
// => headsAndTails.CONTENTTAIL = GENERATE-ATOMIC-CSS-CONTENT-ENDS

API — extractFromToSource()

It’s an internal function which reads the source line, for example:

.pb$$$ { padding-bottom: $$$px !important; } | 5 | 10

and separates “from” (5 above) and “to” (10 above) values from the rest of the string (.pb$$$ { padding-bottom: $$$px !important; }).

The challenging part is that pipes can be wrapping the line from outside, plus, if there is only one number at the end of the line, it is “to” value.

| .mt$$$ { margin-top: $$$px !important; } | 1 |

Here’s an example how to use extractFromToSource():

import { genAtomic, version, headsAndTails, extractFromToSource } from "generate-atomic-css";
const input1 = `.pb$$$ { padding-bottom: $$$px !important; } | 5 | 10`;
const input2 = `.mt$$$ { margin-top: $$$px !important; } | 1`;

// second and third input argument are default "from" and default "to" values:
const [from1, to1, source1] = extractFromToSource(input1, 0, 500);
console.log(`from = ${from1}`);
// from = 5
console.log(`to = ${to1}`);
// from = 10
console.log(`source = "${source1}"`);
// source = ".pb$$$ { padding-bottom: $$$px !important; }"

const [from2, to2, source2] = extractFromToSource(input2, 0, 100);
console.log(`from = ${from2}`);
// from = 0 <--- default
console.log(`to = ${to2}`);
// from = 1 <--- comes from pipe, "} | 1`;"
console.log(`source = "${source2}"`);
// source = ".mt$$$ { margin-top: $$$px !important; }"

Idea

On a basic level, you can turn off heads/tails (set opts.includeHeadsAndTails to false) and config (set opts.includeConfig to false).

Each line which contains $$$ will be repeated, from default 0 to 500 or within the range you set:

.pb$$$ { padding-bottom: $$$px !important; } | 5 | 10

Above instruction means generate from 5 to 10, inclusive:

.pb5 {
  padding-bottom: 5px !important;
}
.pb6 {
  padding-bottom: 6px !important;
}
.pb7 {
  padding-bottom: 7px !important;
}
.pb8 {
  padding-bottom: 8px !important;
}
.pb9 {
  padding-bottom: 9px !important;
}
.pb10 {
  padding-bottom: 10px !important;
}

If you’re happy to start from zero, you can put only one argument, “to” value:

.w$$$p { width: $$$% !important; } | 100

Above instruction means generate from (default) 0 to (custom) 100, inclusive:

/* GENERATE-ATOMIC-CSS-CONTENT-STARTS */
.w0p {
  width: 0 !important;
}
.w1p {
  width: 1% !important;
}
.w2p {
  width: 2% !important;
}
.... .w98p {
  width: 98% !important;
}
.w99p {
  width: 99% !important;
}
.w100p {
  width: 100% !important;
}

Config

What happens if you want to edit the generated list, to change ranges, to add or remove rules?

You need to recreate the original “recipe”, lines .pb$$$ { padding-bottom: $$$px !important; } and so on.

Here’s where the config comes to help.

Observe:

/* GENERATE-ATOMIC-CSS-CONFIG-STARTS
.pb$$$ { padding-bottom: $$$px !important; } | 5 | 10

.mt$$$ { margin-top: $$$px !important; } | 1
GENERATE-ATOMIC-CSS-CONFIG-ENDS
GENERATE-ATOMIC-CSS-CONTENT-STARTS */
.pb5 {
  padding-bottom: 5px !important;
}
.pb6 {
  padding-bottom: 6px !important;
}
.pb7 {
  padding-bottom: 7px !important;
}
.pb8 {
  padding-bottom: 8px !important;
}
.pb9 {
  padding-bottom: 9px !important;
}
.pb10 {
  padding-bottom: 10px !important;
}

.mt0 {
  margin-top: 0 !important;
}
.mt1 {
  margin-top: 1px !important;
}
/* GENERATE-ATOMIC-CSS-CONTENT-ENDS */

If opts.includeConfig setting is on (it’s on by default), your original config will be placed on top of generated content.

Furthermore, if generator detects content heads and tails placeholders, it will wipe existing contents there, replacing them with newly generated CSS.

The idea is you should be able to keep your config in your master email template, only remove config like regular CSS comment when deploying to production. But you’d still keep the master template with config. Later you could reuse it.

Changelog

Open Changelog
↑ back to top
prev next

Copyright

All rights reserved © Roy Revelt 2026
All our open source packages are under MIT licenceopens in a new tab

Activities

🐛 See a bug? Raise an issueopens in a new tab
💘 Check out the Indiewebopens in a new tab and Libera manifestoopens in a new tab