🌈 Designing my own opinionated CSS library on top of TailwindCSS v4

Introducing AltCSS

XQ
The Research Nest
7 min readJun 9, 2024

--

Today, there are a lot of options to pick from (in no preferred order):

Some new and upcoming options include Sugar CSS, and for specific niches, we have libraries like Animate CSS or Pattern CSS, but nothing fits well with me.

Over the last few years, I had problems working with CSS in various contexts.

  1. I rarely use even 50% of the classes these CSS libraries provide. When I look at the documentation, I see that it is filled with so many things that are irrelevant to me, bloated, and clumsy. Most people do not use most of the features and classes provided in most use cases. It doesn’t affect performance, as unused classes are removed anyway, but it doesn’t feel light with development.
  2. I often end up overwriting the CSS or creating my own classes on top of the available ones because, let’s face it — no library is perfect for our own unique contexts. Over time, I see a lot of repetition of work across my projects.
  3. I do not like using multiple CSS classes, even for simple things. If you look at Tailwind CSS, you’d almost always have six or even seven classes clamped together to one element. The class names can quickly get too long — clumsy to write, remember, read, and maintain. Otherwise, you have to put wrapper classes on top of them.

To solve the above issues, I created an opinionated CSS library that works well for me — and not necessarily for everyone else. In short, this library will probably be unusable or follow practices that don’t align with many people. Henceforth, I decided to call this library — AltCSS.

My goals with AltCSS are as follows:

  1. Opinionated styling with 80% fewer CSS classes than a normal library but catering to 80% (or more) of the most common use cases.
  2. Lightweight, optimized, and smaller in size.

The next parts of this article are about the step-by-step process I followed to build this library.

Skip to the end, if you are not interested in the design thoughts and development process and want to explore the final end result.

Step 1: Most commonly used CSS

I narrowed it down to the most commonly used CSS styles online. For this process, I considered four specific types of websites that I often work with (or plan to in the future). For each type, I identified the common styling requirements for my context.

  1. Landing pages
    — Images
    — Buttons
    — Signup/Email forms
  2. Portfolio websites
    —Grid
    — Card
  3. Blogs and documentation sites
    — Typography
    — Text layouts
    — Links
    — Nav
  4. Single-page web apps
    — Form elements like input, radio, checkbox, etc.

For the MVP version, I only focus on the styles that I use actively.

Most CSS libraries adhere to some design system, but I do not conform to anything specific. I kept things organic and simple.

So, do I build everything from scratch?

Initially, I was going in that direction, but around the same time, TailwindCSS open-sourced their progress on their next major release, Version 4.

It just clicked to me that I should use that as a base and build on top of it — like a nice little abstraction packaged in a compact and easy-to-use manner. Tailwind was not built to be used like this, but let me try it out anyway.

Apart from TailwindCSS alpha version 4, I used Bun as my package manager.

Step 2: Naming conventions

I want to keep things as simple, shorthand, and native as possible. Here are the choices made.

  1. All native HTML tags will have Tailwind CSS styles that are inherently applied.
  2. In specific cases when I do have to create a custom class, they will follow the nomenclature a-name with an emphasis on trying to keep it as short as possible.

Step 3: Write the CSS

First, I created an empty project npm init and the file structure I wanted.

I have a few core design principles from a user perspective:

  • Don’t write class names
  • Don’t write CSS
  • Keep it simple
  • Keep it clean

I started directly with dark mode, as that’s my first preference. As much as possible, I tried to style the native HTML elements without creating new class names. This can be tricky with components like cards and grids. I used a nesting approach where native elements are nested in particular ways to create these components. Once all the docs are ready, I will share more examples of such things.

For the MVP, I implemented basic CSS for headings, links, cards, and grid layout. I ensured that all of them were responsive to standard screen sizes.

For example, this is what my typography styles look like.

/* Typography */
/* Headings */
h1, h2, h3 {
@apply text-gray-400 tracking-tight;
}

