Tailwind CSS has been mentioned on social networks recently, and I did a project with it out of interest some time ago. Unlike many other technologies, Tailwind CSS has had its fair share of online controversy since its inception. Unlike many articles on the web, I don’t want to discuss the technical details of atomic CSS in this article. Instead, I want to give my thoughts on the current resurgence of atomic CSS and the development of front-end style writing.

“The Front Three Musketeers”

Even among non-professionals, the three technology stacks of the front end – HTML, CSS and JavaScript – are well known. This is partly because they form the basis of today’s colorful pages, and partly because we can explain their respective uses in simple language and make people think they understand them. The classic explanation is: HTML determines the skeleton and content of the web page; CSS determines the style of a web page; JavaScript determines the behavior of the web page.

All three seem to be working well together. But are they really completely orthogonal? Reality is not always so ideal. HTML has always wanted to insert styles, not to mention the old pure style tags like , but even in modern browsers, basic tags like ,

,

have default styles (which is why CSS Reset, normalize.css, etc.). Some HTML tags also come with the ability to interact, ostensibly to improve the user experience when JavaScript is disabled.

CSS is also expanding its capabilities. CSS was originally designed to add some simple styles, but as people’s needs for style and interaction have become more complex, the old capabilities are far from enough to meet the needs, so new capabilities are constantly added to the CSS standard, and various CSS frameworks have mushroomed. At first they just enhanced the syntax of the original CSS, and later evolved into an almost Turing-complete closed loop. You can often see some cool interaction effects of pure CSS on the web, which is amazing.

As THE DOM API continues to improve, there’s nothing HTML or CSS can do that JavaScript can’t.

But as the capabilities of all three grew, interoperability between them became a bottleneck. For example, when you need to attach different styles to different HTML elements based on complex conditions, you need to write glue code in JavaScript. It’s not that you can’t do it, it’s just that JavaScript oversteps the boundaries of the three, and your mind has to switch between them to understand what apis each of them exposes to the other’s world, which adds to your mental burden. Interestingly, this is exactly the reason CSS is weak and needs to be enhanced, and enhanced CSS forces you to learn the new syntax it introduces, only to do the same thing JavaScript does, further aggravating your mental load.

Since these three are closely related to each other, I wonder if you have ever wondered why these three have to be so dead, rather than unified into a set of systems? Perhaps we need to look at the answer from a different Angle.

History of the Web and other GUI technologies

Although everything that can be implemented in JavaScript will eventually be implemented in JavaScript, so front-end developers can do anything. But let’s take a look beyond the traditional web and take a big front-end look at other GUI technologies that represent how developers thought about designing GUI development in their time.

Java Swing

Swing is a cross-platform GUI development framework supported natively by Java. Since Java itself is a very strict object-oriented language, Swing naturally inherits its object-oriented style as well. Specifically, every component in Swing is an object, and the content, style, and so on of the component are properties of this object. Objects combine to form a complex application interface.

An example of a Swing program looks like this:

// Create a window
JFrame frame = new JFrame("HelloWorld");
Exit the program when the window is closed
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Create a label
JLabel label = new JLabel("Hello World");
// Set some styles and attributes for the tag
label.setIcon(new ImageIcon("path/to/your/image.png"));
label.setLocation(200.100);
label.setIconTextGap(0); 
label.setBorder(null);
label.setOpaque(false);
// Add the label to the window
frame.getContentPane().add(label);
// Set the window size to cover the content
frame.pack();
// Make the window visible
frame.setVisible(true);
Copy the code

This is a relatively simple scene, if you need a complex custom layout, you can also take the underlying AWT Graphic object to draw. While the code examples are clear, in real projects it is easy to get mixed up with logical code, making layout and style hard to visualize.

Android

Android has its own SET of apis, but its design is similar to Swing’s in that it is based on object-oriented thinking. One of the main features of Android is the introduction of an XML layout scheme, perhaps inspired by HTML. But XML is essentially just a wrapper around the underlying API, and the layout described in XML is perfectly possible using Java code. In extremely performance-oriented scenarios, asynchronous parsing or completely handwritten layout code may even be needed to reduce the overhead of parsing XML text. When the layout or content changes, it is also necessary to manually track the range and process of changes through imperative grammar to obtain control objects for operation.

