Important: The information in this article is over 12 months old, and may be out of date or no longer relevant.
But hey, you're here anyway, so give it a read see if it still applies to you.
This article was written for and originally appeared on Blueprint by Tiny.
I remember when I started writing CSS – and how incredibly mind-blowing it was to have all of my layout and styling controlled by a single file. If I needed to change my heading style, I could change my CSS file, and all of my headings would update. Splendid!
And now 20-something years later, the same is still true. CSS gives developers the ability to separate content from presentation. As your content base grows, your company’s brand may too evolve. When the visual elements of your brand change, all you need to do is change your CSS file, and all of your existing content can look just as incredible without having to edit each piece of content individually.
Over the years, my workflow and approach to CSS has changed too. Like most developers, I started with writing vanilla CSS, then moved on to LESS, then SCSS, then Bulma, and right now, Tailwind CSS – and every shift has delivered efficiency and so much developer happiness. And yet the bottom line remains, in every new tool I’ve seen: the result is just a plain vanilla CSS file. The difference is just how these new tools help me as a developer be more efficient at producing that final code.
CSS
Whether you like writing vanilla CSS in a text editor, or using a preprocessor or framework to help you on your way, the end result for your web site will be a CSS file.
This will contain the instructions for the web browser on how to style and present your content – from colours and sizes through to layout and even mobile responsiveness.
The biggest advantage to writing vanilla CSS is that no additional software is needed. You can quickly change a file, refresh your browser, and see the changes – even on your production server. But we never change files on a production server directly, right?
The big compromise, though, is that you need to be responsible for every single line of code – including every vendor prefix for specific definitions – for optimal browser support. While cross-browser support is getting better (and thankfully those IE6 days are long gone), vendor prefixing is still needed at times. Having that responsibility over my shoulders isn’t the kind of weightlifting I like to do, especially when there are tools that can help, such as CSS preprocessors.
CSS preprocessors (such as LESS, SASS and SCSS)
LESS, SASS and SCSS are examples of CSS preprocessors. Depending on the flavour you pick, you will have a slightly different syntax to follow, but all of these allow you to create and re-use variables, write callable functions, as well as nest your CSS.
When your source is processed, all of these components are put in to play to help generate final browser-ready CSS code.
The trick, though, is that you need to use an additional tool to process your source code. You could use a command line utility, a build tool like Webpack or Laravel Mix, or get yourself an app to help manage your projects and configurations. Personally, I have used CodeKit for years (and love it – but it’s for Mac only) – so if you’re needing something for other operating systems, you could consider an app like Koala – and check out the LESS or SASS documentation for other suggestions.
These tools can also help you with auto-prefixing, generating source maps and minification – such valuable assets in any web developer’s toolbox.
One of the best features of using a CSS preprocessor is the introduction of variables. When your design is ready to be built, you’ll know what colours you will need – such as a few shades of blue. You can create these as variables in your source, and reference the variable, not the hex RGB code. If you find that one shade needs changing, you can then change a single variable value, re-process your code, and every reference to that shade of blue is updated and your finished CSS file is ready to go.
You can even use variables for things like specifying font family stacks, and also standardising padding or margin values or responsive breakpoints to help create consistent changes to your interface.
Functions can also be written, and are a bit like variables, but on steroids. If you have specific rule sets that you often refer to, you could encapsulate these in a function. Any time you need to use them in your code, just call the function (which can even accept input variables) and have the function’s CSS contents inserted at processing time.
One of the best time-saving features of these languages is the nesting of rules. In vanilla CSS, if you need to access an H1 element within a wrapper within a section, your CSS needs to be:
1section#content .wrapper h1 {...}
And if you need to style your H2 too, you need to replace the whole rule definition yourself.
1section#content .wrapper h1 {...}2section#content .wrapper h2 {...}
And if the nesting structure changes in your markup, you’ve got a lot of code to go through. With a preprocessor language, you can nest the H1 and H2 definitions:
1section#content {2 .wrapper {3 h1 {...}4 h2 {...}5 }6}
If your IDE has code formatting, your code can easily be formatted to create much more readable source code that can help you visually see the hierarchy, yet when processed will result in the same flat-level CSS code.
While you do need a compilation tool, if I need to write any CSS code these days, it is always with SCSS. With the ease of use of CodeKit, it makes it fast and easy to get SCSS code up and running for easy compilation, auto-prefixing and minification. Such a time saver!
Ready-to-go Frameworks (such as Bulma and Bootstrap)
Frameworks such as Bootstrap and Bulma are fantastic to help you get a new site up and running fast, including commonly used layout components from menus and navigation through to forms and media blocks. They contain the building blocks to help you build your site design with greater ease.
And better yet, they’re written with SASS and SCSS, so are designed to be customised by taking advantage of the preprocessor benefits like variables, nesting, and functions that we’ve already talked about.
Bulma is a modern and clean flex-based CSS-only layout, meaning it is a great choice for those wanting to use their own preferred flavour of JavaScript. Bootstrap, however, includes full interactivity with components and plugins using jQuery. Personally, I think Bulma is fantastic – a great range of building blocks for building your own custom designs, customisable through SASS, and letting you choose the JavaScript implementation that works best for your team and your project.
One concern with a ready-to-go framework can be their file size. While they can be tailored to suit your needs (and only include the components you actually need), you may also be importing lines upon lines of code that you’ll never use or take advantage of, but that still get included in your final CSS – this means your final CSS may not be as efficient as it could be.
Another concern is that many framework-built sites can start to look the same when developers decide to not customise or override the framework’s base stylings. While this can be great for quick prototypes, for a custom-designed website, it does mean you may need to spend time overriding the styles of components to make them look like your own. And if you’re spending that much time overriding every style (and at each breakpoint), it may have been a smarter choice to write your own SCSS from scratch, or use a utility-first CSS framework.
Utility-first Frameworks (such as Tailwind CSS)
Tailwind CSS is a superb utility-first CSS framework. Rather than having defined classes to create components, Tailwind CSS provides hundreds of classes that have a single function each – such as to change the padding, adjust a font size or change a colour.
If you were using Bulma, or even LESS/SCSS, to create a button, your HTML markup might look like this:
1<button class="button">Button</button>
It is clean, clear and simple. But also relies on a class called “button” that tells the browser how to style the element. This may include a background colour, border radius, foreground colour, and hover styles. Looking at the markup itself, you don’t know what that includes.
Using a utility-first framework like Tailwind CSS means your button HTML markup could look like this:
1<button class="px-2 py-1 border-transparent font-medium rounded text-white bg-blue-500 hover:bg-blue-600 focus:outline-none focus:border-blue-700 focus:shadow-outline-blue active:bg-blue-700">2 Button3</button>
At first glance, you might just look and think that it is an incredibly inefficient way to build a website – that was definitely my reaction at first glance. But after developing an interface from scratch using utility classes, it made so much sense and was incredibly intuitive. If I need to adjust the padding along the X axis: great, I can see it is already set to px-2, and can go up or down from there.
As an introduction to Tailwind CSS, to give it a fair road test, I’ve been developing an entire user interface and have not yet written a single line of CSS; I’m just using the Tailwind CSS utility classes. And I feel so liberated for it! An understanding of what CSS properties do is essential – but Tailwind CSS has streamlined my development process by keeping me focused on the layout and build, rather than the semantics of CSS declarations (especially useful when some utility classes can apply multiple properties at once).
Using a button as an example is actually a good one to consider – because if you have 5 buttons on a page, you wouldn’t want to write that code 5 separate times, right? What happens if you have to change the colour from “blue” to green”?
There are two ways that you could manage this, and it depends on your project.
If you’re creating a web app in a component-based way – such as using Vue or React components – you could create a Button component that has these styles built-in. That way you have a single point that captures your button styles. All you do is re-use your Button component and boom, your button styles are all in place.
One other way is to use Tailwind CSS’s @apply that allows you to define a class that is composed of the utility classes (or your own CSS too). Our button style could be defined as:
1.button { 2 @apply px-2 py-1 border-transparent font-medium rounded text-white bg-blue-500; 3 4 &:hover { 5 @apply bg-blue-600; 6 } 7 8 &:focus { 9 @apply outline-none border-blue-700 shadow-outline-blue;10 }11 12 &:active {13 @apply bg-blue-700;14 }15}
Then we can just use a class of “button” to adopt all of those utilities.
If you’re building an app that includes user-entered content – especially one that includes a rich text editor such as TinyMCE - you want to ensure that you don’t require your users to enter a dozen different utility class names just to style a button – this is where @apply can become incredibly useful.
But for your app’s interface, that you code and build, using utility classes (especially with a component-based JavaScript library like Vue or React) makes Tailwind CSS shine.
One important consideration - especially when switching your mindset to a utility-first approach - is that we want to avoid writing extra CSS code, and that goes for using @apply too. When starting out, you might want to carry over your love of CSS files and over-use @apply. When you notice recurring patterns - such as button styles - this is a suitable candidate for using @apply to help style buttons more easily - especially within the context of developing an app that has user-editable content. You want to avoid your users having to replicate all of these utility classes every time.
However, remember that if you’re developing an app with a component-based frontend, like Vue or React, instead of using @apply and maintaining separate CSS blocks, the Tailwind CSS utilities are better stated directly in your reusable components, rather than needing a separate CSS file.
While the default build of Tailwind CSS is rather heavy, it is also designed this way – it offers hundreds of utilities that each do one little thing each. But in my code, I am only using a small fraction of these. Wouldn’t it be good if my CSS only included the utilities I was actually using? Yep, we can do that too!
When configuring your build step (for a tool like Webpack or Laravel Mix), make sure you also set up PostCSS to help control the file size. During the production build, your source code will be reviewed – such as your JavaScript templates (Vue or React components) or framework templates like Blade or Twig files or even static HTML files – and your final CSS is made up of only the Tailwind CSS utilities that are used in your source.
This process can dramatically reduce your final CSS size – especially if your markup is directly using the Tailwind CSS classes. While the HTML markup can become a little more verbose, it also becomes incredibly descriptive and meaningful during development, and takes advantage of reusing Tailwind CSS utilities throughout your code.
After actually building an interface using Tailwind CSS, I feel it is incredibly friendly for developers to work with, and makes building, tweaking and changing your interface feel so intuitive. If you have never used a utility-first framework like Tailwind CSS, set aside some time to build something with it to see how it can fit in with your process. If you’re a little unsure on first glance, don’t worry, I was too. But after using it, I feel it is such a functional and usable framework, without the forced opinions or excess weight of other frameworks – and allows me to build more of my project by writing less CSS.
If you like opening a text editor and writing your vanilla CSS, that is totally fine. But these are some tools that can help make your CSS coding more efficient.
A solid understanding of CSS rules is needed regardless of the approach you take – and using a CSS preprocessor like LESS or SCSS is a great starting point for any project.
If you want to rapidly build an interface, a ready-to-go CSS framework like Bulma or Bootstrap can be incredibly timesaving.
And if you have a more complex project (including the need for more advanced build tools), a utility-first CSS framework like Tailwind CSS is a fantastic approach too.
Just like choosing the best CMS, there is no single answer that will work best for everyone. At the end of the day, CSS is still CSS – it’s just about how you get there that makes the difference.
But if you had the choice to be able to make a change to your dev process that made your life easier, you’d take it, right? Evolving your workflows can help keep your CSS coding more efficient, and providing greater cross-browser support may be an intimidating step initially, but one that I definitely haven’t looked back from.