The use of atomic CSS for styling HTML is described. Modern atomic CSS libraries provide a convenient instrument for styling HTML documents using classes with pre-defined style declarations. Case studies are looked at in detail: how atomic approach to styling may be improved, how MacroCSS simplifies the process of HTML styling, how MacroCSS may be used in real projects to build modern applications.
How should we organize code of our user interfaces? The community of web developers divides into several camps. Some state that we should have components hierarchy, where each component has a purpose – e.g. button, avatar or link, components may be a set of other components – e.g. menu (set of buttons), table (set of columns and rows) etc. Some state that we should have set of tiny elements, each of the element describes a single state of element styling, it is called atomic approach.
Component based approach gives an opportunity of fast start with a pack of well-designed components, whereas atomic approach key feature is freedom to compose your own components at a cost of some amount of work.
Brad Frost considers that atomic elements combine together to form molecules. These molecules can combine further to form relatively complex organisms. To expound a bit further:
Let's take a look, what we consider atomic when we speak about atomic CSS, so here is Tailwind CSS documentation:
We see that one class name describes just a single set of properties. If we need to style our component – we just combine classes:
Combination of classes describes the component. So the atomic way does not contradict the component-based approach, but it significantly eases the pain of working with CSS:
Although, being a rather usable product Tailwind CSS (as well as Tachyon and Bootstrap) – it has some peculiarities (not to say flaws). What are they? Main disadvantages are the following:
Would it be more convenient if we could combine pseudoclasses in groups?
MacroCSS is a library developed by HealthSamurai team. It was inspired by Tailwind CSS and the idea was to avoid the problems of its predecessor:
It is written in Clojure – functional language, JVM hosted LISP. Clojure is homoiconic language, which means code itself is a datastructure of language, it may be threated as ordinary data. Let's take a look at how it works.
Clojure code:
Generated CSS file:
So, things you only need to know are the following:
As we see – it is fully atomic. You just combine declarations of style after macro call. Some of them have predefined values. Some may take arbitrary values. If you are already familiar with Tailwind CSS – MacroCSS has similar structure of intuitive classnames.
Documentation for MacroCSS is organized the same way as in TailwindCSS:
If you want to have your own classnames or redefine value for existing classname – there are instruments to perform it.
To define a single class – you need to declare a new method for multimethod:
To define a bunch of classes – use 'defrules' function, it actually just declares new methods for you:
After declaring background attachments styles – you may use them in your code:
If you want your all or some classes to take parameters – it is also possible. When you declare the value of styles – use lambda-functions for classes that take arguments. Let's write the same bunch of classes with different approach:
So – now we can use this class another way:
Media rules have more complicated mechanism of declaring. We will explain the reason for it further. Default media rules values are:
If you want to set your own media rules – use set-own-mediarules! function, it takes a map that will be new set of media rules.
If you want to add some media rules – use extend-media-rules! function:
Why do we use macro and not just a function? The answer is not that trivial. If we look at the following code – we will understand that a style should already be computed before the code of components is evaluated:
It is just impossible to perform without using a macro.
How do macros work? C. Emerick describes it the following way - it is between the read and evaluation steps where compilation happens and macros occupy a privileged status compared to functions. Whereas function calls in source code carry through to the compiled representation of that code where arguments are evaluated and passed to the function as parameters yielding some result at runtime, macros are called by the compiler with their unevaluated data structures as arguments and must return a Clojure data structure that can itself be evaluated.
There are two versions of c macro: plain c and c-eco macro. What is the difference between them?
As we see – c macro takes and additional argument &env. What is &env? &env – binding for all local bindings, it work slightly different in Clojure and ClojureScript.
In MacroCSS we use it to get namespace, line an column in code where macro was called:
It makes development more convenient because you always know which component to debug and it makes hot-reload of styles more reliable.
c-eco macro is recommended for release purposes – it takes all arguments passed to c macro and calculates a hash of it:
That means that same set of properties will almost always return the same hash, thus we reduce the number of classes used in CSS file, especially when you use reusable components.
We take the following steps to perform style calculations:
but also garden-like declarations:
Artem Alexeev, full-stack developer at Health Samurai LLC
Наши инженеры регулярно делятся профессиональным опытом в формате статей, live-coding, видео, рекомендаций и т.д.
Если у тебя 2+ года опыта в web-разработке, ты хочешь писать на Clojure – присоединяйся к нам.
Если у тебя 3+ года опыта в JS, являешься или хочешь развиваться в сторону full-stack инженера – присоединяйся к нам.
Если умеешь "распутывать" бизнес-контекст, любишь общаться и организовывать разработку, проектировать UI/UX.