Environment friendly Infinite Utility Helpers Utilizing Inline CSS Customized Properties and calc()

No Comments

I just lately wrote a really fundamental Sass loop that outputs a number of padding and margin utility courses. Nothing fancy, actually, only a Sass map with 11 spacing values, looped over to create courses for each padding and margin on either side. As we’ll see, this works, but it surely finally ends up a fairly hefty quantity of CSS. We’re going to refactor it to make use of CSS customized properties and make the system rather more trim.

Right here’s the unique Sass implementation:

$space-stops: (
‘0’: 0,
‘1’: 0.25rem,
‘2’: 0.5rem,
‘3’: 0.75rem,
‘4’: 1rem,
‘5’: 1.25rem,
‘6’: 1.5rem,
‘7’: 1.75rem,
‘8’: 2rem,
‘9’: 2.25rem,
’10’: 2.5rem,
);

@every $key, $val in $space-stops {
.p-#{$key} {
padding: #{$val} !essential;
}
.pt-#{$key} {
padding-top: #{$val} !essential;
}
.pr-#{$key} {
padding-right: #{$val} !essential;
}
.pb-#{$key} {
padding-bottom: #{$val} !essential;
}
.pl-#{$key} {
padding-left: #{$val} !essential;
}
.px-#{$key} {
padding-right: #{$val} !essential;
padding-left: #{$val} !essential;
}
.py-#{$key} {
padding-top: #{$val} !essential;
padding-bottom: #{$val} !essential;
}

.m-#{$key} {
margin: #{$val} !essential;
}
.mt-#{$key} {
margin-top: #{$val} !essential;
}
.mr-#{$key} {
margin-right: #{$val} !essential;
}
.mb-#{$key} {
margin-bottom: #{$val} !essential;
}
.ml-#{$key} {
margin-left: #{$val} !essential;
}
.mx-#{$key} {
margin-right: #{$val} !essential;
margin-left: #{$val} !essential;
}
.my-#{$key} {
margin-top: #{$val} !essential;
margin-bottom: #{$val} !essential;
}
}

This very a lot works. It outputs all of the utility courses we’d like. However, it may possibly additionally get bloated shortly. In my case, they had been about 8.6kb uncompressed and underneath 1kb compressed. (Brotli was 542 bytes, and gzip got here in at 925 bytes.)

Since they’re extraordinarily repetitive, they compress properly, however I nonetheless couldn’t shake the sensation that each one these courses had been overkill. Plus, I hadn’t even completed any small/medium/massive breakpoints that are pretty typical for these sorts of helper courses.

Right here’s a contrived instance of what the responsive model would possibly appear to be with small/medium/massive courses added. We’ll re-use the $space-stops map outlined beforehand and throw our repetitious code right into a mixin

@mixin finite-spacing-utils($bp: ”) {
@every $key, $val in $space-stops {
.p-#{$key}#{$bp} {
padding: #{$val} !essential;
}
.pt-#{$key}#{$bp} {
padding-top: #{$val} !essential;
}
.pr-#{$key}#{$bp} {
padding-right: #{$val} !essential;
}
.pb-#{$key}#{$bp} {
padding-bottom: #{$val} !essential;
}
.pl-#{$key}#{$bp} {
padding-left: #{$val} !essential;
}
.px-#{$key}#{$bp} {
padding-right: #{$val} !essential;
padding-left: #{$val} !essential;
}
.py-#{$key}#{$bp} {
padding-top: #{$val} !essential;
padding-bottom: #{$val} !essential;
}

.m-#{$key}#{$bp} {
margin: #{$val} !essential;
}
.mt-#{$key}#{$bp} {
margin-top: #{$val} !essential;
}
.mr-#{$key}#{$bp} {
margin-right: #{$val} !essential;
}
.mb-#{$key}#{$bp} {
margin-bottom: #{$val} !essential;
}
.ml-#{$key}#{$bp} {
margin-left: #{$val} !essential;
}
.mx-#{$key}#{$bp} {
margin-right: #{$val} !essential;
margin-left: #{$val} !essential;
}
.my-#{$key}#{$bp} {
margin-top: #{$val} !essential;
margin-bottom: #{$val} !essential;
}
}
}

@embrace finite-spacing-utils;

@media (min-width: 544px) {
@embrace finite-spacing-utils($bp: ‘_sm’);
}

@media (min-width: 768px) {
@embrace finite-spacing-utils($bp: ‘_md’);
}

@media (min-width: 1024px) {
@embrace finite-spacing-utils($bp: ‘_lg’);
}