An example of an Android layout is shown below:


      
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/title_bar"
    android:layout_width="match_parent"
    android:layout_height="48dp"
    android:layout_gravity="center"
    android:background="@color/header_background"
    android:gravity="center" >
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingBottom="4dp"
        android:paddingLeft="4dp"
        android:paddingTop="4dp"
        android:src="@drawable/header_icon2" />
</LinearLayout>
Copy the code

The Java equivalent is written as follows (some code has been omitted for simplicity) :

LinearLayout layout = newLinearLayout(context); layout.setId(R.id.title_bar); layout.setBackgroundColor(...) ; layout.setGravity(Gravity.CENTER); ImageView image =new ImageView(context);
image.setPadding(4.0.4.4); image.setDrawable(...) ; layout.addChild(image);Copy the code

The advent of XML solves some of the problems of pure code writing. In the face of static layouts that don’t change much in most cases, writing in XML makes it significantly more readable and intuitive, as well as easy to preview and drag layouts in real time with the IDE. However, its essence has not changed. In actual scenarios, it is still necessary to acquire a large number of objects in the code for operation. When the layout changes, it is also necessary to write codes for manual management of layout objects.

React

React is a Web technology, but I think it’s worth singling it out. React, a new generation of Web technologies, uses functional programming ideas creatively to implement declarative UI writing. The core idea is UI = F (State).

In declarative UI writing, we focus only on rendering the UI according to the latest state, rather than manually changing the state of UI components for each state change, which greatly reduces the complexity of writing code. At the bottom, it uses a set of common Diff algorithms to make the UI re-rendering process as efficient as possible.

Another feature of React is the JSX syntax. JSX looks like HTML, but is actually an extension of JavaScript syntax. The HTML style is designed to make it easier for developers familiar with HTML to get up to speed. It is possible to write using a pure JavaScript API if you prefer.

The advantage of using JSX instead of HTML is that because it is essentially JavaScript syntactic sugar, you can write a layout that works seamlessly with other logical statements, which makes development very smooth. For example, in React, we can render an array as a list element like this:

function List({ data }) {
  return (
    <ol>
      {data.map(item => <li key={item.key}>{item.value}</li>)}
    </ol>
  )
}
Copy the code

React provides its own answer to the problem caused by the separation of HTML, CSS, and JavaScript. It unifies HTML and JavaScript and completely solves the inconvenient situation of interaction between them. React also provides a simple wrapper around CSS, allowing people to express a set of styles in a JavaScript object. This layer of encapsulation also makes React code easy to migrate to other csS-free platforms, such as desktops and mobile.

Flutter

The main idea behind Flutter is the same as today’s popular declarative UI. However, as a new technology, Flutter does not have much historical baggage. The entire framework and its implementation language, Dart, are controlled by Google, so they can directly support the desired features from the level of syntax and toolchain. This also makes them more efficient at runtime, and the syntax looks more aggressive:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Container(
      decoration: new BoxDecoration(color: Colors.white),
      child: new Center(
        child: new Text('Hello World',
            textDirection: TextDirection.ltr,
            style: new TextStyle(fontSize: 40.0, color: Colors.black87)), ), ); }}Copy the code

As you can see, the layout, style, and logic of applications developed with Flutter are all implemented by Dart, making interoperability between them seamless. For some layouts and styles, Flutter provides separate components directly, encouraging the use of combinations without the complexity of layout rules and depth of nesting. When building a layout, you can just build new as you would a normal object, without having to learn another language or DSL. You don’t have to worry about deep nesting and too many temporary objects. The underlying runtime has its own way of optimizing.

Back to the Web side

We’ve listed a few representative GUI technologies that I know a little about in terms of time to get a sense of where the technology is headed. One is the gradual adoption of a declarative approach to writing interface layouts, which has taken a lot of mental weight off of us. On the other hand, almost no one believes that layout, style, and logic should be strictly separated, but should be unified and described by the same system.

