DEV Community

Cover image for Utility Classes Won 2026. I'm Still Betting on Bulma.
Alex Smith
Alex Smith Subscriber

Posted on

Utility Classes Won 2026. I'm Still Betting on Bulma.

It's 2026, and the CSS framework debate is Tailwind vs. Tailwind. shadcn/ui or DaisyUI? Keep your v3 config or move to v4's CSS-first setup? Bootstrap isn't in the conversation anymore. It's the thing still running your admin panel.

Utility classes won. I'll say it plainly.

But I think they won this round. The next one goes to the idea they replaced, object-oriented CSS, and Bulma is already standing where that pendulum is headed.

The button test

A primary button in Tailwind:

<button class="inline-flex items-center rounded-md bg-indigo-600 px-4 py-2 text-sm font-semibold text-white shadow-xs hover:bg-indigo-500 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">
  Save
</button>
Enter fullscreen mode Exit fullscreen mode

The same button in Bulma:

<button class="button is-primary">Save</button>
Enter fullscreen mode Exit fullscreen mode

Yes, you'd wrap the Tailwind version in a component. That's my whole point.

Even Tailwind users are rebuilding objects

Back in 2008, Nicole Sullivan's OOCSS laid out a simple idea: separate structure from skin. You build a reusable object (.button), then layer skins on top (.is-primary, .is-large).

Now look at what the Tailwind ecosystem built next:

  • DaisyUI, one of Tailwind's most popular plugins, exists to give you btn btn-primary.
  • shadcn/ui hides its utility strings behind a variant prop. The API everyone actually touches is <Button variant="destructive">.
  • @apply is still there for every team that wants .card back.

That's OOCSS, reinvented in a plugin and a component layer. Bulma's button is-primary is the same idea, with no build step and nothing to wrap.

Modern CSS fixed what pushed us to utilities

Utilities took off because global CSS was painful. We had specificity wars, styles leaking across the app, and no real way to scope anything.

Plain CSS has mostly solved that. Cascade layers, native nesting, :where(), container queries, and custom properties now work in every major browser.

Bulma v1 was rebuilt for that world.

CSS variables everywhere. You can rebrand without a build step:

:root {
  --bulma-primary-h: 262deg;
  --bulma-primary-s: 80%;
  --bulma-primary-l: 50%;
}
Enter fullscreen mode Exit fullscreen mode

Dark mode built in. It follows prefers-color-scheme automatically. You can also force it with data-theme="dark".

Skeleton loaders. A single is-skeleton class turns an element into a loading placeholder.

A smart grid. The new grid and cell classes sit alongside the classic flexbox columns.

Zero JavaScript, verified

Bulma is pure CSS; its npm package doesn't contain a single .js file. Tailwind is also zero in the browser, since it's a build tool with no runtime. The old guard is a different story.

Here's what each framework ships if you use its interactive components (dropdowns, modals, tooltips):

Framework JS shipped (min + gzip) Notes
Bulma 1.0.4 0 KB Pure CSS
Tailwind 4.3 0 KB Build-time only
Bootstrap 5.3.8 ~24 KB Bundle with Popper (~16 KB without)
UIkit 3.25 ~53 KB Plus ~21 KB for the icon set
Foundation 6.9 ~67 KB ~40 KB, plus the jQuery it requires

Measured from the latest npm packages, September 2026. Foundation hasn't shipped a release since 2024.

This matters more every year. Server Components are the default in the Next.js App Router, and every kilobyte of client JavaScript has to justify itself. A stylesheet has nothing to hydrate.

The honest trade-offs

The full build isn't tiny. The all-in bulma.min.css is about 65 KB gzipped, because it ships every color, every shade, every helper, and the dark theme. Bulma's modular Sass fixes that: import only what you use. A realistic app build (buttons, forms, navbar, cards, modal, dropdown, columns, spacing helpers) came in around 36 KB gzipped in my test.

You bring your own interactivity. Bulma styles a modal or a dropdown, but it doesn't open one. In vanilla JS that's a few lines. In React, it's where a component library earns its keep. I got tired of rewriting the same is-active toggles on every project, so I built one: bestax-bulma, a fully typed React library for Bulma v1 (full disclosure: I maintain it).

The ecosystem is smaller. There are fewer themes and fewer plugins, and your AI assistant has seen far more Tailwind than Bulma. The class names are plain enough that it rarely matters.

When I'd still pick Tailwind

Pick Tailwind if your team already thinks in utilities and ships with shadcn/ui; switching costs are real. It's also the better call if you're building a fully bespoke design system where no framework's defaults would survive anyway.

Bootstrap? Only if you're maintaining something that already uses it.

Try it in 30 seconds

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
Enter fullscreen mode Exit fullscreen mode

Drop that into a CodePen, write <button class="button is-primary">, and see how far you get before you miss anything.

Utility classes won this round. The next one goes to CSS that reads like English, and Bulma has been writing it since 2016.

Top comments (0)