DEV Community

Cover image for Future of CSS: Functions and Mixins
Andrew Bone
Andrew Bone

Posted on

Future of CSS: Functions and Mixins

Today we're going to look at something that hasn't yet been incorporated into the official CSS specification but could change the way we write CSS. I am, of course, talking about Functions and Mixins.

Here's the TL;DR, CSS Functions and Mixin add a way to capture and reuse CSS logic without the use of pre-processors. This allows DRY principles to be executed in CSS without the need of utility classes.

Let's dive in and see how we got here and what these changes may mean in practice.

CSS pre-processors

Traditionally CSS lacked features such as variables, nesting, mixins, and functions. This was frustrating for Developers as it often led to CSS quickly becoming complex and cumbersome. In an attempt to make code easier and less repetitive CSS pre-processors were born. You would write CSS in the format the pre-processor understood and, at build time, you'd have some nice CSS. The most common pre-processors these days are Sass, Less, and Stylus. Any examples I give going forward will be about Sass as that's what I'm most familiar with.

Sass logo

Pre-processors were not without their problems though; for one, the browser can't understand them, you need a build/compile step before you can see what they're doing. For another, each pre-processor has a slightly different syntax for devs to learn and master.

Perhaps as an attempt to remedy this or perhaps just a way to improve vanilla DX (Developer eXperience) the CSS spec began to incorporate aspects of Pre-processors with variables and more recently nesting.

Sass @function

Functions allow you to define complex operations on SassScript values that you can re-use throughout your stylesheet. They make it easy to abstract out common formulas and behaviours in a readable way.

Functions in Sass allows a developer to calculate a single value. This will be calculated at build time so can not include vanilla CSS Variables.

Sass @mixin

Mixins allow you to define styles that can be re-used throughout your stylesheet. They make it easy to avoid using non-semantic classes like .float-left, and to distribute collections of styles in libraries.

Mixins in Sass allows a developer to @include a whole block of css (property and value pairs). It also allows you to pass in arguments that can modify the block. Though, again as this is calculated at build time vanilla CSS Variables aren't available.

Examples and use cases

Let's suppose we have a design system where we have buttons that can be different colours, each button should look the same apart from the different background colour, the hover and active states are slightly darker variants of their base colour.

First let's make a function that will mix any colour with black at a set percentage.

