DEV Community

Cover image for CSS-in-JS vs CSS in CSS
Alberto Forni
Alberto Forni

Posted on

CSS-in-JS vs CSS in CSS

  1. Why the question?
  2. πŸ› οΈ Comparison between CSS Modules and CSS-in-JS πŸ› οΈ
  3. πŸš€ CSS-in-JS: The Trending Path with Vue, Svelte, and Astro πŸš€
  4. πŸ”§ Ok, I’m Convinced About CSS-in-JS. Which Library Should I Pick? πŸ”§
  5. Content Security Policy (CSP)
  6. Have you come across Tailwind CSS?
  7. Conclusion
  8. Resources

Why the question?

Picture this: You've got a classic bicycle. It's beautiful, sturdy, and perfect for leisurely rides through the park. But one day, you decide to enter the Giro di Italia. πŸš΄β€β™‚οΈπŸ’¨ That trusty bike, as lovely as it is, just isn't cut out for the intense, mountainous terrains of the race. Similarly, when the World Wide Web was born, HTML was our trusty bicycle. Designed for simple document structuring, it was perfect for its time. And then came CSS, the stylish accessories to make our HTML bicycle dazzle. But here's the twist: the web evolved! Now, we're not just riding through parks; we're racing in the big leagues.

As the web landscape shifted towards more interactive and dynamic content, the traditional ways of styling started to show their limits.

Relying on manual naming, like BEM, while beneficial, can become cumbersome. Imagine having to remember or reference naming conventions for every component you create:

/* BEM Style */
.button {}
.button__icon {}
.button--disabled {}
Enter fullscreen mode Exit fullscreen mode

The margin for error increases and consistency can waver.

With the introduction of component-based frameworks, each component acted as a self-contained unit, housing both its logic and style.

/* app.svelte */
<script>
    let count = $state(0);

    function increment() {
        count += 1;
    }
</script>

<style>
    button {
        color: red;
    }
</style>

<button on:click={increment}>
    clicks: {count}
</button>
Enter fullscreen mode Exit fullscreen mode

Lastly, as applications grow, the need for shared styles and variables becomes paramount. Consider the scenario where a primary colour needs to be referenced in multiple components. Instead of hardcoding this in each component, we need to use a scalable solution that can reference code written in other components or parts of the application.

In conclusion, "Why the question?" It's because the web isn't static. As we strive for more efficient, maintainable, and scalable solutions, we must evaluate and adapt our tools accordingly.

πŸ› οΈ Comparison between CSS Modules and CSS-in-JS πŸ› οΈ

As we pedal further into our web development journey, the landscape of styling demands a closer look. If we're all in agreement about the need to modularise styles, let's dive deep into comparing two of the most popular techniques: CSS Modules and CSS-in-JS.

Using CSS Modules:

Pros:

  • Separation of Concerns: With CSS Modules, styling resides in its own dedicated file. It's like having a separate gear for uphill climbs on our bike journey – dedicated and efficient.
/* styles.module.css */
.button {
    background-color: blue;
}
Enter fullscreen mode Exit fullscreen mode
  • Simplicity: No extra baggage! There's no need for additional plugins.
  • Hot-Reload: A smooth ride with easy and instant updates as you make changes.
  • Developer Tools: Interacting with dev tools is straightforward since the syntax is the genuine CSS we all know and love.

Cons:

  • File Juggling: The separation means you're constantly switching between your component and style files. It's like having to switch bikes mid-race.
  • Unused Styles: Identifying and removing styles that are no longer in use can be challenging.
  • Variable Usages: Spotting typos and leveraging autocomplete becomes trickier.
  • Navigation: You miss out on the convenience of jumping directly from your component to the associated style.

Using CSS-in-JS:

Pros:

  • Close Proximity to View: Styles are right next to where they're used. It's like having your water bottle attached to your bike – easily accessible when you need it.
import { css } from 'solid-styled';

