Installation
Quick Take
Purpose
When you code responsive email templates (without any frameworks) you need to apply some CSS to some HTML tag. That’s two locations:
- create a new style in
<head>
styles; - add that class onto an HTML tag.
What if we could skip the first step?
- generate all the possible CSS styles, one per style, for example,
.mt1 { margin-top: 1px; } .mt2 { margin-top: 2px; }
, - inject those into HTML
- 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 argument | Type | Obligatory | Description |
---|---|---|---|
str Type: String Obligatory: yes | |||
str | String | yes | Existing atomic CSS string |
opts Type: Plain object Obligatory: no | |||
opts | Plain object | no | Optional 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:
Key | Type | Default | Description |
---|---|---|---|
includeConfig Type: boolean Default: true | |||
includeConfig | boolean | true | Should config be repeated, wraped 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 | |||
includeHeadsAndTails | boolean | true | Should the generated CSS be wrapped with GENERATE-ATOMIC-CSS-CONFIG-STARTS and GENERATE-ATOMIC-CSS-CONFIG-ENDS ? |
pad Type: boolean Default: true | |||
pad | boolean | true | Should the numbers be padded |
configOverride Type: null (off) or string Default: null | |||
configOverride | null (off) or string | null | This is override, you can hard-set the config from outside. Handy when input contains old/wrong config. |
reportProgressFunc Type: function or null Default: null | |||
reportProgressFunc | function or null | null | Handy 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 | |||
reportProgressFuncFrom | natural number | 0 | reportProgressFunc() 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 | |||
reportProgressFuncTo | natural number | 100 | reportProgressFunc() 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.