Abstract
How should you write styles in modern JavaScript applications? The current ecosystem offers a wide range of approaches, from traditional CSS to utility-first frameworks like Tailwind, preprocessors like Sass, and CSS-in-JS libraries. In this article, I walk through the most common solutions, discussing their pros and cons based on hands-on experience. The goal is to provide a practical overview to help developers make informed decisions in real-world projects.
1. Introduction
The ecosystem of tools for styling user interfaces evolves constantly: new frameworks emerge, APIs get redesigned, and trends shift every few months. With so much variety, it's easy to feel lost.
What follows is not a definitive classification but a practical guide grounded in experience. Each section highlights strengths and potential pitfalls of different approaches, based on their use in real-world projects. Naturally, this is a personal perspective; context matters, and things continue to evolve.
2. Vanilla CSS
2.1 Advantages
- No build steps or extra dependencies: lower complexity and zero runtime cost.
- Recent improvements to the standard (e.g., custom properties) now cover features that were once exclusive to preprocessors.
2.2 Disadvantages
- Global scope: a class defined in one file affects the entire project. Naming conventions like BEM help, but require discipline and don't fully prevent name collisions.
- You have to manually handle vendor prefixes when targeting older browsers.
- Limited interaction with JavaScript code.
- Its document-based origin can feel constraining in highly interactive applications.
3. Preprocessors: Sass and Less
Preprocessors add variables, loops, mixins, and nesting, and compile down to standard CSS.
3.1 Advantages
- Powerful tools for generating complex, reusable styles.
- Short learning curve for developers already familiar with CSS.
3.2 Disadvantages
- Everything happens at build time: variables can't react to state changes at runtime.
- Still global in scope; inherits the same scoping issues as vanilla CSS.
- Requires a build step and native dependencies, which can sometimes fall out of date.
- Popularity has declined in the JavaScript ecosystem, with fewer examples and less community maintenance.
4. CSS Modules
Lets you write standard CSS, and when imported into JavaScript, it generates unique class names automatically.
4.1 Advantages
- Solves scoping conflicts without abandoning familiar CSS syntax.
- The composes feature makes it easy to extend existing styles.
4.2 Disadvantages
- Offers limited productivity gains on its own; often paired with PostCSS for tasks like autoprefixing.
- Sharing data between CSS and JavaScript is not straightforward.
5. Single-File Components (Vue / Svelte)
These frameworks support <style scoped> within the same file as the component.
5.1 Advantages
- Styles and markup live together, reducing context switching.
- Automatic scoping prevents collisions transparently.
5.2 Disadvantages
- Stylesheets retain the same limitations as basic CSS when interacting with application logic, unless extended by framework-specific features.
6. CSS-IN-JS: Styled-Components
Each style block is declared as a React component.
The Wrapper component is a reusable-styled block that can wrap other elements while applying custom styles and responsive rules seamlessly:
6.1 Advantages
- Development experience feels close to writing regular CSS, with Sass-like features built in.
- A natural fit for component-based architectures: scoped, conflict-free styles.
- Can cover everything from local styles to global rules and animations.
6.2 Disadvantages
- Adds a small runtime (~11 kB gzipped).
- Currently in maintenance mode with no active development.
- Primarily focused on React; other ecosystems have smaller adaptations.
- Doesn't immediately show the actual HTML tags, which can obscure semantics.
- Partial compatibility with React Server Components.
7. Tailwind CSS
Utility-first framework where small classes represent specific CSS rules.
These classes apply the following CSS to the div:
7.1 Advantages
- Scoping is no longer an issue: everything is utility-based.
- Encourages visual consistency through a predefined design system.
- Framework-agnostic: can be used across different tech stacks.
- After the initial learning curve, UI development can be very fast.
7.2 Disadvantages
- Steeper learning curve than more traditional options.
- Generates a single CSS file that can grow large in big projects.
- Not every design case maps cleanly to utilities, and markup can become verbose.
- Its “inline class” style is polarizing, some teams adopt it enthusiastically, others reject it outright
8. Conclusion
Keeping styles locally scoped makes day-to-day development simpler. This is why solutions that isolate styles, such as CSS Modules, single-file components, CSS-in-JS, or utility-first frameworks, often save time and help prevent errors.
Tool popularity brings community support and learning resources, but it should not be the only deciding factor. Evaluate performance, compatibility with your stack, and how comfortable your team feels using it.
The foundations of CSS: cascade, inheritance, flexbox, grid, accessibility remain essential, regardless of the tooling layer. Investing in these principles always pays off.
There is no universal answer. Choose the solution that best fits your workflow and project requirements. What matters most is using tools thoughtfully and understanding their trade-offs.