A Comparison of Lightweight CSS Frameworks by Shipped Bytes and Missing Features

A comparison of lightweight CSS frameworks by shipped bytes, missing features, and when the omission is the point.

The Lightweight CSS Frameworks Comparison Table

FrameworkGzip SizeApproachBaselineCustom Property ThemingSPCSS1.9 KBClasslessBaseline Widely AvailableYesSakura1.8 KBClasslessBaseline Widely AvailableYesWater.css2.0 KBClasslessBaseline 2022YesMVP.css2.4 KBClasslessBaseline Widely AvailableYesMilligram2.0 KBClass-basedBaseline Widely AvailablePartialChota3.0 KBClass-basedBaseline Widely AvailableYesPure.css3.8 KBModularBaseline Widely AvailableNoPico.css4.8 KBClasslessBaseline Widely AvailableYes

That table is the whole pitch. Every tool under 5 KB gzip serves a project where the page itself is the product, not the chrome around it.

What the Classless CSS Frameworks Actually Ship

Open any of these files and you will find the same architecture. A classless stylesheet targets semantic HTML elements directly. No utility classes. No component names. No grid systems. The selector list is a list of every tag you already know: h1, p, a, blockquote, table, form, input. That is the entire point. You write a document with correct markup, and the stylesheet makes it look like a design system touched it.

The Pico.css minimal CSS size is 4.8 KB gzip, larger than most peers because it ships more opinionated defaults for forms, buttons, and tables. It still styles raw semantic HTML without a single class name, but it also includes a subtle grid and a set of spacing defaults that the truly minimal tools omit. The Water.css classless library is 2.0 KB gzip, and it does less. It sets a readable type scale, handles form controls, and gets out of the way. The MVP.css semantic HTML styling sits at 2.4 KB gzip and adds a single class you can use to opt into a wider container, a small concession to layout control.

What you are paying for in that byte difference is the breadth of the reset and the opinion on spacing. A 1.8 KB file like Sakura assumes you want a clean typographic baseline. A 4.8 KB file like Pico.css assumes you also want form controls that look intentional and a set of component-like patterns for cards and alerts. Both are the same species of animal. One eats more.

When the Classless Approach Fails

Here is the failure case. You add the stylesheet, and your page looks wrong because the browser has a default margin on body or because your custom stylesheet is loaded first and the library rules override them. The fix is not to abandon it. Load the library first and your custom stylesheet second. Use @layer to put the library in a lower cascade layer so your author styles win by priority, not by accident.

Water.css Classless CSS Framework: When 2 KB is the Whole Point

The Water.css classless library is the purest expression of the idea. It ships at 2.0 KB gzip, requires no build step, and styles a document that uses nothing but semantic HTML. The library applies its rules with type selectors and attribute selectors, never class names. Drop it onto a page generated by a static site generator that has never heard of it and the result will look like a deliberate design choice.

The trade-off starts the moment you need to deviate from the default. Water.css exposes a small set of custom properties for theming, but the theming surface is shallow. You can change the accent color, the background, the text color, and the border radius. You cannot restructure the layout. There is no grid, no flexbox utility, no way to make a two-column card layout without writing your own CSS.

What Water.css Answers

The question this library answers is: do you need a design system for a document? If the answer is yes, this is not it. If the answer is no, and you need a blog post, a documentation page, or a project readme to not look like a default browser tab, this is the correct tool. It uses :is() to keep specificity low, so any author stylesheet that comes after it in the cascade will override it without a fight.

The failure case here is expecting the library to style content inside a Shadow DOM without importing the stylesheet into each shadow root. The library rules do not pierce the shadow boundary. If you are building a web component, you must explicitly load the library inside the shadow root or accept that the component will be unstyled.

MVP.css Semantic HTML Styling: the 2.4 KB Reset That Looks Like a Design

The MVP.css semantic HTML styling approach is almost identical to Water.css in ambition but different in execution. It ships at 2.4 KB gzip. Its defining move is the addition of a single class, container, which constrains the content width. That one concession to layout control makes MVP.css feel more useful than Water.css for a simple landing page or a project site.

MVP.css sets a comfortable reading measure, styles all form controls, and provides a reasonable set of default spacing. It does not ship a grid system, and it does not try to be a component library. The value proposition: you can write a complete, accessible, semantic HTML page and have it look like a professional designed it, without writing a line of CSS yourself.

The Cost of the Convenience

