Hello, friends!
My name is Ramadan, and I am a front-end developer and enthusiast who likes to look at familiar things in web development from a new angle.
You may have heard of Functional Programming (FP). It is a paradigm characterized by the use of pure functions and preservation of data transformations. There are even languages in which FP principles dominate, such as Haskell, OCML, and Elixir. Other languages such as JavaScript, Python, and C++ also support this approach, although they are not limited to it.
But an attentive reader will look at the title and ask: “Functional programming is fine. But what does Atomic CSS have to do with it?” I will answer it now!
The thing is, when the atomic approach first appeared, it had another name: Functional CSS. Some people still use it frequently today to avoid confusion with other terms The name. But why is this style of writing CSS called functional?
This is the question that I will try to answer in this article. First, I will describe the basic principles of FP. Next, I’ll talk about the basics of Atomic CSS (which I’ll refer to here as ACSS), drawing analogies with functional programming. I’ll also try to use simple examples to show what problems you can solve by applying Atomic CSS to styling.
When preparing the material for this article, I relied heavily on This lesson And on FP This one On to Functional CSS.
Okay, let’s get started!
Conditions
To follow this article, you only need a basic understanding of HTML, CSS, and JavaScript. There are also some examples where we will use the Atomic CSS framework. mlutbut you don’t need to know its syntax because I’ve provided the text with equivalent CSS styling.
What we will cover:
Fundamentals of Functional Programming
Functional programming is a broad field that has been the subject of many complex disciplines and a thoroughly scientific one Journal. So in my article, I will focus on exploring only the basic principles of FP and drawing analogies with them in Atomic CSS.
This approach is based on the idea that all the functions we want to perform in a program should be done by calling certain functions and their compositions.
Let me outline the basic concepts I will use to explain the basic idea of this approach:
Pure functions
Variability
Function Composition
Pure functions
A function is called pure if it:
Here are some examples to illustrate this:
let c = 10;
let s = 0;
// Pure function
function pureSum(a,b) {
return a + b
}
// Impure function
function notPureSum (a) {
s = a + c
return s
}
The first function is pure because if we input the same arguments we will get the same result. Also, this function does not change any global variables and does not modify objects.
The second function does not satisfy the concept of purity on both points. This changes the external variable. s and uses another external variable. c For calculations, that can change.
Pure functions allow you to write more predictable code. An application can grow and a function with implicit parameters that can change over time can cause great difficulties in debugging and maintaining the code base.
Variability
Immutability is a principle that states that a data object must not change after it is created. To make changes to the data, you must create a new instance of it and then work with that new copy.
At first glance, this approach seems to limit flexibility in the development process and slow down the program. But in reality, if the language or runtime has optimizations for immutable data, following this rule helps you avoid many errors and use parallel computations.
Here’s a fairly simple example: Let’s take a React component that renders a task in a to-do list. A task state is defined by an object. And in order for React to correctly recreate the state of a task component when the user changes something in it, it’s important to pass the new state not as a modified old object, but as a new instance of the object with the current state. Here’s an example where an immutable object is used to state:
import { useState } from "react";
function TodoItem() {
const (todo, setTodo) = useState({
text: "Write article",
done: false
});
const toggleDone = () => {
setTodo({
...todo,
done: !todo.done
});
};
return (
{todo.text} — {todo.done ? "✅" : "❌"}
);
}
export default TodoItem;
Here we will see that clicking the button will change the state of the task and cause the component to re-render. But we can define a function. toggleDone() Using object variables differently:
const toggleDone = () => {
todo.done = !todo.done;
setTodo(todo);
};
With such an event handler, the effect will not work because the link to the object is still the same, even though the object itself has changed.
Function Composition
Function composition involves using the result of one function as an argument to another function. Here is an example of a program that capitalizes the first letter of each word:
function compose(f1, f2) {
return function (str) {
return f1(f2(str));
}
}
function makeFirstCapital(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
function upperEveryFirst(str) {
return str.split(' ').map(makeFirstCapital).join(' ');
}
function lower(str) {
return str.toLowerCase();
}
const capitalize = compose(upperEveryFirst, lower);
const string = 'this sTring should be Capitalized'
console.log(capitalize(string)) // 'This String Should Be Capitalized'
Here, we have defined the function. compose(f1, f2)which returns the composition of functions passed to it in arguments. Next, we use this function to create a function. capitalise()which will capitalize only the first letters of words by composing functions. lower() And upperEveryFirst(). The first one is executed first and returns a string with all lowercase letters as the argument to the second function. The second function capitalizes the first letter of each word.
In larger projects and when complex calculations are required, such compositions can be larger, and their logic richer. In such cases, this approach to computation helps break down large transformations into a series of relatively simple and compact functions that are applied sequentially. This makes it easier to develop, refactor, and debug code.
How FP principles meet their incarnation in Atomic CSS.
Now that we know a little about functional programming, let’s try to answer the question: ‘What does Atomic CSS have to do with it?’ Let us draw an analogy between these methods at the level of the principles described above.
What is Atomic CSS?
But first, let’s take a moment to explain what Atomic CSS is. It’s a method of styling layouts in which we use small atomic CSS rules, each of which performs a single action. These classes are called utilities. They usually apply one CSS property, but not necessarily the only one.
For example, in the mlut framework, the Bgc-red Equals utility. background-colour: red Property, etc -Sz50p Utility corresponds to two properties at the same time: width: 50% And height: 50%.
Modern Atomic CSS frameworks, such as mlut and Tailwind, use a so-called JIT engine. It’s a component that generates CSS based only on the utilities you’ve used in your markup.
Purity
In CSS, sanity is determined by the selectors, or more specifically, classes, used to style elements. In clean CSS, the behavior of an element should be determined entirely by the classes that bind to it. class attribute means that, ideally, a style sheet should not contain selectors such as section or div > ul.
First, they set very general rules, which are likely to be broken or completed in one part or another of the project. So when we style certain elements, we have to constantly keep these styles in mind to understand how to achieve the desired style without spoiling anything.
Secondly, CSS purity is violated for each specified element. Let’s say we have the following markup:
With the CSS style below:
.wrapper > .greeting {
background-color: green;
}
.greeting {
background-color: red;
}
As a result, we find that the first button is red, and the nest is green. This seems harmless enough in this simple case, but when the project structure grows significantly, it will become a pain.
Here we see the result .greeting The custom styling of a class depends on where the corresponding element is located. It is like a function that produces different results for the same input data depending on where it is called.
Atomic CSS allows you to avoid this effect. In this approach, in most cases, styles are applied only to elements for which the corresponding classes are specified. If you need to create several identical elements, a single utility is defined. class Characterization of each such element.
A similar example can be rewritten in mlut as:
And the JIT-engine will generate the following styles:
.Bgc-red {
background-color: red;
}
.Bgc-green {
background-color: green;
}
Here we can see that in this approach the styles of the elements will depend only on the classes assigned to them.
It’s worth noting here that the mlut syntax allows you to do things that deviate from CSS’s strict notion of purity. Sometimes this is necessary to create relatively more complex effects.
Let’s say we want to implement a card that changes the background color of the button inside it on hover. Then we need to write the following in mlut:
CSS:
.-Ctx:hover .\^\:h_Bgc-red {
background-color: red;
}
Immutability in ACSS
By CSS change, I mean element styles are not rewritten. Interchangeability in ACSS means that utilities are generally not interchangeable.
For example, in BEM (Block Element Modifier), the main styles are set by a block or element, and a modifier modifies those styles. In other methods that use covariates, such variations are more frequent and less pronounced.
Let me give you a simple example. Suppose we have a product card that can be in its default state or in a highlighted state. In BEM it will look like this:
Card 1
Card 2
Card 3
.product-card {
background-color: red;
padding: 5px;
}
.product-card--selected {
background-color: green;
}
In this example, we see that product-card Sets the default background color of the class card to red. And to somehow mark the selected card with a different background color we need to add a modifier class that changes the color from red to green. It does this by rewriting. background-color property, which is by changing block styles.
In the atomic CSS approach, this problem is solved because utilities allow you to set CSS properties independently of each other and apply modifications without resorting to mutation.
This example would look like if you use mlut:
Card 1
Card 2
Card 3
.P5 {
padding: 5px;
}
.Bgc-red {
background-color: red;
}
.Bgc-green {
background-color: green;
}
Composition
Functional programming makes extensive use of function composition. In Atomic CSS, the composition function is similar to the composition utility when styling elements. Just as in FP we achieve complex behavior through the sequential application of a set of simple functions, in ACSS we can achieve unusual styling through a set of simple utilities.
As an example, I’ll show a simple smiley face made using only Atomic CSS:
.-Sz150 {
width: 150px;
height: 150px;
}
.Bgc-yellow {
background-color: yellow;
}
.Bdrd100p {
border-radius: 100%;
}
.M-a {
margin: auto;
}
.Ps {
position: relative;
}
.-Sz20p {
width: 20%;
height: 20%;
}
.Bgc-gray {
background-color: gray;
}
.Ps-a {
position: absolute;
}
.T30p {
top: 30%;
}
.L20p {
left: 20%;
}
.R20p {
right: 20%;
}
.W50p {
width: 50%;
}
.H40p {
height: 40%;
}
.Bdb5;s;gray {
border-bottom: 5px solid gray;
}
.T40p {
top: 40%;
}
.L25p {
left: 25%;
}
Our result will look like this:

Thus, by implementing one utility after another, we also created some small CSS art.
The result
In summary, I would say that Atomic CSS truly embodies the fundamental principles of functional programming. Of course, not literally – but in the sense that is relevant for front-end developers and layout designers.
I’d be happy to hear your additions and objections – they’ll be interesting to read and think about.
Finally, I would like to say: see familiar things with a new perspective. And, as always, I wish you success in your exciting journey of front-end development!