So why is the Web the outlier, split into HTML, CSS, and JavaScript? I think there’s a historical answer to that.

HTML came first, before all the other GUI technologies above. The whole process of HTML is HyperText Markup Language. It can be seen that its original intention is just to better describe a document, so some enhancements to ordinary plain text documents are needed.

CSS was invented after HTML as a way to beautify the presentation of HTML, and the results of beautification can vary from person to person and browser to browser (which also fosters the problem of CSS consistency between browsers). CSS also has layers, such as allowing user-specified styles to override the browser’s default styles, which is why CSS is called cascading style sheets. HTML should not rely on CSS to ensure that the content of a web page appears properly even without CSS. For such design purposes, it makes sense to separate HTML from CSS.

JavaScript came much later, and it started out as a simple form and interaction thing, but it didn’t have the kind of ecosystem that it is today when it was invented. One of the best practices that I remember people talking about a few years ago was that JavaScript would be disabled for security reasons, and that the web pages that were disabled should be as good as possible so that JavaScript was not used at all. But after SPA became popular, the sound gradually disappeared.

The biggest difference between Web technologies and the others mentioned above is that the Web was originally intended only as a presentation of documents, while the other technologies were intended as a complete application on their own platforms. The functionality and interaction logic of an application are far more complex than documented presentation, which requires that the interaction layer and presentation layer interoperate as easily as possible at the code level. But the Web has grown so far from what it was invented for. The Web is practically applied. So we need to rethink the Web from an application perspective. This is why we feel that the three-way architecture of the Web is starting to hold us back.

“Layered architecture”

Layering is a common practice in software development. As the size of the code base grows, layering forces developers to understand the dependencies of the various modules in the code and prevents the code from becoming a paste. But specific to how to stratify, also have some exquisite.

Traditionally, when we talk about layered architecture, we mean horizontal layered architecture, such as the MVC architecture when the front and back end were not separated. In a real project, we can further divide it into entity, DAO, Biz, Action, etc. And in the pure front-end domain, HTML, CSS and JavaScript also constitute the structure layer, style layer, interaction layer these three independent layers, which also makes it very clear, easy to be accepted by people.

But as time went on, developers found that such horizontal layering was no longer sufficient for increasingly complex business scenarios and the large systems they comprised. It is difficult to use a set of universal solutions to solve a variety of complex business problems. Therefore, rather than focusing on layering them in terms of technical functions, we are more concerned with separating them from a business module perspective.

The typical example of this is the back-end SOA architecture. We abstract the business logic into relatively independent services, which are invoked by exposing interfaces. However, in the service itself can choose the implementation of the way, as long as the interface remains unchanged, no matter how internal code is divided, how to reconstruct, external callers are black box. This also embodies the principle of high cohesion and low coupling. Further, we have developed microservices architectures to better adapt to cloud computing and distributed development. In the microservices architecture, services communicate with each other through network apis, and different services can even be implemented using different technology stacks.

The industry loves to invent lots of new terms for every tiny technological innovation. Regardless of the nuances of the definitions of these terms, they can also be summarized as hierarchical structures. But instead of traditional horizontal layering, it does vertical layering, dividing the entire application into different business modules, and inside each module, depending on the complexity of the business, can contain one or more horizontal layers, or whatever the developer likes.

Horizontal stratification by technology level and vertical stratification by business type. Figure source kislayverma.com/programming…

This is also happening on the front end. As front-end engineering matures, developers no longer need to manipulate raw HTML, CSS, and JavaScript, but treat them as final compilations. On top of this, front-end componentization quietly rises. Instead of thinking of HTML, CSS, and JavaScript as three distinct hierarchies, we split the project into business components, large and small, breaking the three into each component. React all-in-JS needless to say, even Vue with three separate components needs to use single-file components to encapsulate their fragments in the same file. Following the development of the back end, the standard micro service has become the burgeoning micro front-end architecture. We’ll have to wait and see if the “micro full stack architecture” starts to emerge in the future.

