# Component Design Standards for CSS Modules
This document outlines the component design standards for CSS Modules. It provides guidelines for creating reusable, maintainable, and scalable components using CSS Modules. These standards are crucial for ensuring code quality, team consistency, and long-term project health.
## 1. Component Architecture
### 1.1. Single Responsibility Principle
* **Do This**: Ensure each component has a single, well-defined responsibility. This simplifies debugging, testing, and maintenance.
* **Don't Do This**: Avoid creating "god components" that handle multiple unrelated functionalities.
* **Why**: The Single Responsibility Principle (SRP) promotes modularity and reduces the risk of unintended side effects when modifying a component.
"""jsx
// Good: Button.jsx
// This component is only concerned with rendering a button.
import React from 'react';
import styles from './Button.module.css';
function Button({ children, onClick, className }) {
return (
{children}
);
}
export default Button;
"""
"""jsx
// Bad: ComplexComponent.jsx
// This component handles button rendering, input validation, database updates, causing high complexity and low reusability.
import React from 'react';
function ComplexComponent() {
const handleClick = () => {
// Complex Logic
};
return (
Click Me
// ... more complex logic
);
}
export default ComplexComponent;
"""
### 1.2. Composition Over Inheritance
* **Do This**: Favor composition over inheritance to create more flexible and reusable components. Use props to customize the appearance and behavior of a component.
* **Don't Do This**: Avoid deep inheritance hierarchies, which can lead to tight coupling and brittle code.
* **Why**: Composition promotes loose coupling and allows for more dynamic component configurations.
"""jsx
// Good: Using Composition in Card.jsx
import React from 'react';
import styles from './Card.module.css';
function Card({ children, className }) {
return (
{children}
);
}
export default Card;
// Usage:
// Click
"""
"""jsx
// Bad: Inheritance (Discouraged)
// Using inheritance leads to tight coupling and less flexibility
import React from 'react';
class BaseComponent extends React.Component {
// Base functionality
}
class DerivedComponent extends BaseComponent {
// Inherited functionality
}
"""
### 1.3. Component Naming Conventions
* **Do This**: Use descriptive and consistent naming conventions for components and their corresponding CSS Modules. PascalCase for React components, and kebab-case for corresponding CSS Modules. Prefixing component-specific CSS Modules with the component name improves clarity.
* **Don't Do This**: Use generic or ambiguous names that don't clearly indicate the component's purpose.
* **Why**: Clear naming improves code readability and maintainability.
"""
// Good:
// Component: Button.jsx
// CSS Module: Button.module.css
// Bad:
// Component: Comp.jsx
// CSS Module: styles.module.css
"""
## 2. CSS Module Styles
### 2.1. Modular CSS Class Names
* **Do This**: Utilize CSS Modules' automatic scoping by using descriptive and component-specific CSS class names. Use a structure like "ComponentName_element_modifier" to make your class names very explicit about their origin.
* **Don't Do This**: Use generic class names that could clash with other components or global styles. Avoid using overly specific selectors (ID selectors, complex nesting) within CSS Modules.
* **Why**: CSS Modules prevent naming collisions and ensure that styles are encapsulated within each component.
"""css
/* Good: Button.module.css */
/* Clear, component-specific class names */
.button {
background-color: #007bff;
color: white;
padding: 10px 20px;
border: none;
cursor: pointer;
}
.button_primary {
background-color: #28a745;
}
/* Bad: Button.module.css */
/* Generic class name */
.container {
width: 100%;
}
/* Overly specific selector */
#root .button {
/* ... */
}
"""
"""jsx
// Button.jsx
import React from 'react';
import styles from './Button.module.css';
function Button({ children, onClick, isPrimary }) {
const buttonClass = isPrimary ? "${styles.button} ${styles.button_primary}" : styles.button;
return (
{children}
);
}
export default Button;
"""
### 2.2. Styling Based on Component State
* **Do This**: Use CSS Modules in conjunction with React component state and props to dynamically apply styles.
* **Don't Do This**: Mutate DOM directly or use inline styles excessively. Use CSS variables where appropriate.
* **Why**: This approach ensures that styles are managed declaratively and are consistent with the component's current state.
"""jsx
// Good: ToggleButton.jsx
import React, { useState } from 'react';
import styles from './ToggleButton.module.css';
function ToggleButton({ children }) {
const [isOn, setIsOn] = useState(false);
const toggle = () => {
setIsOn(!isOn);
};
return (
{children}
);
}
export default ToggleButton;
"""
"""css
/* ToggleButton.module.css */
.toggleButton {
padding: 10px 15px;
background-color: #eee;
border: 1px solid #ccc;
cursor: pointer;
}
.toggleButton_on {
background-color: #007bff;
color: white;
}
"""
### 2.3. Global Styles and Theme Integration
* **Do This**: Use global CSS files (e.g., "global.css") for base styles, resets, and theme variables. Utilize CSS variables to maintain a consistent theme across your application and allow easy modifications. Pass theme variables as props when necessary, but prioritize CSS variables for performance.
* **Don't Do This**: Avoid defining component-specific styles in global CSS files.
* **Why**: Global styles provide a foundation for your application's appearance, while CSS variables allow for easy customization.
"""css
/* global.css */
:root {
--primary-color: #007bff;
--secondary-color: #6c757d;
--font-family: sans-serif;
}
body {
font-family: var(--font-family);
margin: 0;
}
"""
"""css
/* Button.module.css */
.button {
background-color: var(--primary-color);
color: white;
padding: 10px 20px;
border: none;
cursor: pointer;
}
"""
### 2.4. Media Queries inside CSS Modules
* **Do This**: Define media queries within each CSS Module to handle responsive styling for specific components.
* **Don't Do This**: Centralize all media queries in global styles, losing component-level context.
* **Why**: This maintains component encapsulation and makes it easier to reason about the responsive behavior of individual components.
"""css
/* Card.module.css */
.card {
border: 1px solid #ccc;
padding: 20px;
}
@media (max-width: 768px) {
.card {
padding: 10px;
}
}
"""
### 2.5. Animation and Transitions
* **Do This**: Use CSS transitions and animations defined within CSS Modules for component-specific visual effects.
* **Don't Do This**: Rely on JavaScript-based animations for simple visual effects that can be achieved with CSS.
* **Why**: CSS animations and transitions are generally more performant and easier to maintain for simple visual effects.
"""css
/* Button.module.css */
.button {
background-color: #007bff;
color: white;
padding: 10px 20px;
border: none;
cursor: pointer;
transition: background-color 0.3s ease;
&:hover { /* use nesting with postcss-nested */
background-color: #0056b3;
}
}
"""
## 3. Component States and Props
### 3.1. Prop Types
* **Do This**: Define PropTypes for all React components. This helps in catching bugs early during development.
* **Don't Do This**: Omit PropTypes, especially for reusable components.
* **Why**: Prop Types provide type checking and ensure components receive the expected props, improving reliability.
"""jsx
// Good: Button.jsx
import React from 'react';
import PropTypes from 'prop-types';
import styles from './Button.module.css';
function Button({ children, onClick, className, isDisabled }) {
return (
{children}
);
}
Button.propTypes = {
children: PropTypes.node.isRequired,
onClick: PropTypes.func,
className: PropTypes.string,
isDisabled: PropTypes.bool,
};
Button.defaultProps = {
isDisabled: false,
}
export default Button;
"""
### 3.2. Handling Component Variants
* **Do This**: Use modifier classes or conditional styling to handle different component variants (e.g., primary, secondary, outlined) within the CSS Module.
* **Don't Do This**: Duplicate styles for each variant; instead, create a base style and apply modifiers.
* **Why**: Modifier classes promote reusability and reduce code duplication.
"""css
/* Button.module.css */
.button {
padding: 10px 20px;
border: none;
cursor: pointer;
}
.button_primary {
background-color: #007bff;
color: white;
}
.button_secondary {
background-color: #6c757d;
color: white;
}
"""
"""jsx
// Button.jsx
import React from 'react';
import styles from './Button.module.css';
function Button({ children, onClick, variant }) {
let buttonClass = styles.button;
if (variant === 'primary') {
buttonClass = "${buttonClass} ${styles.button_primary}";
} else if (variant === 'secondary') {
buttonClass = "${buttonClass} ${styles.button_secondary}";
}
return (
{children}
);
}
export default Button;
"""
### 3.3. Accessibility Considerations
* **Do This**: Ensure that components are accessible by providing appropriate ARIA attributes, semantic HTML, and keyboard navigation support.
* **Don't Do This**: Neglect accessibility, which can exclude users with disabilities.
* **Why**: Accessibility is crucial for creating inclusive user experiences.
"""jsx
// Good: AccessibleButton.jsx
import React from 'react';
import styles from './AccessibleButton.module.css';
function AccessibleButton({ children, onClick, ariaLabel }) {
return (
{children}
);
}
export default AccessibleButton;
"""
## 4. Advanced Techniques
### 4.1. CSS Module Composition
* **Do This**: Use "composes" in CSS Modules to inherit styles from other CSS Modules or global styles.
* **Don't Do This**: Overuse composition, which can lead to tightly coupled styles.
* **Why**: Composition promotes reusability of common styles across multiple components.
"""css
/* Card.module.css */
.card {
border: 1px solid #ccc;
padding: 20px;
}
/* Title.module.css */
.title {
font-size: 20px;
font-weight: bold;
}
"""
"""css
/* CardWithTitle.module.css */
.cardWithTitle {
composes: card from './Card.module.css';
}
.cardTitle {
composes: title from './Title.module.css';
margin-bottom: 10px;
}
"""
"""jsx
// CardWithTitle.jsx
import React from 'react';
import styles from './CardWithTitle.module.css';
function CardWithTitle({ title, children }) {
return (
{title}
{children}
);
}
export default CardWithTitle;
"""
### 4.2. PostCSS Integration
* **Do This**: Leverage PostCSS plugins for advanced CSS transformations, such as autoprefixing, nesting, and variable manipulation (e.g., "postcss-preset-env", "postcss-nested", "postcss-modules-values"). Many CSS Modules implementations come preconfigured with PostCSS.
* **Don't Do This**: Avoid PostCSS plugins that introduce unnecessary complexity or degrade performance.
* **Why**: PostCSS enhances CSS development with powerful features and optimizations.
"""javascript
// postcss.config.js (example)
module.exports = {
plugins: [
require('postcss-preset-env')({
stage: 3,
features: {
'nesting-rules': true,
}
}),
require('postcss-nested'),
require('postcss-modules-values'),
],
};
"""
"""css
/* Button.module.css (using nesting) */
.button {
background-color: #007bff;
color: white;
padding: 10px 20px;
border: none;
cursor: pointer;
&:hover {
background-color: #0056b3;
}
&--primary {
background-color: #28a745;
}
}
"""
### 4.3. CSS-in-JS Libraries (Considerations)
* **Do This**: Consider CSS-in-JS libraries (styled-components, Emotion) for more advanced styling needs, such as dynamic theming, complex component interactions, and runtime style transformations.
* **Don't Do This**: Replace CSS Modules with CSS-in-JS without carefully evaluating the trade-offs. CSS Modules is often sufficient for most component styling needs.
* **Why**: CSS-in-JS offers greater flexibility but can come with a performance and complexity overhead. CSS Modules are often preferred for their simplicity and predictable performance.
## 5. Performance Optimization
### 5.1. Minimize CSS Selectors
* **Do This**: Keep CSS selectors as simple as possible. Avoid deeply nested selectors.
* **Don't Do This**: Use complex selectors that can impact rendering performance.
* **Why**: Simple selectors are faster for the browser to match and apply. CSS Modules encourages flat selectors naturally.
"""css
/* Good: Card.module.css */
.card {
border: 1px solid #ccc;
padding: 20px;
}
/* Bad: Card.module.css */
.card div p {
/* Avoid such deeply nested selectors */
}
"""
### 5.2. Code Splitting
* **Do This**: Utilize code splitting techniques to load only the necessary CSS Modules for each page or component. Tools like Webpack and Parcel support code splitting.
* **Don't Do This**: Load all CSS Modules upfront, impacting initial load time.
* **Why**: Code splitting improves the initial load time and overall performance of the application.
### 5.3. Critical CSS
* **Do This**: Identify and inline critical CSS, allowing above-the-fold content to render quickly.
* **Don't Do This**: Neglect critical CSS, which can result in a poor user experience due to delayed rendering.
* **Why**: Inlining critical CSS improves perceived performance by rendering essential content immediately.
## 6. Testing
### 6.1. Unit Testing
* **Do This**: Write unit tests for each component to ensure that styles are correctly applied based on different states and props.
* **Don't Do This**: Skip unit tests for styling logic, potentially leading to visual regressions.
* **Why**: Unit tests provide confidence in the visual consistency and correctness of components.
### 6.2. Visual Regression Testing
* **Do This**: Use visual regression testing tools to automatically detect visual changes in components. Examples include: Chromatic, Percy, and BackstopJS.
* **Don't Do This**: Rely solely on manual visual inspection, which is prone to human error.
* **Why**: Visual regression testing helps catch unintended visual changes caused by code modifications.
## 7. Security
### 7.1. Sanitization
* **Do This**: Sanitize and validate any dynamically generated CSS class names or styles to prevent injection attacks.
* **Don't Do This**: Directly inject unsanitized user input into CSS class names or styles.
* **Why**: Sanitization prevents attackers from injecting malicious CSS code that could compromise the application.
### 7.2. Dependency Management
* **Do This**: Keep CSS Module dependencies up to date to patch security vulnerabilities. Regularly audit dependencies for known security issues.
* **Don't Do This**: Use outdated CSS Module libraries with known security vulnerabilities.
* **Why**: Keeping dependencies up to date helps prevent security exploits.
## Revision History
| Version | Date | Author | Changes |
| ------- | ---------- | ----------- | ---------------------------------------- |
| 1.0 | 2024-06-07 | Your Name | Initial Draft |
| 1.1 | 2024-06-08 | Your Name | Added sections on performance; security |
This coding standards document aims to provide comprehensive guidance for developing robust and maintainable React components using CSS Modules. Adhering to these standards will help ensure code quality, team consistency, and long-term project success.
danielsogl
Created Mar 6, 2025
This guide explains how to effectively use .clinerules
with Cline, the AI-powered coding assistant.
The .clinerules
file is a powerful configuration file that helps Cline understand your project's requirements, coding standards, and constraints. When placed in your project's root directory, it automatically guides Cline's behavior and ensures consistency across your codebase.
Place the .clinerules
file in your project's root directory. Cline automatically detects and follows these rules for all files within the project.
# Project Overview project: name: 'Your Project Name' description: 'Brief project description' stack: - technology: 'Framework/Language' version: 'X.Y.Z' - technology: 'Database' version: 'X.Y.Z'
# Code Standards standards: style: - 'Use consistent indentation (2 spaces)' - 'Follow language-specific naming conventions' documentation: - 'Include JSDoc comments for all functions' - 'Maintain up-to-date README files' testing: - 'Write unit tests for all new features' - 'Maintain minimum 80% code coverage'
# Security Guidelines security: authentication: - 'Implement proper token validation' - 'Use environment variables for secrets' dataProtection: - 'Sanitize all user inputs' - 'Implement proper error handling'
Be Specific
Maintain Organization
Regular Updates
# Common Patterns Example patterns: components: - pattern: 'Use functional components by default' - pattern: 'Implement error boundaries for component trees' stateManagement: - pattern: 'Use React Query for server state' - pattern: 'Implement proper loading states'
Commit the Rules
.clinerules
in version controlTeam Collaboration
Rules Not Being Applied
Conflicting Rules
Performance Considerations
# Basic .clinerules Example project: name: 'Web Application' type: 'Next.js Frontend' standards: - 'Use TypeScript for all new code' - 'Follow React best practices' - 'Implement proper error handling' testing: unit: - 'Jest for unit tests' - 'React Testing Library for components' e2e: - 'Cypress for end-to-end testing' documentation: required: - 'README.md in each major directory' - 'JSDoc comments for public APIs' - 'Changelog updates for all changes'
# Advanced .clinerules Example project: name: 'Enterprise Application' compliance: - 'GDPR requirements' - 'WCAG 2.1 AA accessibility' architecture: patterns: - 'Clean Architecture principles' - 'Domain-Driven Design concepts' security: requirements: - 'OAuth 2.0 authentication' - 'Rate limiting on all APIs' - 'Input validation with Zod'
# Core Architecture Standards for CSS Modules This document outlines the core architectural standards for developing maintainable, scalable, and performant CSS Modules. It focuses on project structure, organization principles, and fundamental architectural patterns specific to CSS Modules, incorporating modern approaches and best practices. ## 1. Project Structure and Organization A well-defined project structure is crucial for the long-term maintainability of any CSS Modules project. The standards below aim to establish a consistent and logical organization. ### 1.1. Directory Structure **Do This:** * Organize CSS Modules alongside their corresponding components. This promotes locality and makes it easier to understand which styles apply to which components. **Don't Do This:** * Create a single, monolithic "styles" directory, as this hinders maintainability and makes it difficult to reason about the relationship between styles and components. **Why This Matters:** * Locality enhances maintainability because related files are physically close together. It minimizes searching and makes refactoring less error-prone. * It clearly shows the relationship between a component and its styling, improves the developer experience with quicker context switching. **Code Example:** """ src/ ├── components/ │ ├── Button/ │ │ ├── Button.jsx │ │ └── Button.module.css │ ├── Input/ │ │ ├── Input.jsx │ │ └── Input.module.css """ **Anti-Pattern:** """ src/ ├── components/ │ ├── Button/ │ │ └── Button.jsx │ ├── Input/ │ │ └── Input.jsx ├── styles/ │ ├── button.css // Hard to know where this is used │ └── input.css // Hard to know where this is used """ ### 1.2. Naming Conventions **Do This:** * Use the ".module.css" (or ".module.scss", ".module.less") extension for all CSS Modules files. * Prefix CSS Modules file names with the component name. This helps ensure clarity and avoids naming collisions. * Follow the BEM (Block, Element, Modifier) naming convention within your CSS Modules. **Don't Do This:** * Use generic names like "style.css" or "index.css". * Use inconsistent naming conventions across the project. **Why This Matters:** * The ".module.css" extension is recognized by build tools like Webpack and Vite, enabling CSS Modules processing. * Predictable naming makes it easier to find and understand the purpose of each CSS Modules file. * BEM promotes modularity and reusability by clearly defining the relationship between elements and their modifiers. **Code Example:** """css /* Button.module.css */ .button { /* Block */ padding: 10px 20px; border: none; cursor: pointer; } .button__label { /* Element */ font-size: 16px; } .button--primary { /* Modifier */ background-color: blue; color: white; } """ **Anti-Pattern:** """css /* styles.css */ .container { /* Confusing name */ margin: 0 auto; } .text { /* Generic name */ font-size: 14px; } """ ### 1.3. Global Styles vs. Component-Specific Styles **Do This:** * Reserve global styles for project-wide resets, base typography, and utility classes. * Use CSS Modules for all component-specific styling. **Don't Do This:** * Overuse global styles, as this can lead to unintended side effects and conflicts. * Mix global styles and CSS Modules within the same file. **Why This Matters:** * Separation of concerns improves maintainability and reduces the risk of conflicts between different parts of the application. * CSS Modules provide scoping, preventing styles from leaking out and affecting other components. **Code Example:** """css /* global.css (for global styles) */ body { margin: 0; font-family: sans-serif; } .uppercase { /* Utility class */ text-transform: uppercase; } """ """css /* Button.module.css (for component-specific styles) */ .button { /* ... */ } """ **Implementation in Component:** """jsx import styles from './Button.module.css'; import './global.css' // Import global styles function Button({ children, primary }) { return ( <button className={"${styles.button} ${primary ? styles['button--primary'] : ''} uppercase"}> {children} </button> ); } export default Button; """ ### 1.4. Utilizing CSS Variables (Custom Properties) **Do This:** * Define CSS variables for theme-related values like colors, fonts, and spacing. * Use a separate file, such as "variables.module.css" or "theme.module.css", to store these variables. * Apply CSS variables within CSS Modules. **Don't Do This:** * Hardcode values directly in CSS Modules, which makes theme changes difficult. **Why This Matters:** * CSS variables promote consistency and allow for easy theme customization. * Centralized variable definitions facilitate global style adjustments. **Code Example:** """css /* variables.module.css */ :root { --primary-color: #007bff; --secondary-color: #6c757d; --font-size-base: 16px; } """ """css /* Button.module.css */ .button { background-color: var(--primary-color); font-size: var(--font-size-base); color: white; padding: 10px 20px; } """ ## 2. CSS Modules Patterns and Best Practices Understanding and implementing established CSS Modules patterns can significantly improve the quality of your code. ### 2.1. Composition **Do This:** * Use "composes" to reuse styles from other CSS Modules or global CSS classes. * Carefully consider the order of composition to ensure correct style precedence. **Don't Do This:** * Overuse composition, as this can make styles difficult to trace. * Compose styles from unrelated components. **Why This Matters:** * Composition promotes code reuse, reducing duplication and improving maintainability. * It allows you to create variations of existing styles without rewriting them from scratch. **Code Example:** """css /* base.module.css */ .base { padding: 10px; border: 1px solid #ccc; } """ """css /* Button.module.css */ .button { composes: base from './base.module.css'; /* Composes styles from base.module.css */ background-color: blue; color: white; } """ **Anti-Pattern:** """css /* Button.module.css */ .button { /* Duplicates the styles from base.module.css instead of composing */ padding: 10px; border: 1px solid #ccc; background-color: blue; color: white; } """ ### 2.2. Selector Specificity **Do This:** * Keep selector specificity as low as possible. * Avoid nesting selectors excessively. * Use CSS variables to manage style variations instead of relying on complex selectors. **Don't Do This:** * Use "!important" to override styles, as this makes it difficult to manage style precedence. * Create overly specific selectors that are difficult to override. **Why This Matters:** * Low specificity makes it easier to override styles when needed. * It reduces the risk of unexpected style conflicts. **Code Example:** """css /* Good: Low specificity */ .button { /* ... */ } .button--primary { /* ... */ } """ """css /* Bad: High specificity */ .container .button:hover > span { /* Too specific */ /* ... */ } """ ### 2.3. State-Based Styling **Do This:** * Use CSS Modules to manage state-based styling, such as active, hover, or disabled states. * Dynamically apply class names based on component state. **Don't Do This:** * Use inline styles or JavaScript to directly manipulate styles, as this violates the separation of concerns. **Why This Matters:** * CSS Modules provide a declarative way to manage state-based styling. * It keeps styling logic separate from component logic, improving maintainability. **Code Example:** """jsx import styles from './Button.module.css'; import React, { useState } from 'react'; function Button({ children }) { const [isHovered, setIsHovered] = useState(false); return ( <button className={"${styles.button} ${isHovered ? styles.buttonHovered : ''}"} onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} > {children} </button> ); } export default Button; """ """css /* Button.module.css */ .button { /* ... */ } .buttonHovered { background-color: lightblue; } """ ### 2.4. Handling Media Queries and Responsive Design **Do This:** * Define media queries within your CSS Modules to create responsive layouts. * Use CSS variables to manage breakpoint values. * Consider using a CSS-in-JS library with CSS Modules-like scoping for more advanced responsive design patterns (e.g., styled-components with CSS Modules). **Don't Do This:** * Define media queries in global styles, as this can lead to unexpected side effects. * Hardcode breakpoint values in multiple places. **Why This Matters:** * Media queries allow you to adapt your styles to different screen sizes and devices. * CSS variables make it easier to manage breakpoint values consistently. **Code Example:** """css /* variables.module.css */ :root { --breakpoint-md: 768px; } """ """css /* Component.module.css */ .container { /* ... */ @media (min-width: var(--breakpoint-md)) { width: 50%; } } """ ## 3. Advanced CSS Modules Techniques These techniques enhance the power and flexibility of CSS Modules. ### 3.1. Using CSS Preprocessors (Sass, Less) **Do This:** * Use a CSS preprocessor like Sass or Less to add features like variables, mixins, and nesting to your CSS Modules. * Configure your build tool to process ".module.scss" or ".module.less" files as CSS Modules. **Don't Do This:** * Overuse preprocessor features such as nesting, as it can lead to overly specific selectors. **Why This Matters:** * CSS preprocessors can improve the maintainability and readability of your CSS Modules. * They provide features that are not available in plain CSS. **Code Example (Sass):** """scss /* Button.module.scss */ $primary-color: blue; .button { padding: 10px 20px; background-color: $primary-color; color: white; &:hover { opacity: 0.8; } } """ ### 3.2. Integration with CSS-in-JS Libraries **Do This:** * (Carefully) Consider integrating CSS Modules with CSS-in-JS libraries (e.g., styled-components, emotion) to enable dynamic styling and component-level theming with CSS Modules-like scoping. This is generally only needed for complex scenarios. **Don't Do This:** * Blindly replace CSS Modules with CSS-in-JS, as it can introduce performance overhead and complexity. **Why This Matters:** * CSS-in-JS libraries can offer benefits, such as dynamic themes and easier dynamic styling scenarios compared to "vanilla" modules. **Example (very basic, conceptual) using styled-components with CSS Module styles:** First import your desired styles: """jsx import React from 'react'; import styled from 'styled-components'; import styles from './MyComponent.module.css'; const StyledButton = styled.button" /* Inherit base styles from the CSS Module */ ${styles.baseButton}; /* Add custom styles using styled-components */ background-color: ${props => props.primary ? 'var(--primary-color)' : 'white'}; color: ${props => props.primary ? 'white' : 'var(--primary-color)'}; /* Compose Module styles */ &:hover{ ${styles.hoverEffect} } "; function MyComponent({ primary, children }) { return <StyledButton primary={primary}>{children}</StyledButton>; } export default MyComponent; """ """css /* MyComponent.module.css */ .baseButton { padding: 10px 20px; border: none; cursor: pointer; } .hoverEffect{ opacity: 0.8; } """ ### 3.3 Server-Side Rendering (SSR) Considerations **Do This:** * Ensure your CSS Modules are correctly handled during server-side rendering. Most frameworks now have built in module support. * Use libraries or build configurations that are compatible with server-side rendering of CSS Modules. **Don't Do This:** * Use hacks to inject styles during SSR, which can lead to performance issues and inconsistencies. **Why This Matters:** * Correct SSR ensures consistent rendering of styles on both the server and client. * This improves the user experience and avoids layout shifts during initial page load. ### 3.4 Testing CSS Modules **Do This:** * Write unit tests and integration tests for your CSS Modules to verify that styles are applied correctly. * Use tools like Jest and Enzyme to test React components with CSS Modules. * Utilize visual regression testing to catch unexpected style changes. **Don't Do This:** * Avoid testing CSS Modules altogether. **Why This Matters:** * Testing CSS Modules helps prevent regressions and ensures that styles remain consistent over time. * It provides confidence when refactoring or making changes to your stylesheets. ## 4. Performance Optimization Performance is a key consideration in any web application. These standards focus on optimizing the performance of your CSS Modules. ### 4.1. Minimizing CSS Bundle Size **Do This:** * Remove unused CSS rules from your CSS Modules files using tools like PurgeCSS or uncss. * Minify your CSS Modules files using a CSS minifier like cssnano. * Consider code splitting to load CSS Modules files only when they are needed. **Don't Do This:** * Include unnecessary CSS rules in your CSS Modules files. **Why This Matters:** * Smaller CSS bundle sizes result in faster page load times and improved performance. * Removing unused styles reduces the amount of code that the browser needs to parse and render. ### 4.2. Avoiding Layout Thrashing **Do This:** * Minimize the number of layout calculations that the browser needs to perform. * Avoid reading and writing to the DOM in the same frame. * Use CSS transforms and opacity instead of layout-triggering properties like "top", "left", "width", and "height". **Don't Do This:** * Force layout calculations unnecessarily. **Why This Matters:** * Layout thrashing can significantly degrade performance. * Using CSS transforms and opacity is more performant because they do not trigger layout calculations. ### 4.3. Optimizing Rendering Performance **Do This:** * Use the "will-change" property to inform the browser of upcoming changes to an element, allowing it to optimize rendering. * Throttle or debounce event handlers that trigger style changes. **Don't Do This:** * Overuse the "will-change" property, as it can consume excessive resources. **Why This Matters:** * Optimizing rendering performance can improve the responsiveness and smoothness of your application. * The "will-change" property can help the browser optimize rendering for specific elements. ## 5. Security Considerations While CSS Modules mitigate some cross-site scripting (XSS) risks, it's important to be aware of potential vulnerabilities. ### 5.1. Sanitizing User Input **Do This:** * Sanitize any user input that is used to dynamically generate CSS class names. * Use a library like DOMPurify to sanitize HTML content that includes CSS class names. **Don't Do This:** * Directly insert user input into CSS class names without sanitization. **Why This Matters:** * Sanitizing user input prevents XSS attacks by ensuring that malicious code is not injected into CSS class names. ### 5.2. Avoiding eval() and other risky functions **Do This:** * Avoid "eval()" or other functions that allow arbitrary code execution when handling style changes. * Stick to controlled ways of modifying class names. **Don't Do This:** * Use "eval()" or similar ways to inject style changes, as that can introduce XSS vulnerabilities. **Why This Matters:** * "eval()" and similar is a security risk and against security best practices.
# State Management Standards for CSS Modules This document outlines the recommended standards and best practices for managing application state, data flow, and reactivity when using CSS Modules. The focus is on modern approaches using established frameworks and patterns that integrate seamlessly with CSS Modules. ## 1. Principles of State Management with CSS Modules Effective state management is crucial for building maintainable and scalable applications. When working with CSS Modules, the goal is to connect component state to CSS classes dynamically, enabling visual changes based on data. This requires a clear understanding of how state changes trigger updates in the UI via CSS. * **Do This:** Use a unidirectional data flow whenever possible. State changes should propagate down to components that then update their CSS classes. * **Don't Do This:** Directly manipulate the DOM using JavaScript to apply styles unless absolutely necessary. CSS Modules are designed to abstract this complexity. Avoid two-way data binding with direct style manipulation. ### 1.1 Data Flow and Reactivity The core of state management is dictating how data flows through the application and how changes in the state propagate to the UI. * **Do This:** Favor libraries that provide observable state. Reactivity (automatic UI updates in response to state changes) is critical for dynamic CSS class management. * **Don't Do This:** Rely heavily on manual event handling and DOM manipulation to update CSS classes. This increases complexity and reduces maintainability. ### 1.2 Separation of Concerns Keep state management logic separate from component rendering logic. This maintains a clean separation of concerns, making the codebase more organized and testable. Style declarations should be kept in CSS Modules specifically. * **Do This:** Use dedicated state management solutions (e.g., Redux, Zustand, Recoil, Context API with reducers) to manage application state and pass the relevant data to components to conditionally apply CSS classes. * **Don't Do This:** Embed complex state management logic directly within functional components without properly separating it and making it reusable. ## 2. Framework-Specific Approaches Different frontend frameworks offer various ways to manage state and apply CSS Modules. The following sections detail best practices for popular frameworks. ### 2.1 React React's component-based architecture makes it suitable for integrating with a variety of state management solutions. #### 2.1.1 useState and useEffect For simple component-specific state, React's "useState" hook can be effectively combined with CSS Modules. The "useEffect" hook can be used to trigger side effects based on state changes, such as updating CSS classes. * **Do This:** Use "useState" for local component state that directly influences CSS class application. """jsx import React, { useState } from 'react'; import styles from './MyComponent.module.css'; function MyComponent() { const [isActive, setIsActive] = useState(false); const handleClick = () => { setIsActive(!isActive); }; return ( <div className={"${styles.myComponent} ${isActive ? styles.active : ''}"} onClick={handleClick} > Click me! </div> ); } export default MyComponent; """ * **Don't Do This:** Overuse "useState" for global application state. This can lead to prop drilling and make it harder to manage state across components. #### 2.1.2 Context API with useReducer For more complex component-specific state management, consider using the Context API with "useReducer". * **Do This:** Create a context provider and reducer to manage state that needs to be shared across multiple child components. """jsx // MyContext.js import React, { createContext, useReducer, useContext } from 'react'; const MyContext = createContext(); const initialState = { theme: 'light', }; const reducer = (state, action) => { switch (action.type) { case 'TOGGLE_THEME': return { ...state, theme: state.theme === 'light' ? 'dark' : 'light' }; default: return state; } }; const MyProvider = ({ children }) => { const [state, dispatch] = useReducer(reducer, initialState); return ( <MyContext.Provider value={{ state, dispatch }}> {children} </MyContext.Provider> ); }; const useMyContext = () => { const context = useContext(MyContext); if (!context) { throw new Error('useMyContext must be used within a MyProvider'); } return context; }; export { MyProvider, useMyContext }; // MyComponent.js import React from 'react'; import styles from './MyComponent.module.css'; import { useMyContext } from './MyContext'; function MyComponent() { const { state, dispatch } = useMyContext(); return ( <div className={"${styles.container} ${state.theme === 'dark' ? styles.darkTheme : styles.lightTheme}"}> <button onClick={() => dispatch({ type: 'TOGGLE_THEME' })}>Toggle Theme</button> </div> ); } export default MyComponent; // App.js import React from 'react'; import MyComponent from './MyComponent'; import { MyProvider } from './MyContext'; function App() { return ( <MyProvider> <MyComponent /> </MyProvider> ); } export default App; """ * **Don't Do This:** Create large, complex reducers for handling minimal pieces of state. Keep reducers focused and use multiple reducers for different state slices. #### 2.1.3 Redux For complex applications needing centralised state management, integrate Redux. * **Do This:** Connect components to the Redux store to access state and dispatch actions that update CSS classes. """jsx // actions.js export const SET_IS_LOADING = 'SET_IS_LOADING'; export const setIsLoading = (isLoading) => ({ type: SET_IS_LOADING, payload: isLoading, }); // reducer.js const initialState = { isLoading: false, }; const reducer = (state = initialState, action) => { switch (action.type) { case SET_IS_LOADING: return { ...state, isLoading: action.payload, }; default: return state; } }; export default reducer; // MyComponent.js import React from 'react'; import { connect } from 'react-redux'; import { setIsLoading } from './actions'; import styles from './MyComponent.module.css'; function MyComponent({ isLoading, setIsLoading }) { return ( <div className={"${styles.myComponent} ${isLoading ? styles.loading : ''}"}> <button onClick={() => setIsLoading(!isLoading)}> {isLoading ? 'Stop Loading' : 'Start Loading'} </button> </div> ); } const mapStateToProps = (state) => ({ isLoading: state.isLoading, }); const mapDispatchToProps = { setIsLoading, }; export default connect(mapStateToProps, mapDispatchToProps)(MyComponent); """ * **Don't Do This:** Dispatch actions directly from the component without using action creators. Action creators help maintain a consistent structure and make it easier to test the code. Avoid mutating the store's state directly; always return a new state object. #### 2.1.4 Zustand Zustand is a small, fast, and scalable bearbones state-management solution. It's a great alternative to Redux for smaller projects or when you need something simpler. * **Do This:** Define your state and actions within the "create" function, and then use the "useStore" hook to access state and actions in your components. """jsx // store.js import { create } from 'zustand'; const useStore = create((set) => ({ isMenuOpen: false, toggleMenu: () => set((state) => ({ isMenuOpen: !state.isMenuOpen })), })); export default useStore; // MyComponent.js import React from 'react'; import useStore from './store'; import styles from './MyComponent.module.css'; function MyComponent() { const isMenuOpen = useStore((state) => state.isMenuOpen); const toggleMenu = useStore((state) => state.toggleMenu); return ( <div className={styles.container}> <button onClick={toggleMenu}>Toggle Menu</button> <div className={"${styles.menu} ${isMenuOpen ? styles.open : styles.closed}"}> Menu Content </div> </div> ); } export default MyComponent; """ * **Don't Do This:** Directly mutate the state within the store; always use the "set" function to update the state immutably. Avoid creating very large stores with unrelated state; consider splitting them into smaller stores. ### 2.2 Angular Angular’s built-in RxJS observables and services make it effective for managing state, enabling reactivity and data flow suitable for CSS Modules. #### 2.2.1 Services and RxJS Observables Use Angular services to encapsulate state and expose it via RxJS Observables. Components subscribe to these observables to react to state changes. * **Do This:** Create services that manage state and expose it as an observable. """typescript // theme.service.ts import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root', }) export class ThemeService { private themeSubject = new BehaviorSubject<string>('light'); theme$ = this.themeSubject.asObservable(); toggleTheme() { this.themeSubject.next(this.themeSubject.value === 'light' ? 'dark' : 'light'); } } // my.component.ts import { Component, OnInit, OnDestroy } from '@angular/core'; import { ThemeService } from './theme.service'; import { Subscription } from 'rxjs'; import styles from './my.component.module.css'; // Import CSS Modules @Component({ selector: 'app-my-component', templateUrl: './my.component.html', styleUrls: ['./my.component.module.css'], }) export class MyComponent implements OnInit, OnDestroy { theme: string = 'light'; private themeSubscription: Subscription | undefined; constructor(private themeService: ThemeService) {} ngOnInit() { this.themeSubscription = this.themeService.theme$.subscribe((theme) => { this.theme = theme; }); } ngOnDestroy() { if (this.themeSubscription) { this.themeSubscription.unsubscribe(); } } toggleTheme() { this.themeService.toggleTheme(); } getStyles() { return { [styles.myComponent]: true, [styles.darkTheme]: this.theme === 'dark', [styles.lightTheme]: this.theme === 'light' }; } } // my.component.html <div [ngClass]="getStyles()"> <button (click)="toggleTheme()">Toggle Theme</button> </div> // my.component.module.css or my.component.module.scss .myComponent { padding: 10px; border: 1px solid #ccc; } .darkTheme { background-color: #333; color: white; } .lightTheme { background-color: white; color: black; } """ * **Don't Do This:** Directly manipulate the DOM from the service. Services should focus on managing state and exposing it via observables. Overuse "Subject" when "BehaviorSubject" or "ReplaySubject" might be more appropriate. #### 2.2.2 NgRx For complex applications, use NgRx for centralized state management. This pattern is very similar in approach conceptually to Redux with React. * **Do This:** Utilize actions, reducers, and selectors to manage state using best practices. """typescript // app.actions.ts import { createAction, props } from '@ngrx/store'; export const toggleLoading = createAction( '[App] Toggle Loading', props<{ isLoading: boolean }>() ); // app.reducer.ts import { createReducer, on } from '@ngrx/store'; import { toggleLoading } from './app.actions'; export interface AppState { isLoading: boolean; } const initialState: AppState = { isLoading: false, }; export const appReducer = createReducer( initialState, on(toggleLoading, (state, { isLoading }) => ({ ...state, isLoading })) ); """ * **Don't Do This:** Mutate state directly in reducers. Always return new state objects. Dispatch actions directly from the component without using selectors to retrieve state. ### 2.3 Vue.js Vue’s reactivity system and state management libraries provide streamlined approaches for maintaining state and dynamic CSS class binding with CSS Modules. #### 2.3.1 Vue Composition API Vue 3's Composition API, centered around reactive references (using "ref" and "reactive"), can be effectively combined with CSS Modules. * **Do This:** Use "ref" or "reactive" for component-specific state that directly influences CSS class application. """vue //MyComponent.vue <template> <div class="my-component" :class="{ 'my-component--active': isActive }" @click="toggleActive" > Click me! </div> </template> <script> import { ref } from 'vue'; import styles from './MyComponent.module.css'; export default { setup() { const isActive = ref(false); const toggleActive = () => { isActive.value = !isActive.value; }; return { isActive, toggleActive, styles, // Expose styles so we can use them in the template }; }, }; </script> <style module src="./MyComponent.module.css"></style> """ * **Don't Do This:** Directly modify DOM elements without using Vue's reactive bindings. Overcomplicate state management. Use the Composition API appropriately for individual component needs. #### 2.3.2 Pinia For centralized state management in Vue, Pinia offers a modern and lightweight solution. * **Do This:** Define Pinia stores to manage application state and access them in components. """javascript // stores/myStore.js import { defineStore } from 'pinia'; export const useMyStore = defineStore('myStore', { state: () => ({ isLoading: false, }), actions: { toggleLoading() { this.isLoading = !this.isLoading; }, }, }); //MyComponent.vue <template> <div :class="{ 'my-component--loading': myStore.isLoading }" class="my-component"> <button @click="myStore.toggleLoading"> {{ myStore.isLoading ? 'Stop Loading' : 'Start Loading' }} </button> </div> </template> <script> import { useMyStore } from '@/stores/myStore'; import styles from './MyComponent.module.css'; export default { setup() { const myStore = useMyStore(); return { myStore, styles, // Expose styles }; }, }; </script> <style module src="./MyComponent.module.css"></style> """ * **Don't Do This:** Mutate state directly without using actions. Avoid unnecessarily complex store structures. Use Pinia's features to organize state effectively. ## 3. Advanced Patterns ### 3.1 Conditional Class Names based on State Conditional class names are essential for applying styles based on application state. Several libraries and techniques can improve readability. * **Do This:** Use template literals or libraries like "classnames" or "clsx" for managing conditional class names effectively. """jsx // React example using classnames import React, { useState } from 'react'; import classNames from 'classnames'; import styles from './MyComponent.module.css'; function MyComponent() { const [isActive, setIsActive] = useState(false); return ( <div className={classNames(styles.myComponent, { [styles.active]: isActive })}> <button onClick={() => setIsActive(!isActive)}>Toggle Active</button> </div> ); } export default MyComponent; """ * **Don't Do This:** Manually concatenate strings for conditional class names, especially with many conditions. This reduces readability and increases maintainability. ### 3.2 Dynamic Styling based on Application Theme Applications that support different themes require a method to adapt CSS classes dynamically. * **Do This:** Utilize context or state management solutions to provide the current theme to components. Update CSS classes based on the active theme. """jsx // Example using React Context import React, { useContext } from 'react'; import { ThemeContext } from './ThemeContext'; import styles from './MyComponent.module.css'; function MyComponent() { const { theme } = useContext(ThemeContext); return ( <div className={"${styles.myComponent} ${theme === 'dark' ? styles.darkTheme : styles.lightTheme}"}> The current theme is: {theme} </div> ); } export default MyComponent; """ * **Don't Do This:** Hardcode theme-specific CSS classes within components. This limits extensibility and maintainability. ### 3.3 Performance Optimization State updates can trigger re-renders, which might impact performance. Optimize state management to minimize unnecessary renders. * **Do This:** Use memoization techniques (e.g., "React.memo", "useMemo", selectors in Redux) to prevent re-renders of components when props haven't changed. """jsx // React.memo example import React from 'react'; import styles from './MyComponent.module.css'; const MyComponent = React.memo(function MyComponent({ data }) { console.log('MyComponent rendered'); return ( <div className={styles.myComponent}> {data} </div> ); }); export default MyComponent; """ * **Don't Do This:** Update state unnecessarily, causing frequent re-renders. Pass immutable values as props to ensure effective memoization. ## 4. Security Considerations While CSS Modules reduce the risk of global CSS conflicts, security concerns still apply, especially when dynamic class names depend on user-controlled data. * **Do This:** Sanitize or validate user-provided input that influences CSS class names. Prevent CSS injection attacks by ensuring that class names are predefined and controlled. * **Don't Do This:** Use user-provided data directly to generate CSS class names. This can lead to CSS injection vulnerabilities, allowing attackers to inject arbitrary styles into the application. Avoid direct DOM manipulations that can bypass CSS Module protections. ## 5. Testing Ensure that state management logic interacting with CSS Modules is thoroughly tested. * **Do This:** Write unit tests for state management logic (reducers, actions, services) to verify state transitions and conditional CSS class application. * **Don't Do This:** Rely solely on integration tests. Unit tests offer faster feedback and isolate issues more effectively. Neglect testing the interactions between state changes and CSS class updates, leading to potential visual bugs. By following these standards, development teams can ensure their CSS Modules implementations are efficient, maintainable, and secure, providing a solid foundation for building scalable web applications.
# Performance Optimization Standards for CSS Modules This document outlines performance optimization standards for CSS Modules to improve application speed, responsiveness, and resource usage. ## 1. Code Splitting and Lazy Loading Applying code splitting and lazy loading to CSS Modules can drastically reduce initial load times, especially for large applications. ### 1.1. Standard: Component-Level CSS **Do This:** Structure your CSS Modules to align with your component structure. Only load the CSS required for the components that are currently visible or needed. **Don't Do This:** Create a giant, monolithic CSS file that loads all styles regardless of whether they are needed on the current page. **Why:** Loading unnecessary CSS increases the initial download size, delays rendering, and consumes memory. Component-level CSS improves caching and reduces the impact of CSS changes. **Code Example (React with "import"):** """jsx // Button.jsx import React from 'react'; import styles from './Button.module.css'; // Assuming a CSS Modules file function Button({ children, onClick }) { return ( <button className={styles.button} onClick={onClick}> {children} </button> ); } export default Button; """ """css /* Button.module.css */ .button { background-color: #4CAF50; border: none; color: white; padding: 15px 32px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; cursor: pointer; } """ **Explanation:** By importing styles directly into a component, the CSS is bundled as part of the component's module and is only loaded when the component is rendered, supporting automatic code splitting. ### 1.2. Standard: Optimize CSS Bundling **Do This:** Use modern bundlers (Webpack, Parcel, Rollup) with appropriate configurations for CSS Modules. Utilize plugins and loaders to further optimize CSS output. **Don't Do This:** Rely on simple concatenation or outdated bundling techniques that produce overly large CSS files without any optimization. **Why:** Proper bundling eliminates dead code, minifies CSS, and can optimize the order of CSS rules to reduce browser rendering time. **Code Example (Webpack configuration):** """javascript // webpack.config.js const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); module.exports = { //... module: { rules: [ { test: /\.module\.css$/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { modules: { localIdentName: '[hash:base64]', // Production-ready class names }, importLoaders: 1, }, }, 'postcss-loader', // For autoprefixer and other post-processing ], }, ], }, optimization: { minimizer: [ new CssMinimizerPlugin(), // Minify CSS ], splitChunks: { cacheGroups: { styles: { name: 'styles', type: 'css/mini-extract', chunks: 'all', enforce: true, }, }, }, }, plugins: [ new MiniCssExtractPlugin({ filename: '[name].[contenthash].css', // Unique filenames for cache busting }), ], }; """ **Explanation:** * "MiniCssExtractPlugin": Extracts CSS into separate files, critical for performance and parallel loading. * "css-loader": Enables the "modules" option specifically for CSS Modules (".module.css" files). "localIdentName" should be adjusted based on environment (e.g., "[path][name]__[local]" for development). * "postcss-loader": Applies post-processing like autoprefixer. * "CssMinimizerPlugin": Minifies the CSS, reducing file size without loss of fidelity. * "splitChunks": Splits CSS into separate chunks. ### 1.3. Standard: Dynamic Imports for Components **Do This:** Utilize dynamic imports ("import()") in JavaScript/JSX to lazy-load components, including their associated CSS Modules. **Don't Do This:** Load all components upfront, even those that are not immediately necessary. **Why:** Dynamic imports defer the loading of components and their styles until the component is actually needed. It reduces the initial JavaScript bundle size and improves page load time. **Code Example (React with dynamic "import()"):** """jsx // MyComponentLoader.jsx import React, { Suspense } from 'react'; const MyComponent = React.lazy(() => import('./MyComponent')); function MyComponentLoader() { return ( <Suspense fallback={<div>Loading...</div>}> <MyComponent /> </Suspense> ); } export default MyComponentLoader; """ """jsx // MyComponent.jsx import React from 'react'; import styles from './MyComponent.module.css'; function MyComponent() { return ( <div className={styles.container}> {/* Component Content */} </div> ); } export default MyComponent; """ **Explanation:** * "React.lazy()": Loads the "MyComponent" module (including its CSS) only when it's about to be rendered. * "Suspense": Provides a fallback UI while the component is loading and the CSS is fetched. The bundler will automatically split the CSS from "MyComponent.module.css" into a separate chunk. ## 2. CSS Optimizations Optimizing CSS itself is crucial for improving rendering performance. ### 2.1. Standard: Minimize CSS Selectors Complexity **Do This:** Prefer simple, direct CSS selectors. **Don't Do This:** Overly complex or deeply nested selectors. **Why:** Complex selectors require the browser to perform more calculations to determine which elements to style, which slows down rendering. CSS Modules can encourage simpler hierarchies and selectors. **Code Example (Good):** """css /* Button.module.css */ .button { font-size: 16px; padding: 10px 20px; } .primary { background-color: blue; color: white; } """ """jsx //Button.jsx import styles from './Button.module.css'; function Button({ children, primary }) { const buttonClasses = "${styles.button} ${primary ? styles.primary : ''}"; return <button className={buttonClasses}>{children}</button>; } """ **Code Example (Bad):** """css /* Bad Example - overly specific */ .container div button.button span { /* Avoid this */ font-size: 16px; } """ **Explanation:** The "Good" example uses simple, direct class selectors for styling the button and its variations. This is fast and efficient. The "Bad" example of a deeply nested selector is less performant as the browser has to traverse the DOM to find the elements. ### 2.2. Standard: Avoid "@import" in CSS **Do This:** Bundle all CSS using your bundler's mechanisms ("import" in JavaScript), or CSS native "@import" with modern bundler optimization. **Don't Do This:** Use "@import" within your CSS files without bundler support. **Why:** "@import" causes the browser to make additional HTTP requests to fetch CSS files, which delays rendering. Bundlers can inline or optimize "@import" use, but direct use usually leads to performance degradation. **Code Example (Good - bundler managed "@import"):** """css /* global.css */ @import './variables.css'; /* Bundler will often inline or combine this */ body { background-color: var(--body-background); } """ **Explanation:** When used through a bundler, "@import" can often be optimized away or handled efficiently. However, placing raw "@import" statements in CSS files without a bundler negates the benefits of CSS Modules and hurts performance. When importing from JS, the bundler has more control: """javascript import React from 'react'; import styles from './MyComponent.module.css'; import './global.css'; // global.css containing @imports function MyComponent() { return <div className={styles.container}>Hello</div> } export default MyComponent; """ ### 2.3. Standard: Use CSS Custom Properties (Variables) Wisely **Do This:** Utilize CSS custom properties (variables) for theming and reusable values. **Don't Do This:** Overuse CSS custom properties in performance-critical styles that change frequently through JavaScript, especially inside animations. In such cases, consider directly manipulating inline styles or pre-calculated values. **Why:** CSS custom properties can provide performance benefits when values are reused across many elements and are statically defined. However, overuse can lead to recalculations. **Code Example (Good):** """css /* variables.css */ :root { --primary-color: #007bff; --font-size: 16px; } /* Button.module.css */ .button { background-color: var(--primary-color); font-size: var(--font-size); } """ **Explanation:** Using CSS custom properties for global styling is generally performant. ### 2.4. Standard: Utilize "content-visibility: auto;" **Do This:** Utilize "content-visibility: auto;" to defer rendering of off-screen elements, especially in long scrolling pages. **Don't Do This:** Overlook the benefits of "content-visibility" on long pages or components that are initially off-screen. **Why:** "content-visibility: auto;" allows the browser to skip rendering the content until it enters the viewport, dramatically improving initial render time and scrolling performance. **Code Example (CSS):** """css .lazy-section { content-visibility: auto; contain-intrinsic-size: 1000px; /* Height must be known for initial layout */ } """ **Explanation:** * "content-visibility: auto;": Defers rendering until the element is in the viewport. * "contain-intrinsic-size": Specifies the size of the element when it is not rendered, preventing layout shifts. ## 3. Rendering Considerations Understanding how CSS Modules interact with the rendering pipeline is crucial for optimization. ### 3.1. Standard: Minimize Reflows and Repaints **Do This:** Batch DOM updates, avoid reading layout properties (e.g., "offsetTop", "offsetWidth") repeatedly in a single frame, and use CSS transforms for animations. **Don't Do This:** Force synchronous layout by repeatedly reading layout properties in a loop, or causing frequent changes to layout-affecting styles (e.g., "width", "height", "position"). **Why:** Reflows (layout recalculations) and repaints (redrawing parts of the screen) are expensive operations that can cause performance bottlenecks. Minimizing them improves responsiveness. **Code Example (Good - using CSS transforms):** """css /* FadeIn.module.css */ .fadeIn { opacity: 0; transform: translateY(20px); transition: opacity 0.3s ease-out, transform 0.3s ease-out; } .fadeIn.visible { opacity: 1; transform: translateY(0); } """ """jsx // FadeIn.jsx import React, { useState, useEffect, useRef } from 'react'; import styles from './FadeIn.module.css'; function FadeIn({ children }) { const [isVisible, setIsVisible] = useState(false); const domRef = useRef(); useEffect(() => { const observer = new IntersectionObserver(entries => { entries.forEach(entry => setIsVisible(entry.isIntersecting)); }); observer.observe(domRef.current); return () => observer.unobserve(domRef.current); }, []); return ( <div className={"${styles.fadeIn} ${isVisible ? styles.visible : ''}"} ref={domRef} > {children} </div> ); } export default FadeIn; """ **Explanation:** This "FadeIn" component uses CSS transforms (translateY) and opacity for the fade-in animation. Transforms and opacity changes are handled by the GPU where possible, resulting in smoother and more performant animations than animating layout properties such as "top" or "left". ### 3.2. Standard: Avoid Inline Styles **Do This:** Prefer CSS Modules over inline styles, wherever possible. **Don't Do This:** Use inline styles excessively, especially for complex styling or repeated elements. **Why:** Inline styles limit caching, reusability, and maintainability. CSS Modules, with their generated class names, offer better organization and allow for browser-level optimizations. **Code Example (Good):** """jsx // MyComponent.jsx import React from 'react'; import styles from './MyComponent.module.css'; function MyComponent() { return <div className={styles.container}>Content</div>; } export default MyComponent; """ """css /* MyComponent.module.css */ .container { padding: 20px; border: 1px solid #ccc; } """ **Explanation:** The styling is defined in the CSS Module and applied using a class name, offering better separation of concerns and browser optimization. ### 3.3. Standard: Font Optimization **Do This**: Use "font-display: swap;" or other suitable "font-display" values, preload important fonts, and consider using variable fonts to reduce file sizes. Optimize font files using tools like "fonttools". **Don't Do This**: Block rendering on font loading. Load overly large font files that contain glyphs or languages not required for the application. **Why:** Blocking rendering while fonts load can create a poor user experience. Optimizing font loading reduces the time to first contentful paint. "font-display: swap;" allows text to be displayed in a fallback font while the custom font loads. **Code Example (CSS):** """css /* global.css */ @font-face { font-family: 'MyCustomFont'; src: url('/fonts/MyCustomFont.woff2') format('woff2'); font-weight: normal; font-style: normal; font-display: swap; /* Use swap to avoid blocking rendering */ } body { font-family: 'MyCustomFont', sans-serif; } """ **Explanation:** * "font-display: swap;" indicates the browser should use a fallback font until the custom font is loaded. * Preloading: Utilize "<link rel="preload" href="/fonts/MyCustomFont.woff2" as="font" type="font/woff2" crossorigin>" in the HTML "<head>" to start loading the font as early as possible. ## 4. Specific CSS Modules Features Leveraging CSS Modules features can aid optimization. ### 4.1 Standard: Correctly Scope Global Styles **Do This:** Use ":global" sparingly for global styles; carefully scope and document their usage. **Don't Do This:** Overuse ":global", negating the benefits of CSS Modules' encapsulation. **Why:** Global styles can lead to naming conflicts and unexpected style overrides. Limiting their use maintains the modularity of CSS Modules and simplifies debugging. **Code Example (Using ":global" responsibly):** """css /* SpecificReset.module.css */ /* This is a contained reset for a specific element */ .container :global(ul) { margin: 0; padding: 0; list-style: none; } .container :global(a) { color: inherit; text-decoration: none; } """ """jsx // Usage example for a component import styles from './SpecificReset.module.css'; function MyComponent() { return ( <div className={styles.container}> <ul> <li><a href="#">Link 1</a></li> <li><a href="#">Link 2</a></li> </ul> </div> ); } export default MyComponent; """ **Explanation:** ":global" is used only within a specific component ("SpecificReset.module.css") to reset styles for "ul" and "a" within that component's scope. This limited, component-specific application of global styles helps prevent broader conflicts and maintains a degree of modularity. The alternative, adding global styles to "index.css", might be harder to track. ### 4.2. Standard: Composition with Caution **Do This:** Use "composes" judiciously for sharing styles between CSS Modules. Limit composition depth to avoid excessive selector complexity. **Don't Do This:** Create long chains of "composes" that are difficult to trace and negatively impact performance. **Why:** Composition allows you to reuse styles from other CSS Modules, which can improve maintainability. However, excessive composition increases selector complexity and affects rendering. **Code Example (Good):** """css /* BaseButton.module.css */ .baseButton { padding: 10px 20px; border: none; cursor: pointer; } /* PrimaryButton.module.css*/ .primaryButton { background-color: blue; color: white; composes: baseButton from './BaseButton.module.css'; /* Limited Composition */ } """ **Explanation:** The ".primaryButton" composes styles from ".baseButton", which keeps the class inheritance simple. ### 4.3 Standard: Pseudo-classes and Pseudo-elements **Do This:** Ensure that pseudo-classes and pseudo-elements are performant within CSS Modules, especially when combined with JavaScript interactions. **Don't Do This:** Neglect the performance implications of pseudo-classes and pseudo-elements, particularly in dynamic or heavily interactive components. **Why:** Misusing pseudo-classes and pseudo-elements can add unnecessary complexity, especially within animations or dynamic styling. Proper usage ensures smooth interactions. **Code Example (Good):** """css /* Button.module.css */ .button { background-color: #4CAF50; border: none; color: white; padding: 15px 32px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; cursor: pointer; } .button:hover { background-color: #3e8e41; /* Change on hover */ } .button::after { content: ""; /* Add visual effect */ display: block; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.1); opacity: 0; transition: opacity 0.2s ease-out; } .button:hover::after { opacity: 1; /* Fade in on hover */ } """ **Explanation:** The hover effect uses "background-color" and "opacity" changes, which are performant. The "::after" pseudo-element adds a subtle visual effect that transitions smoothly, controlled via "opacity". ## 5. Tooling Using the right tools can significantly improve performance by automating optimization processes. ### 5.1. Standard: CSSNano or other CSS Minifiers **Do This:** Integrate CSS minifiers (like CSSNano) as part of your build process. **Don't Do This:** Deploy unminified CSS to production. **Why:** CSS minifiers remove whitespace, comments, and other unnecessary characters, reducing file size without affecting functionality. **Code Example (PostCSS Configuration with CSSNano):** """javascript // postcss.config.js module.exports = { plugins: [ require('postcss-preset-env')({ browsers: 'last 2 versions', stage: 3, features: { 'nesting-rules': true } }), require('cssnano')({ preset: 'default', }), ], }; """ **Explanation:** This configuration uses PostCSS with "cssnano" to automatically minify CSS during the build process. ### 5.2. Standard: Linting **Do This:** Use CSS linters (like Stylelint) to enforce coding standards, identify potential performance issues, and catch errors early in the development process. Use appropriate plugins and configurations for CSS Modules. **Don't Do This:** Skip linting, which can lead to inconsistent code, styling errors, and performance regressions. **Code Example (Stylelint Configuration with CSS Modules support):** """javascript // .stylelintrc.js module.exports = { extends: [ 'stylelint-config-standard', 'stylelint-config-css-modules', 'stylelint-config-rational-order', ], plugins: [ 'stylelint-order', ], rules: { // Add/override rules as needed 'selector-class-pattern': null, // Disable class name format check 'declaration-empty-line-before': 'never' }, }; """ **Explanation:** * "stylelint-config-standard": Provides standard linting rules. * "stylelint-config-css-modules": Adds CSS Modules-specific linting rules. * "stylelint-config-rational-order": Helps enforce a rational ordering of CSS properties. * "selector-class-pattern: null": Disables the class name format checking (as CSS Modules often generate unique names). * "declaration-empty-line-before: 'never'": Removes declaration empty lines for cleaner code. ### 5.3. Standard: Performance Monitoring **Do This:** Implement performance monitoring tools (like Lighthouse, WebPageTest, or browser-based performance profiling) to identify areas for improvement. **Don't Do This:** Assume your CSS is performant without objective measurement. **Why:** Performance monitoring provides data-driven insights into rendering times, resource loading, and other performance metrics. It helps you prioritize optimization efforts. **Explanation:** Regularly run audits using Lighthouse or WebPageTest to identify areas of improvement in your CSS. Pay particular attention to metrics like First Contentful Paint (FCP), Largest Contentful Paint (LCP), and Cumulative Layout Shift (CLS). Browser-based performance profiling (using Chrome DevTools, for example) can give detailed timing information about CSS selector matching and rendering.
# Testing Methodologies Standards for CSS Modules This document outlines the testing standards for CSS Modules within our development environment. It provides guidelines for unit, integration, and end-to-end testing, specifically tailored to CSS Modules. Adhering to these standards ensures the maintainability, reliability, and visual consistency of our applications. ## 1. General Testing Principles ### 1.1. Testing Pyramid Adherence * **Do This:** Follow the testing pyramid approach: a large number of unit tests, a smaller number of integration tests, and a very small number of end-to-end tests. * **Why:** Balances adequate testing coverage with efficient use of resources. * **Don't Do This:** Rely heavily on end-to-end tests for visual regression. This slows down the test suite and focuses on implementation details rather than component behavior. ### 1.2. Focus on Component Contracts * **Do This:** Test the *contract* of a component: that it renders correctly under specific conditions and that its internal state changes as expected based on user interaction. This often involves testing that the correct CSS classes are applied. * **Why:** Supports a component-driven architecture and allows for easier refactoring. * **Don't Do This:** Test implementation details that are likely to change. For example, avoid testing specific CSS property values unless absolutely necessary. ### 1.3. Test-Driven Development (TDD) * **Do This:** Write tests *before* writing the actual CSS and component code. * **Why:** Encourages a clear understanding of the component's requirements before implementation. * **Don't Do This:** Write tests as an afterthought, which often leads to ad-hoc testing and incomplete coverage. ## 2. Unit Testing CSS Modules Unit tests focus on the smallest testable parts of your application, typically individual components. For CSS Modules, this primarily means validating the correct application of CSS classes. ### 2.1. Tools and Libraries * **Do This:** Use a testing framework like Jest, Mocha, or Jasmine, along with a library like React Testing Library or Enzyme. * **Why:** These tools provide the necessary features for running tests, simulating user interactions, and asserting on component output. * **Example:** Using Jest and React Testing Library: """javascript // component.test.js import React from 'react'; import { render, screen } from '@testing-library/react'; import Button from './Button'; import styles from './Button.module.css'; describe('Button Component', () => { it('should render with the primary style', () => { render(<Button primary>Click Me</Button>); const buttonElement = screen.getByText('Click Me'); expect(buttonElement).toHaveClass(styles.primary); }); it('should apply custom class names', () => { render(<Button className="extra-class">Click Me</Button>); const buttonElement = screen.getByText('Click Me'); expect(buttonElement).toHaveClass('extra-class'); }); it('should render with correct text', () => { render(<Button>Submit</Button>); const buttonElement = screen.getByText('Submit'); expect(buttonElement).toBeInTheDocument(); }); it('should disable the button', () => { render(<Button disabled>Click Me</Button>); const buttonElement = screen.getByText('Click Me'); expect(buttonElement).toBeDisabled(); } ); it('should renders correctly with styled-components and CSS Modules together', () => { const StyledButton = styled.button" background-color: lightblue; // Direct CSS for demonstration "; render(<StyledButton className={styles.primary}>Styled Button with CSS Module</StyledButton>); const buttonElement = screen.getByText('Styled Button with CSS Module'); expect(buttonElement).toHaveClass(styles.primary); expect(getComputedStyle(buttonElement).backgroundColor).toBe('lightblue'); }); }); """ """css /* Button.module.css */ .primary { background-color: #007bff; color: white; padding: 10px 20px; border: none; cursor: pointer; } """ ### 2.2. Testing Class Names * **Do This:** Use CSS Modules to generate unique class names and verify those class names are applied correctly in your unit tests. * **Why:** Ensures styles are applied as expected and that components are visually consistent. * **Don't Do This:** Test the presence of specific CSS properties directly. Focus on the semantic meaning of the class names. * **Example:** """javascript // MyComponent.test.js import React from 'react'; import { render, screen } from '@testing-library/react'; import MyComponent from './MyComponent'; import styles from './MyComponent.module.css'; describe('MyComponent', () => { it('should render with the "container" class', () => { render(<MyComponent />); const containerElement = screen.getByTestId('my-component-container'); expect(containerElement).toHaveClass(styles.container); }); }); """ """jsx // MyComponent.jsx import React from 'react'; import styles from './MyComponent.module.css'; function MyComponent() { return ( <div className={styles.container} data-testid="my-component-container"> Content </div> ); } export default MyComponent; """ """css /* MyComponent.module.css */ .container { border: 1px solid black; padding: 10px; } """ ### 2.3. Mocking External Dependencies * **Do This:** Mock external dependencies, such as APIs or global state, to isolate the component under test. * **Why:** Ensures that tests are focused on the component's behavior and aren't affected by external factors. * **Don't Do This:** Rely on real APIs or databases during unit testing, making tests slow and flaky. * **Example:** """javascript // MyComponent.test.js import React from 'react'; import { render, screen, waitFor } from '@testing-library/react'; import MyComponent from './MyComponent'; import styles from './MyComponent.module.css'; // Mock the API call jest.mock('./api', () => ({ getData: jest.fn(() => Promise.resolve({ message: 'Mocked Data' })), })); import { getData } from './api'; describe('MyComponent with API', () => { it('should display data from the API', async () => { render(<MyComponent />); await waitFor(() => { expect(screen.getByText('Mocked Data')).toBeInTheDocument(); }); expect(screen.getByTestId('data-container')).toHaveClass(styles.dataContainer); // Assert that the mock API function was called expect(getData).toHaveBeenCalledTimes(1); }); }); """ """jsx // MyComponent.jsx import React, { useState, useEffect } from 'react'; import styles from './MyComponent.module.css'; import { getData } from './api'; function MyComponent() { const [data, setData] = useState(null); useEffect(() => { getData().then(result => setData(result)); }, []); return ( <div data-testid="data-container" className={styles.dataContainer}> {data ? data.message : 'Loading...'} </div> ); } export default MyComponent; """ """css /* MyComponent.module.css */ .dataContainer { padding: 16px; border: 1px solid #ccc; margin-top: 10px; } """ ### 2.4 Handling Conditional Styling * **Do This:** When CSS Modules are applied conditionally, write specific tests for each condition. Employ "data-testid" attributes effectively to isolate and target DOM nodes for assertion. * **Why:** Conditional styling is a common pattern, and tests ensure that styles are applied consistently based on application or component state. Effective use of "data-testid" improves the robustness and maintainability of your tests by providing reliable selectors, rather than relying on potentially volatile class names or content. * **Don't Do This:** Only test the default case. Ensure all conditions are considered. * **Example:** """javascript // Alert.test.js import React from 'react'; import { render, screen } from '@testing-library/react'; import Alert from './Alert'; import styles from './Alert.module.css'; describe('Alert Component', () => { it('should render with success style when type is "success"', () => { render(<Alert type="success" message="Operation successful!" />); const alertElement = screen.getByTestId('alert-container'); expect(alertElement).toHaveClass(styles.success); }); it('should render with error style when type is "error"', () => { render(<Alert type="error" message="An error occurred!" />); const alertElement = screen.getByTestId('alert-container'); expect(alertElement).toHaveClass(styles.error); }); it('should render with warning style when type is "warning"', () => { render(<Alert type="warning" message="A warning message!" />); const alertElement = screen.getByTestId('alert-container'); expect(alertElement).toHaveClass(styles.warning); }); it('should render the correct message', () => { render(<Alert type="success" message="Custom message." />); const alertMessage = screen.getByText('Custom message.'); expect(alertMessage).toBeInTheDocument(); }); }); """ """jsx // Alert.jsx import React from 'react'; import styles from './Alert.module.css'; function Alert({ type, message }) { let alertClass = styles.base; switch (type) { case 'success': alertClass = "${alertClass} ${styles.success}"; break; case 'error': alertClass = "${alertClass} ${styles.error}"; break; case 'warning': alertClass = "${alertClass} ${styles.warning}"; break; default: break; } return ( <div data-testid="alert-container" className={alertClass}> {message} </div> ); } export default Alert; """ """css /* Alert.module.css */ .base { padding: 10px; margin: 5px; border: 1px solid transparent; border-radius: 4px; } .success { color: #3c763d; background-color: #dff0d8; border-color: #d6e9c6; } .error { color: #a94442; background-color: #f2dede; border-color: #ebccd1; } .warning { color: #8a6d3b; background-color: #fcf8e3; border-color: #faebcc; } """ ## 3. Integration Testing CSS Modules Integration tests verify the interaction between multiple components. In the context of CSS Modules, this means testing how different styled components work together. ### 3.1. Testing Component Composition * **Do This:** Verify that composed components render correctly with the intended styles when combined. * **Why:** Ensures that different parts of the application work seamlessly together. * **Don't Do This:** Duplicate unit test logic in integration tests. Focus on the interaction. * **Example:** """javascript // Layout.test.js import React from 'react'; import { render, screen } from '@testing-library/react'; import Layout from './Layout'; describe('Layout Component', () => { it('should render header and content', () => { render( <Layout> <div>Content Area</div> </Layout> ); expect(screen.getByRole('banner')).toBeInTheDocument(); // Header expect(screen.getByText('Content Area')).toBeInTheDocument(); // Content }); it('should apply layout specific styles', () => { render( <Layout> <div>Content</div> </Layout> ); const layoutContainer = screen.getByTestId('layout-container'); expect(layoutContainer).toHaveStyle('padding: 20px;'); }); }); """ """jsx // Layout.jsx import React from 'react'; import Header from './Header'; import styles from './Layout.module.css'; function Layout({ children }) { return ( <div data-testid="layout-container" className={styles.container}> <Header /> <main> {children} </main> </div> ); } export default Layout; """ """jsx // Header.jsx import React from 'react'; import styles from './Header.module.css'; function Header() { return ( <header className={styles.header} role="banner"> <h1>My App</h1> </header> ); } export default Header; """ """css /* Layout.module.css */ .container { padding: 20px; } """ """css /* Header.module.css */ .header { background-color: #f0f0f0; padding: 10px; } """ ### 3.2. State Management Integration * **Do This:** If using state management libraries like Redux or Zustand, test how components react to state changes and apply styles accordingly. This often involves mocking store interactions or using a testing provider. * **Why:** Ensures styles are updated correctly based on application state. * **Don't Do This:** Skip testing state-driven styling, as it's a common source of bugs. * **Example (Redux):** Assume you have a component that displays a theme based on a redux store value. """javascript // ThemeSwitch.test.js import React from 'react'; import { render, screen, fireEvent } from '@testing-library/react'; import { Provider } from 'react-redux'; import configureStore from 'redux-mock-store'; import ThemeSwitch from './ThemeSwitch'; import styles from './ThemeSwitch.module.css'; const mockStore = configureStore([]); describe('ThemeSwitch Component', () => { it('should render with the light theme initially', () => { const initialState = { theme: { isDarkMode: false } }; const store = mockStore(initialState); render( <Provider store={store}> <ThemeSwitch /> </Provider> ); const switchElement = screen.getByRole('checkbox'); expect(switchElement).not.toBeChecked(); }); it('should toggle the theme when the switch is clicked', () => { const initialState = { theme: { isDarkMode: false } }; const store = mockStore(initialState); render( <Provider store={store}> <ThemeSwitch /> </Provider> ); const switchElement = screen.getByRole('checkbox'); fireEvent.click(switchElement); const actions = store.getActions(); expect(actions).toEqual([{ type: 'TOGGLE_THEME' }]); }); it('should render with the dark theme when isDarkMode is true', () => { const initialState = { theme: { isDarkMode: true } }; const store = mockStore(initialState); render( <Provider store={store}> <ThemeSwitch /> </Provider> ); const switchElement = screen.getByRole('checkbox'); expect(switchElement).toBeChecked(); }); }); """ """jsx // ThemeSwitch.jsx import React from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { toggleTheme } from './themeActions'; import styles from './ThemeSwitch.module.css'; function ThemeSwitch() { const dispatch = useDispatch(); const isDarkMode = useSelector(state => state.theme.isDarkMode); const handleToggle = () => { dispatch(toggleTheme()); }; return ( <label className={styles.switch}> <input type="checkbox" checked={isDarkMode} onChange={handleToggle} role="checkbox" /> <span className={styles.slider}></span> </label> ); } export default ThemeSwitch; """ """css /* ThemeSwitch.module.css */ .switch { position: relative; display: inline-block; width: 60px; height: 34px; } .switch input { opacity: 0; width: 0; height: 0; } .slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; transition: .4s; border-radius: 34px; } .slider:before { position: absolute; content: ""; height: 26px; width: 26px; left: 4px; bottom: 4px; background-color: white; transition: .4s; border-radius: 50%; } input:checked + .slider { background-color: #2196F3; } input:focus + .slider { box-shadow: 0 0 1px #2196F3; } input:checked + .slider:before { transform: translateX(26px); } """ ## 4. End-to-End (E2E) Testing CSS Modules End-to-end tests validate the entire application flow, including UI, API calls, and data persistence. ### 4.1. Visual Regression Testing * **Do This:** Implement visual regression testing to detect unintended visual changes using tools like Cypress, Playwright, or Jest Image Snapshot. * **Why:** Ensures consistent UI across different browsers and environments. * **Don't Do This:** Rely solely on manual visual inspection. Automate the process. ### 4.2. Browser and Device Compatibility * **Do This:** Run E2E tests across different browsers (Chrome, Firefox, Safari) and devices to ensure compatibility. Use browserstack, saucelabs, or similar platforms to run tests in a variety of emulated or real environments. * **Why:** Avoids unexpected visual issues due to browser-specific rendering differences. * **Don't Do This:** Only test on a single browser during development. ### 4.3. Accessibility Testing * **Do This:** Integrate accessibility testing tools into your E2E tests to ensure the application is usable for people with disabilities. (e.g. axe-core). Focus on testing semantic HTML and ARIA attributes. Verify correct contrast ratios on text elements styled with CSS Modules. * **Why:** Improves inclusivity and meets accessibility standards (WCAG). * **Don't Do This:** Ignore accessibility testing until late in the development cycle. ### 4.4. Example using Playwright for visual regression First, install Playwright: """bash npm install -D @playwright/test npx playwright install --with-deps """ Then, create a test file, e.g., "homepage.spec.js": """javascript // homepage.spec.js const { test, expect } = require('@playwright/test'); test('Homepage has correct title and visual appearance', async ({ page }) => { await page.goto('http://localhost:3000'); // Replace with your app's URL // Title check await expect(page).toHaveTitle('My Awesome App'); // Take a screenshot and compare it against a baseline expect(await page.screenshot()).toMatchSnapshot('homepage.png'); }); """ In your "playwright.config.js": """javascript // playwright.config.js const { defineConfig } = require('@playwright/test'); module.exports = defineConfig({ // ... other configurations testDir: './tests', snapshotDir: './__snapshots__', updateSnapshots: 'missing', // or 'all' to update all snapshots }); """ Run the tests, and Playwright will create initial screenshots. Subsequent runs will compare against these. """bash npx playwright test """ ## 5. Maintaining Test Quality ### 5.1. Code Reviews * **Do This:** Include test code in your code reviews to ensure that tests are well-written and comprehensive. * **Why:** Improves code quality and prevents regressions. * **Don't Do This:** Skip reviewing test code. ### 5.2. Test Coverage Metrics * **Do This:** Track test coverage metrics (line, branch, function) using tools like Jest's built-in coverage reports or SonarQube. Aim for high coverage and prioritize critical areas. * **Why:** Identifies gaps in testing and ensures that all important code is covered. * **Don't Do This:** Treat coverage metrics as the only indicator of test quality. Focus on meaningful tests. ### 5.3. Regular Test Execution * **Do This:** Run tests regularly as part of your CI/CD pipeline and during local development. * **Why:** Detects regressions early and prevents them from reaching production. * **Don't Do This:** Only run tests before major releases. By adhering to these testing standards, we can ensure that our CSS Modules are implemented correctly, maintain visual consistency, and prevent regressions, leading to a more robust and reliable application. Remember that this is a living document, and we should continuously update it as new technologies and best practices emerge.
# API Integration Standards for CSS Modules This document outlines the coding standards for integrating CSS Modules with backend services and external APIs. It focuses on how to manage dynamic styling and data-driven presentation in a modular and maintainable way. ## 1. Principles of API Integration with CSS Modules Integrating APIs with CSS Modules requires careful planning to maintain separation of concerns and avoid tightly coupling styling with data fetching logic. * **Do This**: Separate data fetching and processing logic from CSS Modules. * **Don't Do This**: Directly manipulate the DOM or inline styles within the CSS Modules file based on API responses. ### 1.1 Maintainability Having a clear separation between API calls and styling logic improves the maintainability. * **Do This**: Use container components or services to fetch and process data. Pass the necessary props (including class names from CSS Modules) to presentational components. * **Don't Do This**: Embed data-fetching logic directly into the CSS Modules or components responsible for rendering styles. ### 1.2 Performance Optimization Inefficient API integration can significantly impact performance. * **Do This**: Implement caching strategies, memoization, and lazy loading where applicable to minimize API calls and optimize rendering. * **Don't Do This**: Make redundant API calls or recalculate styles unnecessarily. ### 1.3 Security Properly handling API keys and sensitive data is crucial for security. * **Do This**: Store API keys securely (e.g., environment variables) and avoid exposing them in client-side code. Sanitize any data received from the API to prevent XSS vulnerabilities. * **Don't Do This**: Hardcode API keys directly in the CSS Modules or expose sensitive data unnecessarily. ## 2. Architecture and Design Patterns The architectural approach significantly impacts the overall maintainability and scalability of your application. ### 2.1 Container/Presentational Pattern Adopting the container/presentational component pattern is a robust way to integrate API data with CSS Modules. Container components handle data fetching and pass the processed data to presentational components, which then render the UI using CSS Modules for styling. * **Do This**: Create container components that fetch data from the API. * **Do This**: Create presentational components that receive data as props and apply styles using CSS Modules. * **Don't Do This**: Mix data fetching and styling logic within the same component. **Code Example:** """jsx // Container Component (e.g., src/containers/ProductListContainer.jsx) import React, { useState, useEffect } from 'react'; import ProductList from '../components/ProductList'; import styles from './ProductListContainer.module.css'; // Container-specific styles function ProductListContainer() { const [products, setProducts] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { const fetchData = async () => { try { const response = await fetch('https://api.example.com/products'); if (!response.ok) { throw new Error('Network response was not ok'); } const data = await response.json(); setProducts(data); } catch (err) { setError(err); } finally { setLoading(false); } }; fetchData(); }, []); if (loading) { return <div className={styles.loading}>Loading products...</div>; } if (error) { return <div className={styles.error}>Error: {error.message}</div>; } return ( <div className={styles.container}> <h1>Product List</h1> <ProductList products={products} /> </div> ); } export default ProductListContainer; // src/containers/ProductListContainer.module.css .container { padding: 20px; } .loading { font-style: italic; } .error { color: red; } """ """jsx // Presentational Component (e.g., src/components/ProductList.jsx) import React from 'react'; import Product from './Product'; import styles from './ProductList.module.css'; function ProductList({ products }) { return ( <ul className={styles.productList}> {products.map(product => ( <li key={product.id} className={styles.productItem}> <Product product={product} /> </li> ))} </ul> ); } export default ProductList; // src/components/ProductList.module.css .productList { list-style: none; padding: 0; } .productItem { margin-bottom: 10px; } """ """jsx // Presentational Component (e.g., src/components/Product.jsx) import React from 'react'; import styles from './Product.module.css'; function Product({ product }) { return ( <div className={styles.productCard}> <h2 className={styles.productName}>{product.name}</h2> <p className={styles.productDescription}>{product.description}</p> <span className={styles.productPrice}>${product.price}</span> </div> ); } export default Product; // src/components/Product.module.css .productCard { border: 1px solid #ccc; padding: 10px; margin-bottom: 10px; } .productName { font-size: 1.2em; margin-bottom: 5px; } .productDescription { font-size: 0.9em; color: #666; } .productPrice { font-weight: bold; color: green; } """ ### 2.2 Hooks for Data Fetching Custom hooks can encapsulate data fetching logic, making components cleaner and more reusable. * **Do This**: Create custom hooks to handle API calls and data transformations. * **Don't Do This**: Duplicate data fetching code across multiple components. **Code Example:** """jsx // Custom Hook (e.g., src/hooks/useProducts.js) import { useState, useEffect } from 'react'; function useProducts(url) { const [products, setProducts] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { const fetchData = async () => { try { const response = await fetch(url); if (!response.ok) { throw new Error('Network response was not ok'); } const data = await response.json(); setProducts(data); } catch (err) { setError(err); } finally { setLoading(false); } }; fetchData(); }, [url]); return { products, loading, error }; } export default useProducts; """ """jsx // Component (e.g., src/components/ProductList.jsx) import React from 'react'; import useProducts from '../hooks/useProducts'; import Product from './Product'; import styles from './ProductList.module.css'; function ProductList() { const { products, loading, error } = useProducts('https://api.example.com/products'); if (loading) { return <div>Loading products...</div>; } if (error) { return <div>Error: {error.message}</div>; } return ( <ul className={styles.productList}> {products.map(product => ( <li key={product.id} className={styles.productItem}> <Product product={product} /> </li> ))} </ul> ); } export default ProductList; // src/components/ProductList.module.css .productList { list-style: none; padding: 0; } .productItem { margin-bottom: 10px; } """ ### 2.3 State Management Libraries Libraries like Redux, Zustand, or React Context can manage application state (including API data) and provide a centralized way to access and update it. * **Do This**: Use state management libraries to handle complex data interactions across components. * **Don't Do This**: Rely solely on prop drilling for deeply nested components requiring API data; prop drilling hurts maintainability. ## 3. Dynamic Styling Based on API Data A common use case is to dynamically adjust styling based on the data received from an API. ### 3.1 Conditional Class Names Apply different CSS classes based on the API data. * **Do This**: Use template literals or libraries like "classnames" to conditionally apply CSS classes. * **Don't Do This**: Use inline styles excessively, as they reduce maintainability and prevent CSS Modules from effectively managing styles. **Code Example:** """jsx // Component (e.g., src/components/Product.jsx) import React from 'react'; import styles from './Product.module.css'; import classNames from 'classnames'; function Product({ product }) { const availabilityClass = classNames({ [styles.available]: product.isAvailable, [styles.unavailable]: !product.isAvailable, }); return ( <div className={styles.productCard}> <h2 className={styles.productName}>{product.name}</h2> <p className={styles.productDescription}>{product.description}</p> <span className={styles.productPrice}>${product.price}</span> <span className={availabilityClass}> {product.isAvailable ? 'Available' : 'Unavailable'} </span> </div> ); } export default Product; // src/components/Product.module.css .productCard { border: 1px solid #ccc; padding: 10px; margin-bottom: 10px; } .productName { font-size: 1.2em; margin-bottom: 5px; } .productDescription { font-size: 0.9em; color: #666; } .productPrice { font-weight: bold; color: green; } .available { color: green; font-weight: bold; } .unavailable { color: red; font-weight: bold; } """ ### 3.2 CSS Variables (Custom Properties) Use CSS variables to dynamically change style values based on API data. CSS variables are powerfully dynamic and can be updated directly from JavaScript. * **Do This**: Set CSS variables based on API data using inline styles or JavaScript. * **Don't Do This**: Overuse inline styles for static styling; CSS variables are meant for dynamically changing style values. **Code Example:** """jsx // Component (e.g., src/components/Product.jsx) import React from 'react'; import styles from './Product.module.css'; function Product({ product }) { const cardStyle = { '--product-color': product.color, // Assuming 'color' is a property from the API }; return ( <div className={styles.productCard} style={cardStyle}> <h2 className={styles.productName}>{product.name}</h2> <p className={styles.productDescription}>{product.description}</p> <span className={styles.productPrice}>${product.price}</span> </div> ); } export default Product; """ """css /* src/components/Product.module.css */ .productCard { border: 1px solid #ccc; padding: 10px; margin-bottom: 10px; background-color: var(--product-color, #fff); /* Default to white if variable is not set */ } .productName { font-size: 1.2em; margin-bottom: 5px; } .productDescription { font-size: 0.9em; color: #666; } .productPrice { font-weight: bold; color: green; } """ ### 3.3 Styling with Server-Side Rendering (SSR) When using SSR, ensure that API data is available during the rendering process. * **Do This**: Fetch API data on the server and pass it as props to the component. * **Don't Do This**: Rely solely on client-side data fetching, as it can lead to hydration issues and poor SEO. ## 4. Handling Loading and Error States Provide users with clear feedback while data is being fetched or when errors occur. * **Do This**: Display loading indicators and error messages appropriately. * **Don't Do This**: Leave users guessing about the state of the application. **Code Example:** """jsx // Component (e.g., src/components/ProductList.jsx) import React from 'react'; import useProducts from '../hooks/useProducts'; import Product from './Product'; import styles from './ProductList.module.css'; function ProductList() { const { products, loading, error } = useProducts('https://api.example.com/products'); if (loading) { return <div className={styles.loading}>Loading products...</div>; } if (error) { return <div className={styles.error}>Error: {error.message}</div>; } return ( <ul className={styles.productList}> {products.map(product => ( <li key={product.id} className={styles.productItem}> <Product product={product} /> </li> ))} </ul> ); } export default ProductList; // src/components/ProductList.module.css .productList { list-style: none; padding: 0; } .productItem { margin-bottom: 10px; } .loading { font-style: italic; } .error { color: red; } """ ## 5. Best Practices for External API Interactions Interacting with external APIs requires extra care to ensure performance, security, and reliability. ### 5.1 Caching Strategies Implement caching to reduce the number of API calls. * **Do This**: Use browser caching (e.g., "Cache-Control" headers), service workers, or dedicated caching libraries. * **Don't Do This**: Make redundant API calls for data that rarely changes. ### 5.2 Error Handling and Retry Mechanisms Handle API errors gracefully and implement retry mechanisms for transient failures. * **Do This**: Use "try...catch" blocks and error boundary components to handle API errors. Implement exponential backoff for retrying failed requests. * **Don't Do This**: Expose raw error messages to users or fail silently. ### 5.3 Rate Limiting Respect API rate limits to avoid being throttled or blocked. * **Do This**: Monitor API usage and implement strategies to stay within rate limits, such as queuing requests or implementing client-side throttling. * **Don't Do This**: Exceed rate limits and cause disruptions to the application. ## 6. Security Considerations Safeguarding API keys and user data is paramount. * **API Key Protection**: Use environment variables and store credentials securely. Never commit API keys directly to the codebase. * **Data Sanitization**: Sanitize all data received from the API before rendering it to prevent Cross-Site Scripting (XSS) vulnerabilities. Use tools and libraries that automatically escape or sanitize user-provided content. * **HTTPS**: Always use HTTPS for API requests to encrypt data in transit. By adhering to these guidelines, developers can effectively integrate APIs with CSS Modules, creating maintainable, performant, and secure applications.