That clocks in at about 41.7kb uncompressed (and about 1kb with Brotli, and 3kb with gzip). It nonetheless compresses properly, but it surely’s a bit ridiculous.

I knew it was doable to reference data-* attributes from inside CSS utilizing the [attr() function, so I wondered if it was possible to use calc() and attr() together to create dynamically-calculated spacing utility helpers via data-* attributes — like data-m=”1″ or data-m=”1@md” — then in the CSS to do something like margin: calc(attr(data-m) * 0.25rem) (assuming I’m using a spacing scale incrementing at 0.25rem intervals). That could be very powerful.

But the end of that story is: no, you (currently) can’t use attr() with any property except the content property. Bummer. But in searching for attr() and calc() information, I found this intriguing Stack Overflow comment by Simon Rigét that suggests setting a CSS variable directly within an inline style attribute. Aha!

So it’s possible to do something like <div style=”–p: 4;”> then, in CSS:

:root {
–p: 0;
}

[style*=’–p:’] {
padding: calc(0.25rem * var(–p)) !essential;
}

Within the case of the model=”–p: 4;” instance, you’d successfully find yourself with padding: 1rem !essential;.

… and now you may have an infinitely scalable spacing utility class monstrosity helper.

Right here’s what which may appear to be in CSS:

:root {
–p: 0;
–pt: 0;
–pr: 0;
–pb: 0;
–pl: 0;
–px: 0;
–py: 0;
–m: 0;
–mt: 0;
–mr: 0;
–mb: 0;
–ml: 0;
–mx: 0;
–my: 0;
}

[style*=’–p:’] {
padding: calc(0.25rem * var(–p)) !essential;
}
[style*=’–pt:’] {
padding-top: calc(0.25rem * var(–pt)) !essential;
}
[style*=’–pr:’] {
padding-right: calc(0.25rem * var(–pr)) !essential;
}
[style*=’–pb:’] {
padding-bottom: calc(0.25rem * var(–pb)) !essential;
}
[style*=’–pl:’] {
padding-left: calc(0.25rem * var(–pl)) !essential;
}
[style*=’–px:’] {
padding-right: calc(0.25rem * var(–px)) !essential;
padding-left: calc(0.25rem * var(–px)) !essential;
}
[style*=’–py:’] {
padding-top: calc(0.25rem * var(–py)) !essential;
padding-bottom: calc(0.25rem * var(–py)) !essential;
}

[style*=’–m:’] {
margin: calc(0.25rem * var(–m)) !essential;
}
[style*=’–mt:’] {
margin-top: calc(0.25rem * var(–mt)) !essential;
}
[style*=’–mr:’] {
margin-right: calc(0.25rem * var(–mr)) !essential;
}
[style*=’–mb:’] {
margin-bottom: calc(0.25rem * var(–mb)) !essential;
}
[style*=’–ml:’] {
margin-left: calc(0.25rem * var(–ml)) !essential;
}
[style*=’–mx:’] {
margin-right: calc(0.25rem * var(–mx)) !essential;
margin-left: calc(0.25rem * var(–mx)) !essential;
}
[style*=’–my:’] {
margin-top: calc(0.25rem * var(–my)) !essential;
margin-bottom: calc(0.25rem * var(–my)) !essential;
}

It is a lot like the primary Sass loop above, however there’s no loop going 11 occasions — and but it’s infinite. It’s about 1.4kb uncompressed, 226 bytes with Brotli, or 284 bytes gzipped.

In case you wished to increase this for breakpoints, the unlucky information is which you can’t put the “@” character in CSS variable names (though emojis and different UTF-8 characters are unusually permitted). So you can most likely arrange variable names like p_sm or sm_p. You’d have so as to add some further CSS variables and a few media queries to deal with all this, but it surely received’t blow up exponentially the way in which conventional CSS classnames created with a Sass for-loop do.

Right here’s the equal responsive model. We’ll use a Sass mixin once more to chop down the repetition:

:root {
–p: 0;
–pt: 0;
–pr: 0;
–pb: 0;
–pl: 0;
–px: 0;
–py: 0;
–m: 0;
–mt: 0;
–mr: 0;
–mb: 0;
–ml: 0;
–mx: 0;
–my: 0;
}

@mixin infinite-spacing-utils($bp: ”) {
[style*=’–p#{$bp}:’] {
padding: calc(0.25rem * var(–p)) !essential;
}
[style*=’–pt#{$bp}:’] {
padding-top: calc(0.25rem * var(–pt)) !essential;
}
[style*=’–pr#{$bp}:’] {
padding-right: calc(0.25rem * var(–pr)) !essential;
}
[style*=’–pb#{$bp}:’] {
padding-bottom: calc(0.25rem * var(–pb)) !essential;
}
[style*=’–pl#{$bp}:’] {
padding-left: calc(0.25rem * var(–pl)) !essential;
}
[style*=’–px#{$bp}:’] {
padding-right: calc(0.25rem * var(–px)) !essential;
padding-left: calc(0.25rem * var(–px)) !essential;
}
[style*=’–py#{$bp}:’] {
padding-top: calc(0.25rem * var(–py)) !essential;
padding-bottom: calc(0.25rem * var(–py)) !essential;
}
[style*=’–m#{$bp}:’] {
margin: calc(0.25rem * var(–m)) !essential;
}
[style*=’–mt#{$bp}:’] {
margin-top: calc(0.25rem * var(–mt)) !essential;
}
[style*=’–mr#{$bp}:’] {
margin-right: calc(0.25rem * var(–mr)) !essential;
}
[style*=’–mb#{$bp}:’] {
margin-bottom: calc(0.25rem * var(–mb)) !essential;
}
[style*=’–ml#{$bp}:’] {
margin-left: calc(0.25rem * var(–ml)) !essential;
}
[style*=’–mx#{$bp}:’] {
margin-right: calc(0.25rem * var(–mx)) !essential;
margin-left: calc(0.25rem * var(–mx)) !essential;
}
[style*=’–my#{$bp}:’] {
margin-top: calc(0.25rem * var(–my)) !essential;
margin-bottom: calc(0.25rem * var(–my)) !essential;
}
}

@embrace infinite-spacing-utils;

@media (min-width: 544px) {
@embrace infinite-spacing-utils($bp: ‘_sm’);
}

@media (min-width: 768px) {
@embrace infinite-spacing-utils($bp: ‘_md’);
}

@media (min-width: 1024px) {
@embrace infinite-spacing-utils($bp: ‘_lg’);
}

That’s about 6.1kb uncompressed, 428 bytes with Brotli, and 563 with gzip.

Do I feel that writing HTML like <div model=”–px:2; –my:4;”> is agreeable to the attention, or good developer ergonomics… no, not significantly. However may this strategy be viable in conditions the place you (for some cause) want extraordinarily minimal CSS, or maybe no exterior CSS file in any respect? Sure, I certain do.

It’s value declaring right here that CSS variables assigned in inline kinds don’t leak out. They’re scoped solely to the present aspect and don’t change the worth of the variable globally. Thank goodness! The one oddity I’ve discovered up to now is that DevTools (no less than in Chrome, Firefox, and Safari) don’t report the kinds utilizing this method within the “Computed” kinds tab.

Additionally value mentioning is that I’ve used good previous padding  and margin properties with -top, -right, -bottom, and -left, however you can use the equal logical properties like padding-block and padding-inline. It’s even doable to shave off just some extra bytes by selectively mixing and matching logical properties with conventional properties. I managed to get it right down to 400 bytes with Brotli and 521 with gzip this fashion.

Different use circumstances

This appears most applicable for issues which might be on a (linear) incremental scale (which is why padding and margin looks like an excellent use case) however I may see this probably working for widths and heights in grid techniques (column numbers and/or widths). Perhaps for typographic scales (however perhaps not).

I’ve centered so much on file measurement, however there could also be another makes use of right here I’m not pondering of. Maybe you wouldn’t write your code on this method, however a important CSS software may probably refactor the code to make use of this strategy.

Digging deeper

As I dug deeper, I discovered that Ahmad Shadeed blogged in 2019 about mixing calc() with CSS variable assignments inside inline kinds significantly for avatar sizes. Miriam Suzanne’s article on Smashing Journal in 2019 didn’t use calc() however shared some wonderful issues you are able to do with variable assignments in inline kinds.

The put up Environment friendly Infinite Utility Helpers Utilizing Inline CSS Customized Properties and calc() appeared first on CSS-Methods. You’ll be able to assist CSS-Methods by being an MVP Supporter.

    About Marketing Solution Australia

    We are a digital marketing company with a focus on helping our customers achieve great results across several key areas.

    Request a free quote

    We offer professional SEO services that help websites increase their organic search score drastically in order to compete for the highest rankings even when it comes to highly competitive keywords.

    Subscribe to our newsletter!

    More from our blog

    See all posts

    Leave a Comment