The framework question is really two questions: do you want components handed to you, or do you want a system for writing your own? Every option below sits somewhere on that line, and picking the wrong end of it is why teams end up fighting their CSS.
Utility classes in markup, with the stylesheet generated from what you actually used. Version 4 moved configuration into CSS itself — you write @theme { --color-brand: #ff5f1f; } in your stylesheet rather than maintaining a JavaScript config file — and rebuilt the engine in Rust, so full builds run in the low milliseconds. A typical marketing site ships 8–15 KB of CSS after purging; a large app with many one-off states lands somewhere in the 30–60 KB range.
The real trade is readability. class="flex items-center gap-3 rounded-lg border border-zinc-800 px-4 py-2" tells you exactly what the element looks like and tells you nothing about what it is. That is fine inside components where markup and styling live in one file, and painful in server-rendered templates that get edited by people who do not know the utility vocabulary. Tailwind requires a build step; there is no meaningful CDN mode for production.
Still the fastest route from nothing to a working admin interface. You get modals, dropdowns, offcanvas panels, form validation styles and a grid, plus a colour-mode system that handles dark mode with a data-bs-theme attribute. The full CSS is around 230 KB uncompressed and roughly 30 KB gzipped, and you can cut that substantially by importing only the Sass partials you use.
The cost is that Bootstrap sites look like Bootstrap sites unless you invest real effort in overriding the Sass variables, and the JavaScript components carry opinions about markup structure you have to live with.
Pure CSS with no JavaScript at all, organised around semantic component classes such as card, navbar and hero. Around 200 KB uncompressed. Because there is no JS, you wire up interactive behaviour yourself — which is either a burden or exactly what you wanted if you are using a framework that already owns interactivity.
Pico styles semantic HTML directly: write <button>, <table>, <nav> and they look reasonable with no classes at all. About 10 KB gzipped for the full build. For documentation, internal tools, prototypes and anything where the content matters more than the branding, this is genuinely the highest ratio of result to effort available. The limit is exactly what you would expect: as soon as you need a design that is not "clean defaults", you are writing normal CSS anyway.
Not a framework — a set of CSS custom properties for spacing, sizes, colours, shadows, easings, gradients and animations. You import it and reference var(--size-3) or var(--shadow-2). It gives you the consistency benefit of a design system with none of the class-naming opinions, works without a build step, and is trivially removable because it is just variables.
A lot of what frameworks used to justify is now native. Grid and flexbox killed the 12-column grid requirement. Custom properties killed the need for Sass just to have variables. clamp() handles fluid type. Container queries let components respond to their own space. Nesting works in browsers without a preprocessor. :has() covers parent-state styling. On a small site, plain CSS with custom properties and a couple of layout primitives is a completely defensible answer and ships less than any framework.
Component library needed and design is not the differentiator: Bootstrap. Design system built in-house and components live in JSX or Svelte files: Tailwind. Content site or docs: Pico or plain CSS with Open Props. Existing large codebase: keep what you have — the migration cost of a framework swap is almost always higher than the annoyance you are trying to escape.
Free tools, guides, and resources across the SPUNK13 network.
Visit spunk.bet400+ Free Tools