function Title() {
  css`
    h1 {
      color: red;
    }
  `;

  return <h1>Hello World</h1>;
}
Enter fullscreen mode Exit fullscreen mode
  • Full Power of TypeScript/JavaScript: Imagine being able to adjust your bike's settings on-the-go. That's the flexibility you get here.
  • Type Checking: With TypeScript, you get an extra pair of eyes ensuring you're using the right properties and values.
  • Navigation: The "go to definition" feature works seamlessly, especially if the style isn’t directly attached to the HTML element.

Cons:

  • Bulky View Files: Since everything is in one place, your component file can get crowded, making it harder to parse at a glance.
  • Additional Tooling: Your editor might need some extra setup to understand and highlight styles within JavaScript properly.
  • Performance Overheads: To ensure your app runs smoothly, you might need to incorporate specific libraries.
  • Bundle Size: Due to vendor prefixes not always adhering to .browserlistrc, your bundle might end up heavier, and your developer experience could suffer with unnecessary styles in the CSS panel.

But What About Inline style Attributes?

While it might be tempting to style directly using the style attribute of HTML elements, there are some pitfalls:

  • Lack of Global Styles: You miss out on overarching design consistency.
  • Limited Styling Options: Pseudo-classes, keyframes, and states like hover or media queries are off the table.
  • Performance: Class names are simply faster than inline styles. It's the difference between a road bike and a mountain bike – each has its purpose!

Conclusion:

Both CSS Modules and CSS-in-JS come with their set of advantages and challenges. Your choice will largely depend on the nature of your project, your team's familiarity with the tools, and personal preference. While I won't outright advocate for one over the other, I lean slightly towards CSS-in-JS for TypeScript-heavy projects due to the added benefits of type checking and direct navigation. But remember, the best tool is the one that gets the job done and feels right for your journey!

πŸš€ CSS-in-JS: The Trending Path with Vue, Svelte, and Astro πŸš€

The ever-changing terrain of web development constantly introduces us to newer tools and techniques. A trend that's hard to miss is the surge in the popularity of CSS-in-JS, especially evident in modern frameworks like Vue, Svelte, and Astro. But what's fuelling this trend?

Why the Shift to CSS-in-Component?

Component-Based Architecture: The modular, reusable, and efficient nature of component-based web development naturally complements the CSS-in-JS approach, where styles are directly tied to their respective components.

  • **Dynamic Styling: **Traditional CSS can feel static. CSS-in-JS breaks that mould, allowing styles to adapt based on props, state, or live data.
  • Scoped Styles: Global CSS can sometimes lead to style conflicts. With CSS-in-JS, styles are strictly scoped to the component, preventing unintended overrides.
  • Enhanced Developer Experience: Features like hot-reloading, immediate feedback, and leveraging JavaScript's capabilities elevate the development process.

Spotlight on Vue, Svelte, and Astro:

Vue: Vue's single-file components are a developer's delight, integrating template, script, and style in one place:

/* app.vue */
<template>
  <button class="btn">Click Me</button>
</template>

<script>
export default {
  name: 'MyButton'
};
</script>

<style scoped>
.btn {
  background-color: blue;
  color: white;
}
</style>
Enter fullscreen mode Exit fullscreen mode

The scoped attribute ensures this component's styles won't interfere with others.

Svelte: Svelte offers a clean and intuitive way to structure components:

/* app.svelte */
<script>
  let color = 'blue';
</script>

<button class="btn">Click Me</button>

<style>
  .btn {
    background-color: {color};
    color: white;
  }
</style>
Enter fullscreen mode Exit fullscreen mode

Here, we're using a variable directly within our styles, showcasing the dynamic capabilities.

Astro: Astro, a newcomer to the scene, emphasises delivering the least amount of JavaScript possible. Here's a glimpse of styling in Astro:

/* app.astro */
<button className="btn">Click Me</button>

<style lang="scss">
.btn {
  background-color: blue;
  color: white;
}
</style>
Enter fullscreen mode Exit fullscreen mode

Conclusion:

