Automatically generate fluid CSS variables, using CSS Clamp.
Inspired many years ago by Fluid Typography and re-inspired by Simplified Fluid Typography. This combines the two, giving you the precise scaling control of the former method, with the more concise format of latter. All wrapped up in an easy to type variable, expanded for you in postcss.
We want our font size to be 40px at or below a screen width of 800px. Between 800 and 1600px we want it to seamlessly scale up to 60px, where it tops out and scales no further.
:root {
--design-min: 800;
--design-max: 1600;
}
h1 {
font-size: var(--40-60);
}
👆 Generates 👇
:root {
--design-min: 800;
--design-max: 1600;
--fv-calc: (100vw - var(--design-min) * 1px) / (var(--design-max) - var(--design-min));
--40-60: clamp(40px, calc(40px + 20 * var(--fv-calc)), 60px);
}
h1 {
font-size: var(--40-60);
}
Note no extra media queries, no changing the source declaration, just a CSS variable defined alongside the design specs.
Rather than being configured in Javascript, this plugin relies on the existence of the --design-min
and --design-max
CSS variables supplied in your stylesheet. This allows you to get fancier with nesting, and keeps the stylesheet self-defined.
Due to how they're used in the CSS calc, these variables must not have a unit attached. If you forget and put 800px
instead of 800
, a postcss warning will be logged (...and it won't work).
CSS Variables are only defined once, so var(--10-100)
in the above example can be used across the stylesheet with no extra code penalty.
By default px
is assumed, allowing you to drop the unit. Other units can be specified.
h1 {
/* 4px -> 6px */
line-height: var(--4-6);
/* 4em -> 6em */
font-size: var(--4-6em);
}
Negative values are available, either with another hyphen or an n
for readability.
p {
/* -20px -> -40px; */
margin-top: var(--n20-n40); /* Equivalent */
margin-top: var(---20--40); /* Equivalent */
}
div {
/* 10rem -> -10rem */
left: var(--10-n10rem);
}
Decimals are available using p
, but the syntax hampers things making them sufficiently ugly that I'd avoid them where possible.
h1 {
/* 4.5rem -> 6.3rem */
font-size: var(--4p5-6p3rem);
}
Since they're pure CSS custom properties, this is perfectly fine.
div {
margin: var(--10-20) 100px var(--20-40);
}
CSS variables will be added wherever a design min or max variable is declared. This means you can override design specs for certain pages/areas.
:root {
--design-min: 800;
--design-max: 1600;
}
.layout {
--design-min: 1200;
}
h1 {
font-size: var(--40-60);
}
👆 Generates 👇
:root {
--design-min: 800;
--design-max: 1600;
--fv-calc: (100vw - var(--design-min) * 1px) / (var(--design-max) - var(--design-min));
--40-60: clamp(40px, calc(40px + 20 * var(--fv-calc)), 60px);
}
.layout {
--design-min: 1200;
--fv-calc: (100vw - var(--design-min) * 1px) / (var(--design-max) - var(--design-min));
--40-60: clamp(40px, calc(40px + 20 * var(--fv-calc)), 60px);
}
h1 {
font-size: var(--40-60);
}
Do note that all variables will be injected at every spot where you're changing the design specs. Use this sparingly and make use of the cascade — if you're defining the same design min/max in more than one place, define it on a specific classname and use it in both places. For example:
:root {
--design-min: 800;
--design-max: 1600;
}
.christmas {
--design-min: 768;
--design-max: 1200;
}
<section class="christmas"> <!-- 1200px design herein --> </section>
<section> <!-- 1600px design herein --> </section>
<section class="christmas"> <!-- 1200px design herein --> </section>
If you really must use bespoke values throughout your CSS, you can define custom min/max values in the variable itself.
For example, to use a design-min of 400px and a design-max of 800px:
.item {
font-size: var(--10at400-20at800);
}
This won't create a new injection point and will create a one-off variable for that spot, which helps tackle any code bloat issue. Try to avoid too many of these lest you sacrifice readability and design consistency.
The rule --n10p5at768-n20p45emat1366
is technically valid, but exceedingly hard to get your head around.
Classic use:
const fluidvars = require('postcss-fluidvars');
postcss([
fluidvars(options)
]);
Or use your bundler loader of choice.
To avoid clashing with any existing variables, you can provide a namespace. This also helps fix an issue where older versions of Sass perform math on the values — for some reason it won't mess with it when it has a namespace.
const fluidvars = require('postcss-fluidvars');
postcss([
fluidvars({
namespace: 'bees'
})
]);
:root {
--bees-design-min: 800;
--bees-design-max: 1600;
}
h1 {
font-size: var(--bees-40-60);
}