The cost is in the theming. MVP.css exposes custom properties for the core colors and the font stack, but it does not expose a design token layer for spacing, typographic scale, or border radii. You can change the accent color. You cannot change the rhythm of the page without overriding individual element rules.

Who should not use MVP.css? Anyone who needs to ship a custom-looking product, anyone who needs a component system with states and variants, or anyone who cannot live with a fixed width container. If your project needs utility classes, this is not the tool. If your project needs a library that does not get in the way of a bespoke design, this is also not the tool. The tool is for the project where the content is the interface and the design is the default.

Lightweight Framework Custom Property Theming: the Real Power of Pico.css and Its Peers

The lightweight custom property theming is where the classless and class-based tools separate from the old guard. A custom property, also known as a CSS variable, is a user-defined property that inherits and cascades, enabling runtime theming and component-scoped tokens. The libraries in this comparison use them heavily. Override a single variable at the root level and the entire look of a site changes without touching a single rule.

Pico.css is the most aggressive user of this pattern. It ships with a rich set of custom properties for colors, spacing, borders, and typography. Override five of them and you have a different theme. Override fifty and you have a different design system. The library uses :where() to keep specificity at zero, which means your custom properties will always win if they are defined later in the cascade or on a closer ancestor.

A Concrete Example

You import Pico.css, and the default theme is a blue accent on a white background. You want a green accent on a dark background. You do not edit the library. You do not write a single override. You define your own values for the library's custom properties on the :root element, and the entire component set updates.

:root {
  --pico-primary: #00aa00;
  --pico-background-color: #1a1a1a;
  --pico-color: #e0e0e0;
}

That is the entire theming story. No !important, no specificity battles, no build step. The library's rules reference the variables, and your values flow through.

The Cascade Order Failure

The failure case is cascade order. If you import the library, then define your overrides, then import a component stylesheet that references the library's original variables, your overrides lose. The fix is to define your theme before the component stylesheet, or to put the library in a lower cascade layer using @layer so that unlayered author styles always win.

The Missing Layout System and What You Do Without it

Every library in this comparison ships a layout system that is either absent or one-dimensional. The classless ones give you a single column of content at a constrained width. The class-based ones like Chota give you a 12-column grid, but that grid assumes you will write grid classes in your HTML, which immediately makes your markup heavier and less semantic.

The Pico.css minimal CSS size includes a rudimentary grid, but it is not a replacement for a full grid system. It gives you a way to split a row into columns. It does not handle complex alignment, nested grids, or the kind of component-internal layout that modern interfaces demand. That is the trade-off. You save 20 KB by not shipping a grid system, and in exchange you write your own layout CSS using native CSS features.

Filling the Gap with Native CSS

What fills the gap is flexbox and CSS Grid. Both are Baseline Widely Available and have been for years. A display: grid declaration with grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)) replaces an entire column of a utility library. A display: flex on a row with gap replaces half a dozen utility classes.

.product-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 1rem;
}

.product-list > * {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}

The gap property in flexbox shipped later than grid gap, but it is now Baseline Widely Available. Use it in both contexts without a fallback. This is the modern way to handle layout, and it is the reason the lightweight libraries work for production projects if you are willing to write your own layout rules.

The Missing Component Library and the Cost of Not Having One

The biggest omission in the lightweight libraries is a component library. There are no accordions, no modals, no tabs, no dropdowns, no tooltips, no date pickers, and no autocomplete widgets. The libraries style the elements that exist in HTML by default. They do not implement any of the interactive patterns that a modern web application needs.

That is the point. A component library like Bootstrap or Tailwind UI ships at 20 KB gzip or more, and it carries a set of JavaScript dependencies for the interactive pieces. The lightweight library ships nothing, because the lightweight library assumes you will build the interactive pieces yourself, or that you do not need them.

When the Omission Matters

If the page is a document, a blog post, or a simple marketing site, the missing component library is irrelevant. If the page is a web application with a dashboard, a settings panel, or a complex form, the missing component library is a dealbreaker. You will spend more time building a modal that does not break on mobile than you saved by choosing the 2 KB library.

What fills the gap is either a dedicated component library with its own CSS, or a hand-rolled set of components using custom properties and @supports to conditionally load behavior. The latter is more work but keeps the total shipped bytes low. The former is faster to build but adds weight back.

The Missing Utility Set and What You Write Instead

