This is a work in progress. I'm writing it in public and updating it as I continue writing.
I've been wanting to develop my boilerplate for a long while now. I'm at a point where I keep reinventing and trying to figure out the things and would rather work from a boilerplate to decrease the amount of decisions I make each project onset.
Foundations
Naming is one of the most important things of a design system. It needs to be consistent, but flexible; declarative, but not prescriptive.
These days, primitives is a relatively standard nomenclature for this group of tokens. I, personally, don't like that word to define them. There are too many definitions that can create ambiguity or mis-frame their import.
Collectively, I like to call it the foundation. These tokens are what everything's built on—adding meaning as compositions emerge by adding style to semantic structure. These also need to be the smallest, simplest variable names for ease of re-use and development (when getting much farther along the system creation, token names tend to get lengthy). I'm one for typographpical poetry of length, so if the foundations are the smallest units with the shortest names, they're quickly identifiable, understandable, and rememberable.
Constants
These are ones that I'll always use, but are singular in words:
--paperfor white, and developing tints;--inkfor black, and developing shades;--ashfor gray, and developing tones.
Prefixes
A prefix is something at the beginning. In grammar, these are remixable, contextual preambles like re-, con-, or pre-.
These are the first words of my boilerplate's token set:
--font-forfont-family;--scale-forfont-size;--stretchforfont-width;--style-forfont-style;--weight-forfont-weight;--tracking-forletter-spacing;--stroke-forborder-width;--leading-forline-height;--opacity-for...well, opacity values;--palette-for base colors;--duration-for transition and animation timings;--radii-forborder-radius(and the fun of using two i's in a row);--space-forpadding,margin, andgap;--measure-formax-widthand typographic line-length;--layer-forz-index;--ratio-foraspect-ratio;--prominence-forbox-shadow;
Values & Gradients
For some of these, I use explicit values. For others, semantic gradients.
It depends on how the token's purpose and relationship with it's property counterpart.
Typography
font
Each of these are the typical ones I come across in most settings.
They're also grounded in generic font family naming, just a limited set. In total, there're 12 potential generic font families to use, but these four tend to cover most of my use cases (and identity systems).
:root{
--font-sans: $stack, sans;
--font-serif: $stack, serif;
--font-mono: $stack, mono;
--font-hand: $stack, cursive;
weight
Most of my design decisions boil down to typographic history and practical use.
These are, generally, named for their typical weight naming scheme, respectively mapped to their default weights. Because variable fonts provide a plethora of potential values, having these as tokens rather than defaults enables depth of polish.
:root{
--weight-thin: 100;
--weight-extralight: 200;
--weight-light: 300;
--weight-regular: 400;
--weight-medium: 500;
--weight-semibold: 600;
--weight-bold: 700;
--weight-extrabold: 800;
--weight-black: 900;
stretch
Like weight, this has default values but can be set differently in variable font situations.
These map to another typical font design for typeface widths, developed over the centuries to give semantic name to width.
:root{
--stretch-ultra-con: ultra-condensed; /* 50% width */
--stretch-extra-con: extra-condensed; /* 62.5% width */
--stretch-con: condensed; /* 75% width */
--stretch-semi-con: semi-condensed; /* 87.5% width */
--stretch-reg: normal; /* 100% width */
--stretch-semi-exp: semi-expanded; /* 112.5% width */
--stretch-exp: expanded; /* 125% width */
--stretch-extra-exp: extra-expanded; /* 150% width */
--stretch-ultra-exp: ultra-expanded; /* 200% width */
measure
Measure is the fancy typography word for length of a line of text, specifically from the left to right edges of a text block. In terms of reading length, it's good to aim for 45–85 characters per line, inclusive of spaces.
The primary aspects that influence measure are: size of the type and size of its container (be it block, like a section, or a unit, like a paragraph).
:root{
--measure-xxs: 25ch;
--measure-xs: 35ch;
--measure-sm: 45ch ;
--measure-md:55ch ;
--measure-lg: 65ch;
--measure-xl: 75ch ;
--measure-xxl: 85ch;
scale
--scale-forfont-size;
style
--style-forfont-style;
tracking
--tracking-forletter-spacing;
leading
--leading-forline-height;
Color
opacity
--opacity-for...well, opacity values;palette
--palette-for base colors;duration
--duration-for transition and animation timings;
Block
radii
--radii-forborder-radius(and the fun of using two i's in a row);ratio
--ratio-foraspect-ratio;stroke
--stroke-forborder-width;space
--space-forpadding,margin, andgap;prominence
--prominence-forbox-shadowlayer
--layer-forz-index;
Semantic Gradients
I've written about semantic gradients before, making lists, and compiling them. They're these cool relational word ladders that transition from one end of a spectrum to another.
I prefer these over numbering systems—100,200,300,400,... or 10,20,30,40,...—because words. I find the lack of explicit and definite number scale provides room for play, interpretation, but also ways of individualizing the scale to act as a trap street—a maker's mark to detect copyright violations (and, as is the case these days, detecting content was used as fodder for scrapers and large language model training).
Each of the prefixes, above, come with default semantics—gradient or otherwise. I'm thematically grouping them for ease of reading.