It’s easy to understand the changes in code organization and developer attitudes in recent years. It’s not just a technical problem, it’s a human problem. The layering of code, horizontally by technology or vertically by business modules and components, is simply looking at different angles of the same problem, and there is no right or wrong way. But as projects get bigger and more complex, no one has all the details of a project, so you have to think about how many people work together. And dealing with people is a lot more difficult than dealing with code.

If multiple people collaborate, with one responsible for page content, one responsible for interaction, and one responsible for styling, then everyone has to understand the business logic of all the requirements, but on the other hand, they will spend a lot of time fighting over their coupling parts, which can greatly reduce development efficiency. So normally we have each person responsible for an entire, independent business requirement. Changes in the organizational structure of the project staff will ultimately be reflected in the code structure.

In addition, each person has a different personality and a different way of writing code. Although we can use code specifications, formatting tools, etc., to make code look similar and avoid what is generally considered bad writing, it is impossible to solve the problem in the same way. That’s why reading someone else’s code is the most painful. Therefore, we strive for as high cohesion and low coupling as possible, so that the module can do whatever it wants inside as long as the external interface is healthy.

Front-end componentization and the decline of class

After the componentization of the front end, we had to rethink whether what we had done was still valid today.

This is where the HTML class attribute comes in. Now, when you talk about the use of the class attribute, it’s basically just a way to use CSS to attach styles to elements. But class was designed for more than that. This is also evident in the name, which means “class name” and is meant simply to add a (repeatable) name to the element.

In the early days of HTML, there were very few supported tag names, and HTML was a single file, making the content less readable. Adding human-readable names to some elements can improve the look and feel of the code. That said, classes aren’t just for CSS, which is one of the reasons classes have traditionally been recommended with meaningful names.

HTML5 defines more semantic tags for HTML that describe the common parts of many websites. It also acts as a standardisation, which is more conducive to SEO, but also to some extent replaces and diminishes the role of class.

In today’s componentized era, developers have the ability to encapsulate various raw tags into business components that need only be combined when assembling the upper level interface. This equates to the ability to create new tags that express the meaning of the component in the name of the tag. This further weakens the ideographic role of class. With declarative UI writing, we rarely use class to manipulate native DOM objects.

Finally, all that’s left of class is the ability to append CSS styles.

However, if you use class just to attach styles, does it make you feel like you’ve taken your pants off and farted? Programmers know that naming is one of the most difficult things. Is it worth it to use it just once for additional styles that require you to come up with so many names, even spawns a cumbersome solution like BEM? In addition, the separation of HTML and CSS code also causes your attention to skip repeatedly while developing and reading the code, and the window to turn pages repeatedly, which also destroys the coherence of your development ideas.

So, should I use inline style? It is believed that the trained mind will immediately take an instinctive dislike to the word, and will cite reasons why it should not be done. Well, inline style code looks ugly and cumbersome, no doubt about it. But is any other reason valid today? This is a debatable question.

One objection to inline style is that it breaks the hierarchy of CSS, because inline styles take precedence, deprioning the rest of the project of the ability to override styles, which goes against the intent of cascading style sheets. But is the ability to override styles really worth using? I don’t think there are many people who can quickly recall the priority rules of various CSS selectors without reference. If you want to use CSS overlay style ability as a feature in a large scale in the project, it will only make the logic more chaotic and difficult to maintain in the long run, and finally only! The important point.

One might want to use this feature for component-style custom interfaces. For example, you might have a button that is normally blue, but in a particular scenario, you can change the color to red by overriding the style of the button. Note, however, that this is not a CSS problem. Rather than simply using CSS overwrites, we should think about the business scenarios in which we need to change the color of the button, and give the button component a property that distinguishes the different scenarios. In addition, as component styles become richer, many CSS styles are required to create a complete visual effect, so it is unrealistic to expect a simple overlay of CSS to achieve color change.

A good example is the Button component in the Element UI. Instead of directly exposing a class to be overridden by the caller, it differentiates different business scenarios through the Type attribute to appear in different colors. On the one hand, this clarifies the business scenario and conforms to the semantic design. On the other hand, it also provides the color specification and prevents the visual confusion caused by the misuse of the caller.

