For years, CSS frameworks like Bootstrap and preprocessors like SASS dominated web development. They solved real problems—CSS was missing essential features like variables, grid systems, and proper modularity. But CSS has evolved dramatically. Today’s native CSS is powerful enough that most projects don’t need these tools anymore. For this website, I decided to go without a framework or preprocessor and just write raw CSS from scratch. In this post, I reason why I chose so and how it worked out.
The role frameworks played
CSS frameworks provided crucial capabilities that CSS lacked back in the days:
- Grid systems: Complex float-based layouts that made responsive design manageable
- Variables: SASS and LESS gave us
$primary-colorbefore CSS had--primary-color - Nesting: Organizing styles hierarchically instead of repeating selectors
- Mixins and functions: Reusable style patterns
- Cross-browser consistency: Normalizing browser differences
These frameworks pushed CSS forward. Browser vendors took notice and added native features to address these needs. The frameworks succeeded—so much so that they made themselves less necessary.
What this site uses
For generating this website, I used zero CSS frameworks or preprocessors. Just plain CSS files that leverage modern, native features:
- CSS Grid: The entire layout uses native grid (
display: grid) with named grid areas - CSS Variables: Theme colors, spacing, and typography scales defined with custom properties
- Flexbox: Navigation and component layouts
- Modern selectors:
:has(),:is(),:not(), and more - Media queries: Responsive breakpoints without framework overhead
- Native nesting: Supported in all modern browsers
Let me show you some examples from the actual code.
Modern CSS features in action
CSS Grid replaces grid frameworks
Remember the 12-column grid systems? Here’s how this site lays out its structure:
body {
display: grid;
grid-template-columns: 1fr var(--gutter-width) var(--main-width) 1fr;
grid-template-rows: auto 1fr auto;
}
Three lines. No framework classes like col-md-8 offset-md-2. The grid is explicit, readable, and flexible.
CSS Variables replace preprocessors
Before CSS variables, you needed SASS to define reusable values:
// SASS
$primary-color: #268bd2;
$spacing-md: 1rem;
Now CSS does this natively, with the added benefit that variables can be changed at runtime (for dark mode, user preferences, etc.):
:root {
--sol-blue: #268bd2;
--space-md: 1rem;
--link-color: var(--sol-blue);
}
This site uses CSS variables for its entire Solarized color theme, allowing seamless light/dark mode switching without JavaScript manipulation of styles.
Modern layout tools
Remember fighting with floats and clearfixes? CSS Grid and Flexbox make complex layouts trivial:
.top-nav ul {
display: flex;
gap: var(--space-lg);
}
The gap property alone eliminates countless margin hacks. The shape-outside property lets text flow around rotated images naturally—something
that previously required complex JavaScript calculations.
No build step required
One of the biggest advantages: no compilation. Edit a CSS file, refresh the browser, see the changes. No webpack configuration, no build errors, no waiting for SASS to compile. Development is instant. A workflow that I use now oftentimes, is editing the CSS in my browser’s development tools. This gives me immediate feedback and validation. When I’m satisfied with an edit, I just copy and paste the CSS from the browser development tools into my code repository.
YMMV
As always, “your mileage may vary.” For my modest personal website, using raw CSS has simplified my workflow, and I don’t perceive any downsides. I can imagine this is different when you’re building a huge corporate website, a web shop, or any web-based application.
The benefits of framework-free CSS
-
Smaller file sizes: This site’s entire CSS bundle is under 10KB. Most framework base stylesheets are 10-50x larger.
-
Better performance: Browsers are optimized for native CSS. No runtime overhead from JavaScript-based CSS-in-JS solutions.
-
Future-proof: CSS is a web standard. Frameworks come and go, but CSS is forever. (Well, that’s what we can hope for at least…)
-
Easier debugging: Inspect element shows your actual CSS, not generated class names or abstraction layers. Corrected CSS can be copied back from the browser into the code base.
Downsides
No downsides, really? Well, as I said, I didn’t perceive any while working on this website so far. But if I’m really critical, there’s actually one example in this website’s CSS that I don’t like. For setting some colors differently in light and dark mode, there was no way I could prevent repeating the definition of color for the dark scheme:
/* Light mode - default and explicit override */
:root,
.light-mode {
--bg-color: var(--sol-base3);
--bg-color-alt: var(--sol-base2);
--text-color: var(--sol-base00);
/* left out the rest of the definitions for brevity. */
}
/* Dark mode - auto based on system preference */
@media (prefers-color-scheme: dark) {
not(.light-mode) {
--bg-color: var(--sol-base03);
--bg-color-alt: var(--sol-base02);
--text-color: var(--sol-base0);
}
}
/* Manual dark mode - overrides auto mode and light mode.
* We need to repeat the definitions here, CSS doesn't have a way to prevent this repetition. */
.dark-mode {
--bg-color: var(--sol-base03);
--bg-color-alt: var(--sol-base02);
--text-color: var(--sol-base0);
}
A preprocessor-based solution might have been able to prevent this repetition. But to be honest, I’m willing to pay this price for the decreased complexity of the entire code base and build process.
Conclusion
CSS frameworks and preprocessors played a crucial role in web development history. They identified pain points and inspired native CSS features. But CSS has grown up. Modern CSS has native variables, grid, flexbox, nesting, and powerful selectors. For many projects, that’s all you need.
The best tool is the one that solves your problem without adding unnecessary complexity. For this website, that tool is plain CSS. And it’s been a joy to work with.
Try it on your next project. You might be surprised how little you miss the framework.