The second biggest omission is a utility class set. Tailwind popularized the pattern of text-center, p-4, flex, and items-center as a way to build interfaces without leaving the HTML. Lightweight libraries ship none of that. They give you element-level defaults, and they expect you to handle any deviation with your own stylesheet.

Most of what people use utility classes for is spacing, typography, and display behavior. You can write those rules in your own CSS in a few lines. Because you only write the ones you need, the total size is often smaller than a utility library that ships every possible combination.

.mt-0 { margin-top: 0; }
.mt-4 { margin-top: 1rem; }
.text-center { text-align: center; }
.flex { display: flex; }
.items-center { align-items: center; }

The trade-off is development speed, not the final CSS size. Writing class="mt-4" is faster than opening a stylesheet, writing a class, and giving it a name. But the lightweight libraries are not designed for rapid prototyping of bespoke layouts. They are designed for shipping a document with the least possible overhead. For that purpose, a utility set is dead weight.

Who Should Not Use a Lightweight Framework

If your project demands a comprehensive component library, skip the lightweight tools. The Water.css classless library, the MVP.css semantic HTML styling, and the rest are the wrong tools for an application that needs a date picker, a modal, or a select menu with search. You will spend more time building the missing pieces than you saved in bytes.

If you cannot add custom properties for theming, skip them as well. Every library in this comparison uses custom properties as the theming hook. If your project targets a browser from before 2017, or if you have a corporate standard that forbids CSS variables, you cannot use these libraries. The fallback is to serve a pre-compiled static CSS build with variables resolved, or to use @supports (--custom: 0) to load the library only when custom properties are supported.

If you are building a design system that needs precise control over every state, every variant, and every interaction, the lightweight libraries are a starting point, not a destination. They give you a clean slate of element defaults. They do not give you the vocabulary to express a disabled button, a selected tab, or a loading state. That vocabulary has to come from your own CSS.

The failure case is trying to force a classless library into a component-driven architecture. You end up writing your own component classes, fighting the library's element-level selectors, and using !important to override the cascade. That is a worse outcome than not using the library at all.

The Class-Based Lightweights: Chota, Milligram, and the Hybrid Approach

Not every lightweight tool is classless. Chota ships at 3.0 KB gzip and offers a 12-column grid, a set of utility classes for spacing and display, and a handful of component classes for buttons and badges. It also styles semantic HTML directly when you do not add a class, a pragmatic middle ground for a site that needs a bit of layout control without a full component library.

Milligram at 2.0 KB gzip is class-based in a different way. It provides the bare minimum of component classes for buttons, forms, and lists, but it does not ship a grid system. You are expected to use flexbox for layout, which is fine, but it means the library is less of a complete solution than Chota.

The Hidden Weight of Classes

These hybrid tools are useful when you need the layout control of a grid but do not want to write CSS Grid rules yourself. The cost is that you are back to putting classes in your HTML, which increases your markup size and makes the page less semantic. The shipped bytes comparison is not about the CSS file alone. It is about the total weight of the HTML, the CSS, and the JavaScript. A class-heavy HTML file can outweigh the bytes you saved on the CSS.

The common technique these replace is hand-writing a reset/normalize stylesheet plus custom element styling. That was the old way, and it involved a lot of repetition. The libraries encode the repetition into a single file, but they also impose their own opinion on what a button or a form control should look like.

The Failure Mode: Double Layout and Cascade Order Bugs

Two mistakes come up repeatedly with these tools. The first is using a library grid class inside a CSS Grid or flex container without disabling the library's grid styles. You end up with a double layout, where the library tries to lay out the page the way its grid expects, and you try to lay it out the way your container expects. The result is unpredictable. Isolate the library's grid to a single level of the DOM.

The second mistake is overriding library variables after the library import but before component imports. The cascade order works against you because the component imports come later and reference the original variable values. Set your overrides after all imports, or use @layer to control the priority of the cascade.

@layer framework, base, components;
@import url("pico.css") layer(framework);
@import url("my-theme.css") layer(base);
@import url("my-components.css") layer(components);

The @layer declaration at the top says that the framework layer is first, the base layer is second, and the components layer is third. Any rule in a later layer wins over an earlier layer regardless of specificity. This is the modern way to manage cascade order, and it is the answer to the most common source of bugs.

How to Choose: the 5-Question Decision for Your Project