The adoption of CSS-in-JS in frameworks like Vue, Svelte, and Astro is indicative of the web development community's desire for more integrated, efficient, and dynamic styling methods. These frameworks, by melding styles with components, offer an approach that's both intuitive and powerful, ensuring web experiences are not only functional but also aesthetically pleasing.

πŸ”§ Ok, I’m Convinced About CSS-in-JS. Which Library Should I Pick? πŸ”§

Diving into the world of CSS-in-JS, it's evident that many contemporary frameworks come with styling solutions out of the box. But, if you're like me, with a React project in hand, you might be thinking, "Alright, I'm convinced. Hand me the right library!" But wait, it's not that straightforward.

Just as the web development landscape is vast and varied, so are the choices for CSS-in-JS libraries. It's reminiscent of the myriad state management libraries available – each with its unique features and quirks. But fear not! Let's break down the essential features to consider, helping you navigate this space with clarity:

1. (Tagged Templates)
Using ES Tagged Templates, styles are defined as strings:

  • πŸ“ Syntax: Adheres to the familiar kebab-case, akin to plain CSS.

  • πŸ”„ Migration: Transitioning from plain CSS to CSS-in-JS becomes smoother, saving you from a total rewrite.

  • πŸ”§ Tooling: To beautify your code with syntax highlighting and code completion, you'll need extra plugins. Without them, it's just plain strings.

  • πŸ”„ Parsing: An additional step is needed to morph the string into JS.

2. { } (Object Styles)
Employing plain JavaScript objects, styles are defined as... well, objects!

  • **πŸ“ Syntax: **Leverages camelCase for property names, echoing React Native conventions.

  • πŸ”„ Migration: Brace yourself for a full rewrite if you're moving existing CSS.
    for instance, backgroundColor: "transparent", in JS becomes background-color: transparent; in CSS and Developer Tools. Note the

  • camelCase β†’ kebab case,

  • , instead of ;,

  • "" go away.

- πŸ”§ Tooling: The beauty of JS objects means you get out-of-the-box syntax highlighting, sans any extra tooling.

3. .css (Static CSS Extraction)
Your styles get a new life as static .css files:

  • βš–οΈ Bundle Size: Say goodbye to additional runtime libraries, trimming your bundle/page size.

  • πŸš€ Performance: With an empty cache, this might be a tad slower for your users (affecting FCP/FMP metrics). But with a full cache? Lightning-fast page loads.

  • 🎨 Dynamic Styling: The generated file could bulk up since all style combos need pre-generation at build time.

  • πŸ–₯️ Use Case: Ideal for scenarios like e-commerce sites or blogs where cached styles play a pivotal role.

4. <style> tag
Your styles find their home inside <style> tags within the document's <head>:

  • 🎨 Dynamic Styling: Tailor-making styles on-the-fly becomes a breeze.

  • βš–οΈ Payload: A tad bulkier, given that a runtime library is required for dynamic style handling.

  • πŸ”„ SSR Considerations: Styles necessary for the initial render are shipped twice: during SSR and hydration.

  • πŸ–₯️ Use Case: Perfect for SPAs that are dynamic and interactive.

Content Security Policy (CSP)

A common problem that libraries that create a <style> tag at runtime could face is the Content Security Policy. Styles might be blocked if the HTML page contains Content-Security-Policy: style-src. For that, you can use a nonce that is dynamically produced from the server for every HTTP request and injected into your style tag. This is how Emotion does it.

Have you come across Tailwind CSS?

It's a notable player that has popularised the Atomic CSS approach. While it simplifies styling by allowing users to directly add CSS classes to HTML elements and offering sensible defaults, it does introduce a new "language" to learn. It could be a good option if you start from scratch, but it might not be the most practical choice for existing projects that have extensively utilised traditional CSS or CSS-in-JS.

Conclusion

As you set out to pick your ideal CSS-in-JS library, keep in mind the specific needs and nuances of your project. While there's no one-size-fits-all solution, understanding the features and trade-offs of each approach will ensure you make an informed choice.

Resources

Top comments (0)