h1 {
@apply text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold my-6;
}

h2 {
@apply text-3xl sm:text-4xl md:text-5xl lg:text-6xl font-semibold my-5;
}

h3 {
@apply text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-medium my-4;
}

h4 {
@apply text-lg text-yellow-400 sm:text-xl md:text-2xl lg:text-3xl font-medium my-3 tracking-tight;
}

/* Text */
p {
@apply text-lg sm:text-xl md:text-2xl text-gray-500 my-3 leading-relaxed tracking-wide;
}

/* Links */
a {
@apply text-yellow-400 relative transition-colors duration-200 ease-in-out no-underline;
}

a::after {
content: "";
position: absolute;
left: 0;
bottom: -2px;
width: 100%;
height: 2px;
background-color: currentColor;
transform: scaleX(0);
transform-origin: bottom right;
transition: transform 0.3s ease;
}

a:hover::after {
transform: scaleX(1);
transform-origin: bottom left;
}

Step 4: Minifying the CSS

Once the code is ready, it is time to create a minified version of it into a single file ( alt.min.css ). I used the alpha version of Tailwind Cli for this.

Here’s my package.json for reference.

{
"dependencies": {
"@tailwindcss/cli": "^4.0.0-alpha.16",
"tailwindcss": "^4.0.0-alpha.16"
},
"scripts": {
"build": "bunx @tailwindcss/cli@next -i app.css -o dist/alt.min.css --minify"
},
"devDependencies": {
"prettier": "^3.3.1",
"prettier-plugin-tailwindcss": "^0.6.1"
}
}

app.css includes all the CSS files imported from my src folder.

bun run build does the magic of creating the minified file as per the above script.

There are a few additional things to do in this step. The minified file now includes all the tailwind classes and variables, apart from the ones I created. The goal is to filter them out so that only the elements and variables I used in my design are in the final minified file. I’ll leave this enhancement for future updates.

Step 5: Testing

Once the minified file was ready, I created some example pages to test everything. After that, it was time to publish this as an npm package to make it easy for others to install and include in their projects.

Step 6: NPM Publish

It’s actually quite straightforward to publish our code as an npm package. I created a new package.json inside my dist folder where the alt.min.css file is saved.

This file includes all the details required to publish the npm package and the files I want to push to the final package. In my case, I wanted to push the dist folder, which is the final output. Here’s my dist package.json.

{
"name": "altcss",
"version": "0.0.6",
"description": "Opinionated, unconventional, lightweight CSS component library.",
"main": "alt.min.css",
"files": [
"alt.min.css",
"README.md"
],
"keywords": [
"css",
"library",
"altcss",
"tailwindcss"
],
"author": "aditya-xq",
"license": "MIT"
}
cd dist
npm login
npm publish

That’s it!

The Final Result

The MVP is all set and ready to be used and experimented with.

You can directly add altcss into your project using any of the below commands.

npm install altcss
pnpm add altcss
bun add altcss

Then, you can import the required file into your main script or global styles. For example, in Sveltekit, you should import it under the script tag of +layout.svelte file.

import 'altcss/alt.min.css';

Write native HTML and enjoy the styling that’s automatically applied.

Here’s a simple demo page built with AltCSS.

Here’s the GitHub repo for this project.

And finally, here’s another page I built using AltCSS.

End notes

This is just an MVP with a few components and styles implemented. I plan to use AltCSS as a standard CSS framework/library for most projects. When I need to implement a specific style or component, I will add it to the library.

Here’s a general roadmap I have in mind:

  1. Complete the design for all the remaining shortlisted HTML tags.
  2. Clean and filter the minified CSS to include only the styles I actually customized.
  3. Implement a proper light and dark mode.
  4. Create a “CSS Design Builder” to help people customize AltCSS to their design preferences via a nice user interface and export the minified CSS for their projects.

Until next time.

--

--

XQ
The Research Nest

Exploring tech, life, and careers through content.