Ask yourself these five questions before you pick a library. The choice becomes obvious.

  • Does the page need to load fast on a slow connection? If yes, a 2 KB classless library is the right choice. The Pico.css minimal CSS size is still under 5 KB, but it is heavier than the alternatives.
  • Does the page need a grid system? If you are building a dashboard or a magazine layout, you need classes. Chota gives you a grid at 3 KB. If you are writing a document, you do not need a grid.
  • Do I need to theme this per-client or per-brand? If yes, custom properties are non-negotiable. Every tool here uses them, but the depth of theming varies. Pico.css and Chota expose the most surface area.
  • Am I building a component library? If yes, stop looking at lightweight tools. You need the full control of a utility-first tool or a full component library. The lightweight tools are for the content, not the chrome.
  • What is the fallback if the library's CSS does not load? If you have not written a fallback, the page will look like a default browser page. For a classless library, that is acceptable. For a class-based one, the layout will break. Plan for the failure.

The lightweight CSS comparison is only useful if you know what you are optimizing for. If you are optimizing for speed and simplicity, the classless tools are the answer. If you are optimizing for development speed with a custom design, you are better off with a utility library and a build step.

The Performance Reality: Gzip, Brotli, and What the Bytes Mean

The shipped bytes number is not the raw file size. It is the size after gzip compression or brotli compression, and the difference matters. A 4.8 KB gzip file might be 12 KB uncompressed. The gzip algorithm exploits the repetition in the CSS. Class names that repeat, values that repeat, and rules that share a selector all compress to a fraction of their original size.

This is why utility classes compress so well, and why a utility library can be 10 KB gzip even though the raw CSS is 50 KB. The repetition is the compression. The same logic applies in reverse. A classless library like Water.css might have little repetition because each rule is unique, but the file is so small that the difference is negligible.

Measure with Brotli

Always measure with brotli, not gzip. Brotli is the standard for HTTP/2 and it is supported in all modern browsers. The Baseline status for brotli is Widely Available, so you can rely on it for your content negotiation.

The trade-off is not how many bytes you ship. It is how many bytes your users need to download before they see a flash of unstyled content. A 2 KB CSS file loads in a single round trip on a slow connection. A 50 KB CSS file might take three or four round trips. For a reader on a mobile device, that difference is the difference between reading the article and giving up.

The Build Step Question: Why Classless Frameworks Win on Simplicity

Most of these tools require no build step. Download the CSS file, link to it in your HTML, and you are done. No PostCSS. No Babel. No Sass. No tree-shaking. No dead code elimination. The library is the file, and the file is the library.

This is a feature, not a limitation. A build step introduces a failure point. The build can break, the dependencies can conflict, and the output can be different from the source. A classless library eliminates that entire class of problems. Open the CSS file in a text editor and understand every rule in it.

The Trade-Off

The trade-off is that you cannot opt out of the parts you do not use. With a build step and a utility library, you can tree-shake the unused classes and ship only the CSS you actually use. With a classless library, you get the whole file, even if you only use half of it. That is a fair trade for not having to maintain a build pipeline.

If you need a build step for other reasons, like transpiling modern CSS features your deployment target does not support, you can still use a classless library. Run it through your pipeline like any other CSS file. The library does not care.

The Modern CSS That Makes Them Work: :where(), ::selection, and More

Lightweight libraries rely on modern CSS features to keep their specificity low and their file size small. The most important is :where(), which always has zero specificity. A rule like :where(h1, h2, h3) { font-weight: bold; } has a specificity of zero. Any other rule in the cascade can override it without needing a higher specificity selector.

This is what allows the classless libraries to be overridden so easily. Your custom stylesheet, loaded after the library, will win the cascade as long as it also has a specificity of zero or higher. The library is not fighting you because it uses :where() to keep its own specificity at zero.

Dynamic Color with color-mix()

