Maintaining bespoke CSS for websites can be hard. Using a CSS framework, such as Bootstrap, Bulma, or Foundation, seems like a good idea.

A CSS framework will give you lots of default CSS and JavaScript functionality out of the box, including a huge list of components, such as:

  • Alerts
  • Inputs and buttons
  • Modals
  • Navigation bars
  • Pagination
  • Tabs and pills
  • …etc.

Once setup, you can add your own bespoke CSS to override the defaults and make your website match your design and branding. Alternatively, you can take the complex approach of replacing the defaults with custom values by precompiling the source SASS yourself. Only then can you start to build bespoke components on top of that framework.

The problem with CSS frameworks

There are several problems with CSS frameworks that could make you think twice about using one. Below are a few reasons why a framework could hold you back, reduce your productivity, and make CSS maintenance harder for you.


Frameworks are opinionated, generating classes even though you have not asked for those classes. Frameworks also define what default styles are applied to a class and dictate the HTML markup needed to display correctly.

Bloated code

Overriding default styles adds extra lines of code, which increases the file size. In addition, you are not likely to use every component shipped with a framework. It is critical to use a tool to remove unused styles, such as PurgeCSS. As the project ages, the CSS file size will continue to grow as you add new template designs and custom components.

Design similarities

Frameworks do a good job of giving you lots of components that are great looking, consistent, and accessible. So why would you want to change the default styles? If you only make slight changes, or no changes at all, your website could start to look like other websites using the same framework.

Long-term productivity

After several months of not working on a website, it can be difficult to jump back into a project and start developing straight away. It is time consuming trying to remember the website setup and framework used. You can only start to make changes once you are up to speed.

Understanding syntax

Unless you are familiar with a framework, you will need to learn the syntax for the CSS and HTML. Classes are likely to be named semantically (describing its use or role). In Bootstrap, a nav-bar class is used for an element containing a navigation menu, which makes sense. To correctly implement a nav-bar, you must know all the classes used and the markup structure. Syntax is not always intuitive, and you will need to refer to documentation.

Misuse of CSS classes

Once you understand the class syntax, it is important to use the framework as intended. A btn class describes that it will add button-like styles to a HTML element. The class should be added to a <a> or <button> element, but there are no restrictions to stop a developer from adding the class to a <span>. Reusing the class for consistent styling seems like a good idea, but the semantic, descriptive naming has suddenly lost its meaning.

Semantic CSS naming is hard

Whether you are creating bespoke CSS or a framework component, naming is hard. Semantic class names, descriptive of its role or use, are much harder to name as websites grow, with lots of components starting to share similar design ideas and characteristics… assuming the name you come up with hasn’t already been used.

Loss of knowledge

It is possible, especially for inexperienced CSS developers, to lose knowledge of the underlying CSS used by a framework. Multiple styles are abstracted behind descriptive class names. Unless you are overriding default styles, or inspecting the generated CSS, there is no need to see what border or padding values are being set for a component.

Risk when making changes

The biggest problem with bespoke CSS and frameworks is the risk when making changes. You must be aware of what will be affected when you make a change. If you add, edit, or delete styles, it can have adverse effects on another template. Content management systems (CMS) and JavaScript plugins might apply classes dynamically, making it harder to search for what classes are being used.

Utility-first CSS

Alternatively, you can use an unopinionated approach, such as utility-first CSS, which removes the problems caused by using bespoke CSS or frameworks.

Utility-first CSS frameworks give a constrained set of primitive classes, called utilities, that are not named semantically and usually apply a single style, such as margin-bottom. Using a utility-first CSS framework, such as Tailwind CSS, lets you customise and access pre-existing utility classes and apply them directly to your HTML.

In fact, most CSS frameworks already have some utility classes in their systems. Bootstrap has a large list of utility classes to let you add styles alongside components, including:

  • Borders: weight, colour, and radius
  • Colours: text, backgrounds, and gradients
  • Display: inline, block, flex, and hidden
  • Position: static, relative, absolute, fixed, and sticky
  • Spacing: margin and padding
  • Text: alignment, wrapping, font weight, decoration, and transform
  • …etc.

If you have used Bootstrap, you may have used its margin utility classes, such as mb-{x}:

.mb-2 {
  margin-bottom: 5rem;

Getting started with utility classes

Let’s create a card component with bespoke CSS. The card design (see image below) must have:

  • A white background, slightly rounded corners, and a subtle shadow
  • An image that stretches full width, but keep its ratio
  • A padded content section with a title, date, and summary
  • A link with padding, ideal for touch interactions

A design for the component card to be created

HTML markup

With bespoke CSS, you need to think of the names for your classes. To reduce the risk of breaking your design if you change the element later, avoid using HTML elements in your CSS selectors. The HTML snippet below is an example of good, semantic markup with some appropriately named classes to style the component:

<article class=”media-card”>
  <img class=”media-card-image” src=”” alt=”Sad dog” />
  <div class=”media-card-body”>
    <h2 class=”media-card-title”>Lorem Ipsum Dolar Sit Amet</h2>
    <p class=”media-card-meta”>23 May 2020</p>
    <p class=”media-card-summary”>Sed ut perspiciatis…</p>
  <p class=”media-card-footer”>
    <a class=”media-card-footer-link” href=”#”>Read article</a>

Bespoke CSS

First, let’s see how you could style the card component using bespoke CSS:

.media-card {
  background: #ffffff;
  border-radius: 0.25rem;
  box-shadow: 0 1px 3px 0 rgba(0,0,0,0.1);
  overflow: hidden;
.media-card-body {
  padding: 1rem 1.25rem;
.media-card-title {
  font-size: 1.125rem;
  font-weight: 700;
.media-card-meta {
  color: #a0aec0;
  font-size: 0.875rem;
  margin-top: 0.25rem;
.media-card-summary {
  color: #4a5568;
  margin-top: 0.5rem;
.media-card-footer {
  border-top: 1px solid #e2e8f0;
  margin-top: 0.75rem;
.media-card-footer-link {
  color: #4299e1;
  display: block;
  font-weight: 600;
  padding: 0.75rem 1.25rem;

Additional styles may be needed to override any browser styles or reset stylesheets, but the media-card component is now ready to be used across multiple templates. Brilliant.

However, there are some questions about re-usability, maintenance, and expansion with the CSS snippet above:

  • Is the name media-card appropriate for the class? Should it be named article-card instead? If so, will that make it less reusable?
  • If you have a version without an image, is it still a media-card?
  • What should you name this class if media-card already exists? Should you try to start modifying the existing class to work for this example?
  • What if you want to add an icon to the title, or show the author name and avatar? Now you must edit your CSS to create the new styles for a media-card-title-icon and media-card-author (plus other child classes)

The core problem with this approach is style duplication. In the CSS snippet, the media-card-body and media-card-footer-link both use the same padding left and right values. What if the media-card styles for background and shadow are also widely used on your website? Maybe you have tweet-card and menu-card components that use those styles. The margin top of the media-card-summary may also be a common spacing pattern in your design. You could set these values as CSS properties or SASS variables to stop you repeatedly writing values out if you want to make changes. But you are still duplicating the styles in your CSS, which increases file size.

Utility-first CSS with Tailwind CSS

With Tailwind CSS, reusing utility classes removes style duplication. Your productivity is increased as you no longer need to worry about naming classes or editing styles. The risk when making design changes is gone as you change the classes added to HTML elements and you never edit the CSS.

Tailwind CSS gives you lots of utility classes to use, including margin, padding, and font-weight. Let's update the HTML to use Tailwind CSS utility classes instead:

<article class=”bg-white overflow-hidden rounded shadow”>
  <img class=”block” src=”” alt=”Sad dog” />
  <div class=”px-5 py-4”>
    <h2 class=”text-lg font-bold”>Lorem Ipsum Dolar Sit Amet</h2>
    <p class=”mt-1 text-gray-500 text-sm”>23 May 2020</p>
    <p class=”mt-2 text-gray-700”>Sed ut perspiciatis…</p>
  <p class=”mt-3 border-t”>
    <a class=”block px-5 py-3 font-semibold text-blue-500” href=”#”>Read article</a>

The padding classes have been reused for the card body and the card footer link. If you also wanted to apply the same background (bg-white), rounded corners (rounded), and shadow to other elements in your design, your CSS would only need to be aware of those three classes used once:

.bg-white {
  background: #ffffff;
.rounded {
  border-radius: 0.25rem;
.shadow {
  box-shadow: 0 1px 3px 0 rgba(0,0,0,0.1);

With utility-first CSS, you still learn and maintain a deep understanding of how to write CSS in a traditional, bespoke way… but you no longer need to worry about writing it.

About the author

Related links