Another objection to inline style (and perhaps the most important) is that styles can be reused using class. This was really important in the pre-componentization era because it was the only way to reuse styles. But with componentization, we were left with an awkward question: should we use the class reuse style or the component encapsulation reuse style? It’s not hard to realize, of course, that you can reuse everything in component form, whereas class can only be used for reuse styles. The component is completely superior replacement.

In addition, the emergence of componentization has made it difficult to reuse styles using class. Before componentization, no matter how complex your CSS was, you could share the same style sheet globally and reuse styles as long as the class name was the same. However, in order to avoid class name conflicts between different components, we tend to use CSS Modules, Scoped CSS and other methods to avoid componentalization, which impedes the reuse of styles between different components.

Consider another very practical factor. In a real project, there is always something customized for different requirements. You have worked hard to design a reuse system, and once the product changes to a requirement, it is back to pre-liberation. This results in a very limited number of styles that we can actually reuse. For example, we designed a reusable style where everything looked good:

<div class='container'>container1</div>
<div class='container'>container2</div>
<div class='container'>container3</div>
<style>
.container {
  display: flex;
  margin: 10px 0;
  padding: 10px;
}
</style>
Copy the code

But as the product iterated and tweaked, it slowly became this:

<div class='container1'>container1</div>
<div class='container2'>container2</div>
<div class='container3'>container3</div>
<style>
.container1 {
  display: flex;
  margin: 10px 0;
  padding: 10px;
}

.container2 {
  display: flex;
  margin: 10px 0;
  padding: 0;
}

.container3 {
  display: flex;
  margin: 10px 20px 0 5px;
  padding: 0 10px;
}
</style>
Copy the code

It’s hard to live with code that is not reusable, but not completely reusable. In retrospect, you realize that reusing CSS at such a coarse-grained level is not going to work. So you break down the style code more granular by function, down to the smallest unit you can reuse, and rewrite it to look something like this:

<div class='container1 flex'>container1</div>
<div class='container2 flex'>container2</div>
<div class='container3 flex'>container3</div>
<style>
.container1 {
  margin: 10px 0;
  padding: 10px;
}

.container2 {
  margin: 10px 0;
  padding: 0;
}

.container3 {
  margin: 10px 20px 0 5px;
  padding: 0 10px;
}

.flex {
  display: flex;
}
</style>
Copy the code

Congratulations, you are now inventing Tailwind CSS.

What does Tailwind CSS do for us

CSS served its role in styling well in the early days of the Web. However, as front-end web pages gradually change from simple documents to complexity and application, the characteristics of CSS, HTML and JavaScript separation actually hinder the improvement of development efficiency. After the rise of front-end engineering and componentization, people’s concept of front-end software engineering has changed and developed. The principles and best practices we used to be familiar with when we learned CSS are no longer true or useful. Times have changed, my Lord.

However, the development of Web for a long time has created an extremely large Internet ecosystem. Throughout the history of technology, the secret of a successful, long-lasting technology is often not the technology itself. More important than the technical solution is the first mover advantage and the ability to create a large and active ecosystem. So even though there are many things about Web technology that don’t seem to make sense now, these historical baggage can’t be easily discarded. What we can do is encapsulate the bad side as much as possible and make it more useful.

This is where Tailwind CSS gets it right. It’s not like Sass/Less, which tries to invent a “better” CSS, or Flutter for Web, which tries to make everyone forget ABOUT CSS and everything else and reshape the entire Web ecosystem, or CSS in JS, which embraces componentization, Still, make your hands dirty with cumbersome CSS. It gently insulates the CSS world for us, wrapped in atomic classes. It was astute at spotting the deterioration and decay of the class attribute and took advantage of it to make it easy for developers to understand and use without needing to learn a new syntax.

The emergence of Tailwind CSS is also an inevitable result of historical development. As I mentioned in the previous section, atomic classes have been used to solve the problem of fine-grained CSS reuse, either intentionally or unintentionally. Tailwind CSS normalized and engineered all of this.

Tailwind CSS also provides some additional capabilities, such as standardization of various margins, colors, and units in a project. This is also quite a commendable point, keeping a lower limit on the aesthetics of our pages. With the right problems at the right time, this is surely the most useful CSS framework of our time.