Another feature is color-mix(), which allows the library to generate shades of a color without pre-computing them. If Pico.css wants a hover state that is 10% darker than the base color, it can use color-mix(in srgb, #000, 10%, var(--pico-primary)) to get there. This keeps the file small because the library does not need to define the hover color as a separate variable.

The Baseline status for color-mix() is 2023. It is available in all modern browsers but not in older ones. If you need to support a browser that does not have it, you need a fallback. The safest fallback is to define the hover color as a static value alongside the color-mix() declaration. The browser will use the static value if it does not understand the function.

The Fallback Strategy: When Custom Properties Are Not There

Every library in this comparison uses custom properties for theming. What if the browser does not support them? The Baseline status for custom properties is Widely Available since 2017. Any browser made in the last decade supports them. If you have a user on an ancient browser or a strict enterprise environment, you need a fallback.

The first fallback is to serve a pre-compiled static CSS build with variables resolved. Run the library through a preprocessor at build time, substituting the variable values with their computed values. The output is a CSS file that does not reference any custom properties. This is a one-time cost, but it is a build step, which the libraries try to avoid.

The second fallback is to use @supports (--custom: 0) to conditionally load the library. Write one stylesheet that loads the library only if the browser supports custom properties, and a second stylesheet that loads a legacy build for everyone else.

@supports (--custom: 0) {
  @import url("framework.css");
}
@supports not (--custom: 0) {
  @import url("framework-legacy.css");
}

The legacy build is the same library with the variables manually resolved. It is heavier, but it works everywhere. The @supports rule is the modern way to feature-detect and conditionally load, and it is Baseline Widely Available.

The Baseline Status Check: a Quick Reference on What You Can Use

Before you pick a library, check what CSS features it relies on and what the Baseline status is for those features. The Baseline status tells you when a feature is available in all major browsers. It is a reliable indicator of whether you can use a feature today.

Here is what the libraries in this comparison use, and when they became available in the major engines:

  • CSS Custom Properties: Baseline Widely Available since March 2017. Chrome 49, Firefox 31, Safari 9.1, Edge 15. Every modern browser has them.
  • color-mix(): Baseline 2023. Chrome 111, Firefox 113, Safari 16.2. This is newer. The libraries that use it need a fallback for older browsers.
  • :where(): Baseline Widely Available since January 2021. Chrome 88, Firefox 84, Safari 14. This is the selector that keeps specificity low.
  • :is(): Baseline Widely Available since January 2021. Chrome 88, Firefox 78, Safari 14. Similar to :where() but with the specificity of the most specific argument.
  • Flexbox and Grid: Baseline Widely Available. Flexbox has been around since 2015, and Grid since 2020. Both are safe to use.

The Baseline status is not a guarantee that a feature will work in a specific browser. It is a guarantee that a feature works in the latest versions of the major browsers. If you are supporting a browser that is not in the Baseline list, do your own testing.

The Real-World Interop Gaps: What Ships vs What Works Everywhere

A feature in the Baseline list does not mean it behaves the same way in every browser. The interop gap is the difference between the features that browsers support and the features that browsers support consistently.

For example, container queries are Baseline Widely Available since 2023, but the container units (cqw, cqh) have interop bugs in older versions of Safari that shipped container queries. The feature is there, but the units do not always work as expected. Test in Safari, or avoid the units and use a fallback.

The gap property in flexbox is another example. It shipped in Grid first, then in Flexbox later. If you use gap in a flex container, make sure the browser supports it. The Baseline status for gap in flexbox is Widely Available since 2021, but older browsers will silently ignore it.

Cascade Layers: Order Matters

Cascade layers are Baseline Widely Available since 2022, but the ordering can be tricky if you mix layered and unlayered styles. The @layer rule declares the priority order. A rule in a later layer wins over an earlier one, regardless of specificity. If you do not declare the order, the browser uses a default order. The mistake is assuming that @layer framework, base; means the framework wins. It does not. It means the layer order is framework, then base, and base wins.

These are the kinds of gaps that a working front-end developer needs to know. They are the difference between a page that looks correct and a page that has a subtle layout bug in a specific browser.

The Accessibility Angle: What the Frameworks Handle and What They Miss

The classless libraries do a decent job of styling semantic HTML, which means they inherit the accessibility of the markup. Write a button or an input type="checkbox", and the browser provides the native behavior. The library styles it. But the libraries do not add ARIA roles, they do not manage focus states, and they do not handle keyboard navigation for custom interactive elements.

The accessibility tree is affected by CSS in ways that are easy to miss. display: none removes an element from the accessibility tree. visibility: hidden does the same but keeps it in the layout. The content property generates text that may or may not be exposed to assistive technology, depending on the browser and the screen reader.

The libraries in this comparison are not designed to be accessibility tools. They are designed to style documents. If you need to build an accessible component, write the ARIA and the keyboard handling yourself. The library will not do it for you.

Test your pages with a screen reader and a keyboard. The library handles the visual styling. It will not fix a focus trap or a missing label. Those are your responsibility.

The Viewport and the Initial Containing Block: What the Frameworks Assume

Every library in this comparison assumes a viewport of at least 320 pixels and makes some assumptions about the initial containing block. The viewport is the initial containing block. Percentage heights resolve against its computed height. If the viewport is 100vh, a percentage height on a child resolves against that 100vh.

The libraries set a default width to 100% on the body and a default min-height to 100vh on the html element. That is why a page with very little content still fills the screen. The body element does not have a height by default, so the library sets it to a minimum of the viewport height.

The Mobile Toolbar Problem

The problem is a mobile browser with a dynamic toolbar. The 100vh unit includes the area behind the browser chrome, which means the bottom of the page can be cut off. The fix is to use the svh (small viewport height) or dvh (dynamic viewport height) unit instead. The libraries in this comparison use vh by default, the safest choice for compatibility, but it can be wrong on mobile.

If you need to support the dynamic viewport, write a @supports rule to override the library's default.

@supports (height: 100dvh) {
  html {
    min-height: 100dvh;
  }
}

This is a subtle but important detail. The libraries are designed to work out of the box. They cannot predict every device.

The FAQ: What People Actually Ask About Lightweight CSS Frameworks

What is the difference between a classless library and a class-based one? A classless library styles semantic HTML elements directly, with no class names in the markup. A class-based library requires you to add classes like btn or row to get the styles. The classless approach is simpler and produces cleaner HTML, but it offers less control.

What is the largest lightweight CSS library? Pico.css is the largest in this comparison at 4.8 KB gzip. It is still under the 10 KB threshold, but it is on the heavier end because it ships more opinionated styles for forms and buttons.

Can I use a lightweight library with a JavaScript framework like React or Vue? Yes, but be careful. The library's styles will apply to any element that matches its selectors, including those rendered by a JavaScript framework. Import the stylesheet at the top level, and it will work. If you are using a component library that injects its own styles, you may need to use @layer to control the cascade order.

What is the best lightweight CSS library for a blog? The best is the one that ships the least CSS. Water.css and MVP.css are both excellent choices because they are tiny and style a blog post beautifully out of the box. Pico.css is a good choice if you want a bit more control over the theme.

What should I do if I need a feature the library does not have? Write it yourself. The libraries are designed to be extended. Add a custom stylesheet that defines the missing component in terms of the library's custom properties. Use @layer to make sure your styles are not overridden.

The Failure Case: What You Do When the Framework is Not Enough

The libraries are small, and they are intentionally limited. When you hit the limit, you have two options. The first is to write custom CSS. This is the expected path. The library gives you a baseline, and you extend it with your own rules. You are not fighting the library because the library has no opinion about your specific layout.

The second option is to use a different tool. If you are writing custom CSS for every component, the library is not saving you any time. The Pico.css minimal CSS size is not worth the trouble if you are going to override every single rule. A utility library like Tailwind might be a better fit, even though it is heavier.

The Worst-of-Both-Worlds Failure

The failure case is when you combine the worst of both worlds. You use a classless library, then you add utility classes to it, then you write custom component styles, and then you have no idea which rule is causing the bug. Pick one approach and stick with it.

The other failure case is when you expect the library to handle interactive components. The lightweight libraries do not ship JavaScript. No modals. No dropdowns. No tabs. If you need those, you build them or you use a component library. The library will style the HTML. It will not make it interactive.

The Final Word: Who This is for and Who Should Skip it

The lightweight CSS comparison is a story about trade-offs. You trade the convenience of a component library and a utility set for the speed of a single small file. You trade the ability to customize every pixel for a design system that works out of the box. You trade a build step for simplicity.

This subject suits the developer who is shipping a document, a blog, a marketing page, or a simple project site. It suits the full-stack engineer who wants a page to look decent without writing a stylesheet. It suits the technical writer who needs a clean canvas for a tutorial. It does not suit the developer building a complex web application with dozens of unique components. It does not suit the team that needs a consistent design system across a large product. It does not suit the person who cannot write CSS, because eventually the library will not be enough.

The Water.css classless library is the purest expression of the idea. The MVP.css semantic HTML styling is the most pragmatic. The Pico.css minimal CSS size is the most feature-rich. All of them are tools, and like any tool, they work best when you know what you are building.

If you are a traveler on a budget, this is the budget airline. It gets you there, but do not expect a meal. If you want a guided tour, buy the package. The full frameworks are the package. The lightweight tools are the backpacking hostel that costs $5 a night. You will not get a private bathroom, but you will have enough money left over to eat.