@function --darken(--base, --percent) {
  @return color-mix(in srgb, var(--base), #000 var(--percent));
}
Enter fullscreen mode Exit fullscreen mode

Now that we have a function that can darken our base colours let's make a mixin that will make our default button style with the hover and active states, we can use nesting in the mixin to make this easier.

You'll notice the --color argument has a default colour which will be used if one isn't supplied.

@mixin --button(--color: #174D7C) {
  background-color: var(--color);
  color: white;
  border: none;
  padding: 0.5rem 1rem;
  border-radius: 4px;
  font-size: 1rem;
  cursor: pointer;

  &:hover {
    background-color: --darken(var(--color), 5%);
  }

  &:active {
    background-color: --darken(var(--color), 10%);
  }
}
Enter fullscreen mode Exit fullscreen mode

Finally let's make three buttons one using the default colour then a green and also an orange one.

.primary-btn {
  @apply --button
}

.green-btn {
  @apply --button(#6F9F9C)
}

.orange-btn {
  @apply --button(#FE5F55)
}
Enter fullscreen mode Exit fullscreen mode

Three buttons matching the colour themes

And just like that we have 3 beautiful buttons with the bulk of our CSS only being written once and everything being calculated at run time. Meaning we get full reign of native CSS variables.

Fin

Just when we think the faucet of CSS changes has been shut off something huge like this comes our way. Whilst it will take a while to be finalised into spec and then a while more roll out to browsers the possibilities are simply mind boggling.

Thanks so much for reading. If you'd like to connect with me outside of Dev here are my twitter and linkedin come say hi ๐Ÿ˜Š.

Top comments (20)

Collapse
 
thumbone profile image
Bernd Wechner

Can't wait to see the end of preprocessors.

Collapse
 
link2twenty profile image
Andrew Bone

As CSS gets more and more features pre-processors seem less and less necessary, I imagine they'll hang around for a few years yet, even if they're no longer required.

Collapse
 
link2twenty profile image
Andrew Bone

@afif I can already picture the day you start sharing your code, like you amazing triangles in this format ๐Ÿ˜…

@function --gradient(--radius: 0px) {
  --gradient-part: calc(tan(60deg)*var(--radius)) top var(--radius), #000 98%, #0000 101%;

  @return 
    conic-gradient(from 150deg at 50% calc(3*var(--radius)/2 - 100%), #000 60deg, #0000 0) 0 0/100% calc(100% - 3*var(--radius)/2) no-repeat,
    radial-gradient(var(--radius) at 50% calc(100% - 2*var(--radius)), #000 98%, #0000 101%),
    radial-gradient(var(--radius) at left var(--gradient-part)),
    radial-gradient(var(--radius) at right var(--gradient-part));
}

@mixin --triangle(--radius: 0px, --width: 100px) {
  width: var(--width);
  aspect-ratio: 1/cos(30deg);
  -webkit-mask: --gradient(var(--radius));
  clip-path: polygon(50% 100%, 100% 0, 0 0);
  background: linear-gradient(45deg, #FA6900, #C02942);
}

.triangle {
  @apply --triangle(20px, 180px)
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
afif profile image
Temani Afif

Not only for a specific shape but it would be a function that can return most of the shapes.

Something like @mixin --triangle(--type: 1,--radius: 0px, --size: 100px) where type will define the nature of the triangle ๐Ÿ˜‰

Collapse
 
link2twenty profile image
Andrew Bone

Honestly I'm excited to see how far you can take it ๐Ÿ˜ฒ

Collapse
 
cscarlson profile image
cScarlson

You know that trick where you overlay a checkbox or radio button over a button's display so that the user interacts with the input instead of the button?... And how I have to recreate them all the time or use a JS component just to have one that's reusable?... Yeah, can't wait for mixins... This is going to be awesome!!!

Collapse
 
chovy profile image
chovy

we'll be using this asap at Profullstack and Primatejs

Collapse
 
link2twenty profile image
Andrew Bone

Awesome! Now that all the browsers are evergreen adopting new features like this is so convenient.

Collapse
 
link2twenty profile image
Andrew Bone

Can you think of any more powerful examples of how to use CSS functions?

Collapse
 
efpage profile image
Eckehard • Edited

Rerferring to this source, W3Schools listed 228 CSS properties in 2020, some people counted 584, but the trend seems to be further upwards.

Adding functions and mixins will make CSS more flexible, but seems to establish a second programming language in the browser. And you probably will still need some javascript too, as not all transitions refer to styling. Think of a content, that needs to change in sync with the styles.

Iยดm not sure we should be too happy about this trend. Javascript has already more than enough (some people say: too many) features. Often you have far more than one way to do the same thing, which can be pretty confusing. Now we get more options to do the same things in CSS, that could been done in JS before. This blows up the whole balloon again...

When will it burst?

Image description

Collapse
 
link2twenty profile image
Andrew Bone

A valid concern, where CSS has JS beat is with optimisations. Browser makers can take the strain of doing calculations off the developers hands and allow the calculations to be done off main thread.

This can, of course, be abused and used wrong but less JS is always a good thing in my mind.

Collapse
 
efpage profile image
Eckehard

So we add functions and mixins to CSS to avoid Javascript? This is a strange logic. Balancing a tower of different and mostly incompatible tools certainly doesn't make things any easier.

Thread Thread
 
afif profile image
Temani Afif

Why don't you see the problem differently like JavaScript developers using JS to emulate native CSS feature? This is not something new, it's like that since years. I cannot count the number of JS library used to animate stuff where a simple CSS keyframe will do the job.

We don't want to avoid JS but we want JS developer to not avoid CSS and consider all the native feature that are already implemented instead of reinventing the wheel with a complex JS code.

As side note, you don't need to know all the 584 CSS properties. No one need to know them. The fact that CSS is adding new features doesn't oblige you to use them.

Thread Thread
 
efpage profile image
Eckehard • Edited

Inventing a new language for every sentence you want to speak is not a good Idea...

We see an inflation of new tools, each providing a "new syntax" as if there had been no other programming language โ€‹โ€‹before (See my post about the different ways to write loops). And often, this "new" approaches are created with a limited target in mind and a fairly limited syntax, so they limit what you can do.

Languages like C++, Pascal or Javascript are "general purpose" languages, they give you a small set of rules with which you can formulate all kinds of tasks. And they have a consistent syntax which is kind of battle tested.

We have seen this so many times, an all the time it is claimed to make things "easier" for developers. Angular uses JSX, which is more or less simply HTML syntax, so, there is no option to write loops. So, Angular introduced a new command: ng-for

<ul>
    <li *ngFor="let item of stuff">
      {{item.title}}
    </li>
</ul>
Enter fullscreen mode Exit fullscreen mode

The syntax is driven by what HTML provides, but it is far from "consistent". It is more a "dirty hack". This is not HTML, it is not Javascript, it is not a programming language at all. It is not even a template literal, which would be the common form to include variables into JSX. It is absolutely no wonder that you find thousands of pages just explaining how to use ng-for. I donยดt think this is "easy" in any way.

And what does ng-for provide? You just can loop through simple flat arrays, nothing else. What if you want to display every second element, reverse the order, your array is nested or whatever may happen to you? Come on, let introduce some new properties to handle all this special cases?

It is true: Nobody needs 584 CSS properties. But If I have a special task, I need to find the right property. Nobody can remember 584 CSS properties, so it will take some time to find the right one. What if there are 5.840 CSS properties or 58.400?

There is an infinite number of things people may want to do on a website. If we create a new property for every possible situation, we will need an infinite number of properties. I would prefer a "general purpose" language to do this job. And as browsers already understand Javascript, why use something else?

Thread Thread
 
latobibor profile image
Andrรกs Tรณth

I partially agree with you. On ngFor and Angular absolutely. React version of JSX made a lot of sense instead, it is just better design. Same thing is true with yaml and helm: horrible new, mini languages, instead of supporting a couple of solid languages and passing your own JS object for example. You know what nobody considers mini programming languages? God damned command line interfaces, where there's an absolute chaos on if you just write -auth or -a or simply auth? Is -f file or is it force?

So I get why exactly you are angry about it and why you are defensive.

However, however! Mixins are amazing for composing reusable blocks of CSS. You can reduce the bundle one needs to download and also you can make things DRY by defining it once.

I personally use mixins to describe the things that are not obvious in CSS.

But where I disagree:

  • unlike my example with yaml kubernetes and infrastructure, in the browser it does matter when and how things are optimized. It means you won't be able ever to beat pure CSS and pure GPU acceleration. You just can't do it in JS.
  • therefore it makes sense for CSS to be able to do very limited things like variables, very simple input-output functions, very simple calculations
  • CSS is optimized also for bundle size as it is an exception-based language: meaning you have the most out of it if you create a terse set of core defaults and override those defaults when they are needed
  • there are excellent new courses in CSS that makes you familiar with the small subset of the right tools: I have read through this and got mostly enlightened: every-layout.dev/rudiments/boxes/ You also need to do an advanced CSS course somewhere and you will be top-notch (it is true that there are too many bad advice, bad documentation is out there)

Where it sucks though: when you have variable and values YOU MUST PROVIDE ERROR MESSAGES the very least. With CSS variables I struggled with this. It must be solved.

Finally, I think the whole things is a false-dichotomy and we don't look at the real problem:
We clearly have a need for a unified, well-designed and performant language for the browser, essentially turning it into a sandboxed virtual machine. We can't have it because of Browser Wars.

Thread Thread
 
afif profile image
Temani Afif

Sorry but it seems you are misunderstanding how CSS works. It doesn't work per property and we don't learn properties when working with CSS, we learn concepts. We learn "Flexbox" and "CSS grid" as a layout algorithm but we don't learn all the properties that comes with Flexbox and CSS Grid. You learn the concept of CSS Selector and Cascading but you don't have to know all the selectors by heart.

CSS will keep adding more and more properties and features but you don't have to know them and use them. I work with CSS everyday and I probably use only 10% of it. You can build a website without knowing CSS grid, without using any math functions, without clipping/masking, without knowing gradients, etc

Also CSS and JS are two different beast. No one is meant to replace the other. If you can do your job using JS then do it but If I am able to do the same using CSS then I will do. Why limiting people to one universal approach?

And as browsers already understand Javascript, why use something else?

Browsers also understand CSS so I can ask you the same question: Why using JS to write an algorithm that is natively implemented by browsers using CSS?

Thread Thread
 
efpage profile image
Eckehard

If they add an option to use Javascript inside CSS, this would be great. All the complicated rules where and when to apply one ore another rule could be wirtten ins a language most people would unterstand. But CSS functions are not Javascript, they are a new island created just for CSS. Why?

Assume I want to do some fancy stuff, let say I want the second sibling of the first element be green, but only on sundays. It would be easy to write such a rule in Javascript.

If I want to do this with CSS functions, I first need to learn the new syntax. May be there are some constrain? Does CSS konow the time?

My example could be more complex, let say I want to use the weather report of New York to change the appearance of my app? Javascript gives me all this without any new rules and tools.

Thread Thread
 
link2twenty profile image
Andrew Bone

The web is made up of three fundamentals

HTML - which is only the structure of the page

CSS - how the page looks

JS - adding interactivity, modifying the Dom and other logic.

JS is very powerful but is often the reason web pages run slow, writing good JS is hard.

For a long time people have used JS to fill in the gaps that traditionally CSS left but as CSS gets better that gap gets smaller leading to a better web for everyone.

The overlap is artificial, in an ideal world there would be little overlap between the functions of HTML, CSS and JS.

Thread Thread
 
efpage profile image
Eckehard

Wo is using HTML to describe the structure of a web page? Is it 1980 again? People are using React today, and they have good reasons to do so. The "separation of concerns" is a phantom that never was true.

The capabilities of HTML to describe the structure of a document are very limited. You create a document by mixing some CSS-references into your document, but there is no level of abstraction. You do not describe the structure of a document, you are mixing HTML and CSS and hope the result looks good.

Systems like Bootstrap tried to add some level of abstraction and to provide some prebuilt elements you could use, but people found this was limiting them too much.

Today, they use Tailwind, which adds all the CSS right into HTML.

It that what you are talking about?

Thread Thread
 
link2twenty profile image
Andrew Bone

I'm unsure if you're being actively obtuse but I'll give you the benefit of the doubt, though I think this will be my last comment ๐Ÿ˜Š.

React uses a virtual Dom in JS that react-dom turns into html. For anything to be displayed in the dom it must be HTML, which was invented in the 90s, or the browser can't understand it.

Whilst it is true that JSX is not HTML by the time a browser sees it, it is. This adds a lag, even though it's a small one, between the JSX being parsed and the HTML being rendered. It's because of this that things like RSC have been created to build and ship HTML rather than JSX where possibly to reduce that lag.

Tailwind is a set of CSS utility classes that can be added to elements and is a solution for DRY but can lead to longer build times and shipping more to the client then you need to again this causes lag but this time it is at download rather than execution.

Making it so there is less need to patch things with slow cumbersome JS is a win for the end user, it is also something a dev will have to learn but that it the difference between good Devs and great Devs, putting the end user above DX.

With all that said the web is, and always will be, backwards compatible if you're happy doing it the way you know no one will force you to change your ways, I'm looking forward to the changes and that's ok. It sounds like you're not looking forward to the changes and that's ok too.

Thanks for taking the time to comment. ๐Ÿ˜Š

Some comments have been hidden by the post's author - find out more