MODERN CSS STYLING APPROACHES
CSS styling has evolved significantly, moving from traditional global stylesheets and independent style files to more modular, component-level approaches. Modern CSS focuses on scalability, reusability, and maintainability, helping developers create UI designs that are easier to manage and more predictable in large applications. This document outlines the evolution from traditional styling methods to modern CSS techniques.
Evolution of CSS Styling
-
Global Stylesheets
Early CSS styling typically involved a single global stylesheet for the entire application, where all styles were defined in one file or a few large files.- Pros: Simple to manage for small applications.
- Cons: Difficult to maintain in large applications, prone to style conflicts, and lacks reusability.
-
Independent Style Files (CSS per Component)
With the growth of larger applications, developers started organizing CSS in separate files for each component or page to improve manageability.- Pros: Better organization compared to a single stylesheet.
- Cons: Still lacks true isolation; components can inadvertently affect each other.
-
Global Utility Classes
In an effort to create reusable styles, developers used utility classes like.btn
,.text-center
, and.margin-10
. While this was an improvement, global classes still led to conflicts and were often too restrictive.- Pros: Reusable classes, reduced duplication.
- Cons: Hard to customize for specific components, and styles still risk global conflicts. Relying heavily on custom global classes can add significant mental overhead for developers, especially in larger projects with complex styling needs.
Modern CSS Approaches
Today, CSS is evolving toward modular, reusable, and scoped solutions that align well with component-based architecture. Here are the most popular modern CSS approaches:
1. CSS Modules
CSS Modules scope styles locally by default, which means classes and IDs are only applied to the component where they’re defined, helping avoid style conflicts in larger applications.
- Benefits:
- Avoids global CSS conflicts.
- Styles are imported as objects, making them easy to reference within components.
- Usage:
- Commonly used with frameworks like React, Vue, and Next.js.
- Resource: CSS Modules
In a React application CSS Modules would look like this
/* Button.module.css */
.button {
background-color: blue;
}
import styles from "./Button.module.css";
<button className={styles.button}>Click Me</button>;
In Vue we would use the $style
object
<template>
<p :class="$style.red">This should be red</p>
</template>
<style module>
.red {
color: red;
}
</style>
2. Localised style tags
Localised Styles in Raw HTML with Inline <style>
Tags
While the HTML5 scoped attribute for <style scoped>
tags was proposed and is still sometimes mentioned, it is not supported in modern browsers. However, you can achieve a similar “local” effect by using inline <style>
tags with classes or IDs to limit the scope manually.
Example: Using a Localized <style>
Tag within a Section
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Localized Styles Example</title>
</head>
<body>
<section id="special-section">
<!-- Localized styles only for this section -->
<style>
#special-section h2 {
color: green;
font-size: 24px;
}
#special-section p {
font-style: italic;
color: darkgreen;
}
</style>
<h2>Styled Heading in Special Section</h2>
<p>Only paragraphs within this section are styled.</p>
</section>
<!-- This section is unaffected by the local styles -->
<section>
<h2>This Heading is Not Styled</h2>
<p>This paragraph is outside of the special section and remains unaffected.</p>
</section>
</body>
</html>
Explanation
• <style>
Tag Inside Section: Place a <style>
tag within a specific section (like <section>
or <div>
) to contain styles relevant only to that part of the document.
• Scoped Selectors (e.g., #special-section h2
): By using an ID or a unique class for the section, you can target only the elements within that section, effectively creating scoped styles.
<style>
Tags in HTML
Benefits of Local • Encapsulation: Styles only apply within the section they’re defined in, helping avoid global style conflicts. • Self-Contained Styling: This approach keeps styles close to the HTML they apply to, making it easier to maintain in smaller or self-contained HTML files.
3. Scoped Styles in Frameworks (Vue and Angular)
Frameworks like Vue and Angular allow for scoped CSS within single-file components, which automatically scopes styles to a particular component.
- Benefits:
- Prevents styles from leaking outside the component.
- Simplifies CSS management in large applications.
- Usage:
- Vue:
<style scoped>
attribute in Vue SFCs. - Angular: Uses encapsulated view for styles by default.
- Vue:
- Resource: Vue Scoped Styles
How Scoped Styles Work in Vue and Nuxt
In a Vue SFC, when you add the scoped attribute to the <style>
tag, Vue’s compiler will:
- Generate unique attributes (like data-v-f137c5cf) for the component’s DOM elements.
- Automatically apply those attributes to styles, limiting them to the specific component instance.
This approach is similar to “local” or “scoped” styles but handled automatically without requiring inline <style>
tags or manual scoping.
Example: Scoped Styles in a Nuxt Component
<template>
<div class="example">Scoped styles only apply here.</div>
</template>
<script>
export default {
name: "ExampleComponent"
};
</script>
<style scoped>
.example {
align-items: baseline;
font-size: 1.5rem;
background-color: #242424;
width: fit-content;
border: 1px solid white;
}
</style>
Generated CSS in Nuxt/Vue
With the scoped attribute, Nuxt/Vue automatically generates unique data attributes, resulting in CSS that looks like this:
.example[data-v-f137c5cf] {
align-items: baseline;
font-size: 1.5rem;
background-color: #242424;
width: fit-content;
border: 1px solid white;
}
4. Atomic CSS
Atomic CSS is a utility-first approach where styles are broken down into small, reusable utility classes (e.g., text-center
, bg-blue
). This method minimizes CSS bloat and enables rapid UI development.
- Benefits:
- High reusability and reduces CSS size.
- Encourages consistent design by reusing the same utilities.
- Popular Framework: Tailwind CSS
- Resource: Tailwind CSS
<button class="bg-blue text-white p-2 rounded">Click Me</button>
5. CSS-in-JS
CSS-in-JS involves writing CSS directly within JavaScript files, allowing styles to be tightly coupled with component logic. It also provides the ability to use JavaScript variables and logic within styles, enhancing flexibility.
- Benefits:
- Co-locates styles with component logic.
- Dynamically applies styles based on component state or props.
- Popular Libraries: Styled Components, Emotion
- Resource: Styled Components
In a React application it would typically look like this using styled-components
import styled from "styled-components";
const Button = styled.button`
background-color: blue;
color: white;
padding: 10px;
`;
Dynamically setting styles in Vue with a :style binding based on props is similar to the principles of CSS-in-JS but adapted for Vue’s component system. It allows you to apply styles inline, using computed properties and reactive state without needing a dedicated CSS-in-JS library.
In Vue, this approach works particularly well when:
- You need styles that vary based on component props or state.
- You want to avoid hardcoded inline styles and maintain a level of reactivity and readability.
Example: Dynamic Styling with :style Binding and Computed Properties
Here’s a more detailed example using dynamic props such as display
, gap
, and padding
for a <FlexBox>
component that applies these styles based on passed props.
<template>
<div :style="computedStyles">
<slot />
</div>
</template>
<script>
export default {
props: {
display: {
type: String,
default: "flex"
},
gap: {
type: String,
default: "16px"
},
padding: {
type: String,
default: "8px"
},
alignItems: {
type: String,
default: "center"
},
justifyContent: {
type: String,
default: "center"
}
},
computed: {
computedStyles() {
return {
display: this.display,
gap: this.gap,
padding: this.padding,
alignItems: this.alignItems,
justifyContent: this.justifyContent
};
}
}
};
</script>
<style scoped>
/* Optionally define base styles for fallback or custom adjustments */
</style>
6. Design Systems and Component Libraries
Design systems and component libraries incorporate reusable components and standardized styling across an entire application, enabling consistency and improving scalability.
- Benefits:
- Enforces consistent UI design.
- Reduces design debt and minimizes styling inconsistencies.
- Popular Tools: Storybook, Figma, Chromatic
- Resource: Storybook for Design Systems
7. BEM (Block Element Modifier)
BEM is a naming convention that structures CSS in a way that is readable, predictable, and modular. Each component has its own "block" name, with modifiers and elements organized in a clear hierarchy.
- Benefits:
- Avoids naming conflicts and organizes styles predictably.
- Helps structure complex UIs.
- Resource: BEM Methodology
.button--primary {
/* primary button */
}
.button--secondary {
/* secondary button */
}
.button__icon {
/* icon inside button */
}
8. CSS Variables
CSS styling has evolved significantly, with CSS variables (custom properties) emerging as a powerful way to manage reusable values like colors, font sizes, and spacing across components. By defining variables in a central location (often the :root
), CSS variables allow for consistent, scalable styling that can adapt to themes, layouts, and user preferences. This document explores CSS variables as part of modern CSS techniques.
Defining CSS Variables
CSS variables are typically defined at the :root
level, making them globally accessible. This enables centralised styling control, which is especially useful for theme management and consistent design across a large application.
:root
Example of CSS Variables in :root {
/* Color variables */
--color-primary: #007bff;
--color-secondary: #6c757d;
--color-background: #f8f9fa;
/* Font and spacing variables */
--font-size-base: 16px;
--spacing-small: 8px;
--spacing-medium: 16px;
--spacing-large: 24px;
}
Benefits of CSS Variables
- Consistency: Centralised values prevent style inconsistencies across components.
- Dynamic Theming: Values can be updated dynamically to enable light and dark modes or other themes.
- Responsiveness: Variables can adapt based on media queries or user preferences.
.button {
background-color: var(--color-primary);
color: #fff;
font-size: var(--font-size-base);
padding: var(--spacing-small) var(--spacing-medium);
}
.button--secondary {
background-color: var(--color-secondary);
}
Recommended Approach for Component-Driven CSS
To create maintainable, scalable, and consistent styling in modern applications, the following practices are recommended:
-
Use Scoped or Modular CSS
Isolate styles at the component level using scoped styles (Vue), CSS Modules, or CSS-in-JS. This reduces conflicts and makes components more predictable. -
Adopt Utility-First CSS for Speed
Consider Tailwind CSS or another atomic CSS approach for rapid development, especially in applications where style consistency is key. -
Create a Design System
Develop a standardized design system or component library for consistent styling across the application. Tools like Storybook are great for documenting and testing these components. -
Use CSS-in-JS for Dynamic Styling
For components with complex, state-based styling requirements, CSS-in-JS can help by allowing inline logic and variables.
Summary
Modern CSS approaches provide flexibility, modularity, and scalability, enabling efficient and maintainable styling for component-driven applications.
CONTINUE READING
MENTAL OVERHEAD OF CUSTOM GLOBAL CLASSES IN CSS
Relying heavily on custom global classes can add significant mental overhead for developers, especially in larger projects with complex styling needs.
November 15, 2024
FRONTEND BEST PRACTICES
Learn how to build clean, efficient, and reusable frontend components that scale effortlessly. Dive into best practices like component-driven development (CDD), where focusing on isolated UI elements can transform your approach to building modern, maintainable UIs.
November 15, 2024