# 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 (
{children}
);
}
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 (
Loading...}>
);
}
export default MyComponentLoader;
"""
"""jsx
// MyComponent.jsx
import React from 'react';
import styles from './MyComponent.module.css';
function MyComponent() {
return (
{/* Component Content */}
);
}
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 {children};
}
"""
**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 Hello
}
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 (
{children}
);
}
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 Content;
}
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 "" in the HTML "" 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 (
Link 1
Link 2
);
}
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.
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'
# Tooling and Ecosystem Standards for CSS Modules This document outlines standards for tooling and the ecosystem surrounding CSS Modules, ensuring consistent, maintainable, and performant code across projects. It focuses on leveraging the latest features and best practices within the ecosystem. ## 1. Linting and Formatting ### 1.1. Standard: Utilize Stylelint with CSS Modules Configuration Stylelint is crucial for maintaining code quality and consistency. Configure it specifically for CSS Modules to catch common errors and enforce style guidelines. * **Do This:** Integrate Stylelint into your project with a configuration tailored for CSS Modules. Use plugins like "stylelint-selector-bem-pattern" to enforce BEM naming conventions. * **Don't Do This:** Rely on manual code reviews alone for catching styling errors or inconsistencies. * **Why:** Automated linting ensures every developer adheres to the same standards, reducing bugs and improving readability. """javascript // .stylelintrc.json { "extends": ["stylelint-config-standard", "stylelint-config-css-modules"], "plugins": ["stylelint-selector-bem-pattern"], "rules": { "selector-class-pattern": null, // Disable default class name pattern "plugin/selector-bem-pattern": { "preset": "bem", "utilitySelectors": "^\\.u-" } } } """ Explanation: * "stylelint-config-standard": Provides a set of recommended Stylelint rules. * "stylelint-config-css-modules": Tailored configuration for CSS Modules, understanding the local scope. * "stylelint-selector-bem-pattern": Enforces BEM naming, facilitating component-based styling. * "utilitySelectors": Allows utility classes that don't follow BEM. ### 1.2. Standard: Employ Prettier for Automatic Code Formatting Prettier automates code formatting, reducing the need for manual adjustments and ensuring consistent styling. * **Do This:** Integrate Prettier with your IDE and use a pre-commit hook to automatically format code before committing. * **Don't Do This:** Manually format code or rely on inconsistent IDE formatting settings. * **Why:** Automated formatting ensures a consistent code style across the team, improving readability and reducing merge conflicts. """javascript // .prettierrc.js module.exports = { semi: true, trailingComma: 'all', singleQuote: true, printWidth: 120, tabWidth: 2, useTabs: false, }; """ Explanation: * "semi: true": Ensures semicolons are used at the end of statements. * "trailingComma: 'all'": Adds trailing commas wherever possible, improving diff readability. * "singleQuote: true": Uses single quotes for strings. * "printWidth: 120": Specifies the line length at which Prettier will wrap code. * "tabWidth: 2": Specifies the number of spaces per indentation level. * "useTabs: false": uses spaces instead of tabs. ### 1.3 Standard: Use EditorConfig To maintain consistent coding styles across different IDEs, use EditorConfig. * **Do This:** Add an ".editorconfig" file to the root of your project. * **Don't Do This:** Rely on individual IDE settings to enforce code style. * **Why:** EditorConfig ensures that basic coding styles (indentation, charset, etc.) are consistent regardless of the editor being used. """editorconfig # EditorConfig is awesome: https://EditorConfig.org # top-most EditorConfig file root = true [*] charset = utf-8 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true [*.{js,jsx,ts,tsx,css,scss,less}] indent_style = space indent_size = 2 """ ## 2. Build Tools and Module Bundlers ### 2.1. Standard: Utilize Webpack, Parcel, or Rollup with CSS Modules Support Select a module bundler that natively supports CSS Modules and provides options for customization. * **Do This:** Configure Webpack, Parcel, or Rollup to process CSS Modules files, generate unique class names, and handle dependencies. * **Don't Do This:** Manually manage CSS dependencies or rely on global stylesheets. * **Why:** Module bundlers automate the process of bundling CSS and JavaScript files, optimizing performance and simplifying deployment. """javascript // webpack.config.js module.exports = { module: { rules: [ { test: /\.module\.css$/, // Target CSS Modules files use: [ 'style-loader', { loader: 'css-loader', options: { modules: { localIdentName: '[name]__[local]--[hash:base64:5]', }, importLoaders: 1, }, }, 'postcss-loader', ], }, { test: /\.css$/, exclude: /\.module\.css$/, // Exclude CSS Module files use: [ 'style-loader', 'css-loader', 'postcss-loader', ] } ], }, }; """ Explanation: * "test: /\.module\.css$/": Targets files ending with ".module.css" as CSS Modules. * "css-loader": Processes the CSS files and handles "@import" and "url()" statements. * "modules": Enables CSS Modules and configures the generated class names. * "localIdentName": Defines how class names are generated – here, using a combination of the file name, local name, and a hash. * "importLoaders: 1": Ensures that "postcss-loader" is applied to imported CSS as well. * "style-loader": Injects the CSS into the DOM. * "postcss-loader": Applies PostCSS transformations (see below). * The separate rule handles global CSS files to not apply CSS Modules transformations to them. ### 2.2. Standard: Integrate PostCSS for Advanced CSS Processing PostCSS enables advanced CSS processing, such as autoprefixing, minification, and future CSS syntax. * **Do This:** Use PostCSS with plugins like "autoprefixer", "cssnano", and "postcss-preset-env" to optimize CSS code. * **Don't Do This:** Manually manage browser prefixes or skip CSS minification in production. * **Why:** PostCSS automates browser compatibility and optimizes CSS code, improving performance and maintainability. """javascript // postcss.config.js module.exports = { plugins: [ require('postcss-preset-env')({ browsers: 'last 2 versions', stage: 0, // Enable all features from CSSWG drafts autoprefixer: { grid: true }, // Enable grid prefixes }), require('cssnano')({ preset: 'default', }), ], }; """ Explanation: * "postcss-preset-env": Enables the use of future CSS features while ensuring compatibility with older browsers, configured to target the last two versions of each browser. "stage: 0" enables all experimental features. "autoprefixer" adds necessary browser prefixes. * "cssnano": Minifies the CSS code, reducing file size. ## 3. Naming Conventions and Organization ### 3.1. Standard: Adopt BEM or Similar Naming Convention Use a consistent naming convention, such as BEM (Block, Element, Modifier), to improve code readability and maintainability. * **Do This:** Apply BEM naming to CSS class names and component structures. * **Don't Do This:** Use generic or ambiguous class names that lack context. * **Why:** BEM provides a clear structure for CSS class names, making it easier to understand the relationships between elements. """css /* Block: button */ .button { background-color: #007bff; color: white; padding: 10px 20px; } /* Element: button__label */ .button__label { font-size: 16px; } /* Modifier: button--primary */ .button--primary { background-color: #28a745; } """ ### 3.2. Standard: Structure CSS Modules Files Based on Component Hierarchy Organize CSS Modules files to mirror the component hierarchy, improving code discoverability and maintainability. * **Do This:** Create a CSS Modules file for each component or module, and nest them within the corresponding directories. For instance, the styles for "components/Button/Button.jsx" should reside in "components/Button/Button.module.css". * **Don't Do This:** Store all CSS Modules files in a single directory or mix them haphazardly. * **Why:** Mirroring the component hierarchy simplifies navigation and makes it easier to locate the styles for a specific component. """ src/ ├── components/ │ ├── Button/ │ │ ├── Button.jsx │ │ └── Button.module.css │ ├── Input/ │ │ ├── Input.jsx │ │ └── Input.module.css """ ## 4. Testing and Debugging ### 4.1. Standard: Use Jest and Enzyme (or React Testing Library) for Component Testing Test React components with Jest and Enzyme (or, preferrably, React Testing Library) to ensure that styling is applied correctly. * **Do This:** Write unit tests for components to verify that CSS classes are applied as expected. * **Don't Do This:** Skip testing styling or rely solely on manual testing. * **Why:** Automated testing ensures that changes do not inadvertently break the styling of components. """javascript // Button.test.jsx 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 apply the correct base class', () => { render(<Button>Click me</Button>); const buttonElement = screen.getByText('Click me'); expect(buttonElement).toHaveClass(styles.button); }); it('should apply the primary modifier class when primary prop is true', () => { render(<Button primary>Click me</Button>); const buttonElement = screen.getByText('Click me'); expect(buttonElement).toHaveClass(styles.button); expect(buttonElement).toHaveClass(styles['button--primary']); // Access with bracket notation }); }); """ Explanation: * Uses "@testing-library/react" for better testing practices. * Imports the CSS Modules stylesheet. * Tests that the correct CSS classes are applied to the rendered component, including modifier classes. * Access style class names from the imported "styles" object, using bracket notation for BEM modifiers ("styles['button--primary']"). This is necessary because the keys in the "styles" object will reflect the original dashes in the CSS class names. ### 4.2. Standard: Utilize Browser Developer Tools for Inspecting CSS Modules Leverage browser developer tools to inspect applied styles and diagnose CSS Modules issues. * **Do This:** Use the "Elements" panel in Chrome Developer Tools or Firefox Developer Tools to inspect the generated class names and applied styles. * **Don't Do This:** Rely solely on source code examination or guesswork when debugging styling issues. * **Why:** Browser developer tools provide direct insight into the rendered CSS and can help identify issues such as specificity conflicts or incorrect class names. ## 5. Integration with Frameworks and Libraries ### 5.1. Standard: Seamless Integration with React and Other Component-Based Frameworks CSS Modules, by design, integrate well with component-based frameworks like React, Vue, and Angular. Ensure correct usage within each framework. * **Do This:** Import CSS Modules stylesheets directly into components and access the generated class names via the imported object. * **Don't Do This:** Use global stylesheets or bypass the CSS Modules mechanism within components. * **Why:** Component-level styling with CSS Modules ensures encapsulation and prevents style collisions. """jsx // Button.jsx (React example) import React from 'react'; import styles from './Button.module.css'; function Button({ children, primary }) { return ( <button className={"${styles.button} ${primary ? styles['button--primary'] : ''}"}> {children} </button> ); } export default Button; """ Explanation: * Imports the "Button.module.css" stylesheet. * Applies the "button" class from the "styles" object to the button element. * Dynamically applies the "button--primary" modifier class based on the "primary" prop. * Uses template literals for class names, bracket notation for modifier class names. ### 5.2. Standard: Handling Third-Party Libraries and Components When integrating with third-party libraries, ensure CSS Modules styles don't conflict with the library's styles. * **Do This:** Prefix class names or use CSS Modules composition to avoid naming collisions. Consider wrapping the third-party component and applying custom styles to the wrapper. * **Don't Do This:** Override third-party styles directly without proper encapsulation. * **Why:** Prevents unintended style conflicts and maintains the integrity of the third-party library's styling. """jsx // WrappedThirdPartyComponent.jsx import React from 'react'; import ThirdPartyComponent from 'third-party-library'; // Example: A charting library import styles from './WrappedThirdPartyComponent.module.css'; function WrappedThirdPartyComponent(props) { return ( <div className={styles.wrapper}> <ThirdPartyComponent {...props} className={styles.thirdPartyComponent} /> </div> ); } export default WrappedThirdPartyComponent; """ """css /* WrappedThirdPartyComponent.module.css */ .wrapper { /* Styles for the wrapper */ border: 1px solid #ccc; padding: 10px; } /* This rule won't work out of the box. Need to pass the className down to allow styling. */ .thirdPartyComponent { /* Styles specifically targeting the third-party component's root element */ color: blue; } """ Explanation: * Wraps the "ThirdPartyComponent" in a custom component. The third party component *must* accept a "className" prop. * Applies a "wrapper" class to the outer "div" and sets overall styles. * Applies a specialized "thirdPartyComponent" class to customize the third-party component's root. ## 6. Performance Optimization ### 6.1. Standard: Minify and Optimize CSS Modules in Production Optimize CSS Modules stylesheets for production by minifying and removing unused styles. * **Do This:** Use PostCSS with "cssnano" to minify CSS code and tools like PurgeCSS (or "remove-unused-css") to remove unused styles. * **Don't Do This:** Skip CSS minification or load unnecessary styles in production. * **Why:** Reduces the size of CSS files, improving page load times and overall performance. ### 6.2. Standard: Code Splitting for CSS Modules Implement code splitting to load only the CSS needed for the current route or component. * **Do This:** Use dynamic imports or React.lazy to load CSS Modules stylesheets on demand. * **Don't Do This:** Load all CSS Modules stylesheets upfront, regardless of whether they are needed. * **Why:** Improves initial page load time by reducing the amount of CSS that needs to be loaded upfront. """jsx // MyComponent.jsx import React, { Suspense } from 'react'; const stylesPromise = import('./MyComponent.module.css'); function MyComponent() { return ( <Suspense fallback={<div>Loading styles...</div>}> <MyComponentContent stylesPromise={stylesPromise} /> </Suspense> ); } function MyComponentContent({ stylesPromise }) { const styles = stylesPromise.then(module => module.default); return ( <div className={styles.container}> {/* Component content */} </div> ); } export default MyComponent; """ or, a simplified approach using React.lazy and dynamic imports, though not directly applicable to importing CSS Modules: """jsx import React, { lazy, Suspense } from 'react'; const OtherComponent = lazy(() => import('./OtherComponent')); function MyComponent() { return ( <div> <Suspense fallback={<div>Loading...</div>}> <OtherComponent /> </Suspense> </div> ); } """ Explanation: * Instead of directly importing the "styles" object, use dynamic "import()" to load the stylesheet asynchronously. * Use React's "Suspense" component to gracefully handle the loading state. The "fallback" prop takes a React element to display while the component is loading. * The component that uses the styles ("MyComponentContent") now receives the "stylesPromise" as a prop. * Inside "MyComponentContent", "stylesPromise.then" resolves when the dynamic import finishes, which gives you access to the "styles" object. * This approach requires configuration to handle CSS imports with dynamic imports; typically Webpack or your bundler config would need modifications, such as using "mini-css-extract-plugin". The exact configuration depends heavily on the bundler setup. ## 7. Security Considerations ### 7.1. Standard: Sanitize Class Names Be cautious when dynamically generating class names based on user input to prevent CSS injection vulnerabilities. * **Do This:** Sanitize or validate user input before using it to construct class names. Avoid directly using user-provided strings as CSS class names. * **Don't Do This:** Directly use user-provided strings as CSS class names without any validation. * **Why:** Prevents malicious users from injecting arbitrary CSS code into your application. """javascript // Example (insecure): const userInput = '<style>body { background: red; }</style>'; const className = "user-${userInput}"; // Insecure: direct use of user input! // Example (secure): const sanitizedInput = userInput.replace(/[^a-zA-Z0-9_-]/g, ''); // Remove invalid characters const className = "user-${sanitizedInput}"; // Sanitized usage """ Explanation: * Never directly use user-provided input in CSS class names, as this can lead to CSS injection and XSS vulnerabilities. * Sanitize the input by removing or encoding any characters that are not alphanumeric, underscores, or hyphens. This greatly reduces the risk of injecting malicious CSS. ## 8. Community and Evolution ### 8.1. Standard: Stay Up-to-Date with the Latest CSS Modules Developments Keep abreast of the latest features, best practices, and ecosystem tools related to CSS Modules. * **Do This:** Follow CSS Modules-related blogs, forums, and social media channels to stay informed. * **Don't Do This:** Rely on outdated information or ignore new developments in the CSS Modules ecosystem. * **Why:** Ensures that you are using the most efficient and effective techniques for CSS Modules development. ### 8.2. Standard: Contribute to the CSS Modules Community Share experiences, contribute to open-source projects, and help improve the CSS Modules ecosystem. * **Do This:** Participate in discussions, submit bug reports, and contribute code to CSS Modules-related projects. * **Don't Do This:** Be a passive consumer of CSS Modules technology. * **Why:** Helps to improve the CSS Modules ecosystem and ensures that it meets the evolving needs of developers. These detailed standards, when diligently followed, will allow developers and AI coding assistants to produce high-quality, maintainable, and performant CSS Modules code. They cover everything from choosing the correct tools to writing secure and optimally configured code.
# 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.
# Code Style and Conventions Standards for CSS Modules This document outlines the coding style and conventions standards for CSS Modules aimed at fostering maintainable, performant, and consistent codebases. These standards are tailored specifically for CSS Modules, taking into account their unique features and benefits. ## 1. Formatting and Structure Consistent formatting and a well-defined structure are crucial for readability and maintainability. ### 1.1 General Formatting * **Do This:** Use consistent indentation (2 spaces). * **Why:** Improves readability and reduces merge conflicts. """css /* Do This */ .container { display: flex; flex-direction: column; } """ * **Don't Do This:** Use inconsistent indentation or tabs. """css /* Don't Do This */ .container { display: flex; flex-direction: column; } """ * **Do This:** Use a newline after each declaration. * **Why:** Enhances readability and simplifies debugging. """css /* Do This */ .button { background-color: #4CAF50; color: white; padding: 10px 20px; } """ * **Don't Do This:** Group multiple declarations on the same line. """css /* Don't Do This */ .button { background-color: #4CAF50; color: white; padding: 10px 20px; } """ * **Do This:** Add a blank line between rule sets. * **Why:** Visually separates styles and improves organization. """css /* Do This */ .header { font-size: 2em; } .footer { font-size: 0.8em; } """ * **Don't Do This:** Omit blank lines between rule sets. """css /* Don't Do This */ .header { font-size: 2em; } .footer { font-size: 0.8em; } """ ### 1.2 File Structure * **Do This:** Colocate CSS Modules files with their corresponding components. For example, "Component.jsx" and "Component.module.css". * **Why:** Improves discoverability and maintainability as styles are logically grouped. * **Do This:** Use a consistent naming convention for CSS Modules files, such as ".module.css" or ".module.scss". * **Why:** Provides clarity and ensures clear differentiation. """ src/ └── components/ └── Button/ ├── Button.jsx ├── Button.module.css └── Button.test.jsx """ * **Don't Do This:** Place all CSS Modules files into a single, monolithic directory. * **Why:** Leads to poor organization and reduced discoverability, especially in larger projects. ### 1.3 Order of Declarations * **Do This:** Follow a logical order for CSS declarations, such as grouping related properties together. A common order is: Box Model, Typography, Visual, Animation/Transition. * **Why:** Makes styles easier to scan and understand. """css /* Do This */ .element { /* Box Model */ display: block; width: 100%; padding: 10px; margin: 0 auto; /* Typography */ font-size: 16px; line-height: 1.5; color: #333; /* Visual */ background-color: #f0f0f0; border: 1px solid #ccc; /* Animation/Transition */ transition: all 0.3s ease; } """ * **Don't Do This:** Randomly arrange CSS declarations. """css /* Don't Do This */ .element { color: #333; width: 100%; display: block; font-size: 16px; background-color: #f0f0f0; padding: 10px; } """ ## 2. Naming Conventions Choosing appropriate and consistent names for classes is essential for maintainability and collaboration. ### 2.1 Class Names * **Do This:** Use kebab-case for class names (e.g., "main-header", "submit-button"). * **Why:** Kebab-case is widely adopted and provides better readability. It also avoids potential conflicts with JavaScript variable naming conventions where camelCase is prevalent. """css /* Do This */ .main-header { font-size: 2em; } """ * **Don't Do This:** Use camelCase, snake_case, or PascalCase for class names. """css /* Don't Do This */ .mainHeader { font-size: 2em; } """ * **Do This:** Use semantic and descriptive class names that reflect the element's purpose. * **Why:** Improves understanding and maintainability. """css /* Do This */ .error-message { color: red; } """ * **Don't Do This:** Use generic or ambiguous class names like "div1", or "box". """css /* Don't Do This */ .box { border: 1px solid black; } """ * **Do This:** When using BEM (Block, Element, Modifier) methodology, adhere to the CSS Modules naming convention by separating parts with double underscores and modifiers with double dashes: "block__element--modifier". * **Why:** BEM improves component reusability and maintainability but must be adapted for CSS modules to avoid global scope conflicts. """css /* Do This */ .form {} /* Block */ .form__input {} /* Element */ .form__input--error {} /* Modifier */ """ * **Do This:** Use single responsibility classes. Avoid overly long and complex class names that attempt to define too much styling in a single class. * **Why:** Enhances reusability and reduces specificity issues. """css /* Do This */ .button { /* Base styles */ } .button--primary { /* Modifier for primary style */ } """ * **Don't Do This:** Create deeply nested class names that tightly couple styles to specific DOM structures. This limits reusability and increases the risk of conflicts. """css /* Don't Do This */ .article__content__paragraph__highlight { } """ ### 2.2 JavaScript Interoperability * **Do This:** Import CSS Modules using a descriptive variable name. * **Why:** Provides clear context for the styles being used. """javascript // Do This import styles from './MyComponent.module.css'; function MyComponent() { return <div className={styles.container}>Hello</div>; } """ * **Don't Do This:** Use generic names such as "style". """javascript // Don't Do This import style from './MyComponent.module.css'; function MyComponent() { return <div className={style.container}>Hello</div>; } """ * **Do This:** When composing multiple class names, utilize a utility library or a simple template literal for clarity. * **Why:** Enhances readability when combining multiple styles. """javascript // Using template literals function MyComponent({isHighlighted}) { return ( <div className={"${styles.base} ${isHighlighted ? styles.highlighted : ''}"}> Content </div> ); } //Using classnames library (popular option) import classNames from 'classnames'; function MyComponent({isHighlighted}) { return ( <div className={classNames(styles.base, { [styles.highlighted]: isHighlighted })}> Content </div> ); } """ * **Don't Do This:** Manually concatenate class names without using a dedicated utility function, as this can lead to errors. """javascript //Don't do this function MyComponent({isHighlighted}) { return ( <div className={styles.base + ' ' + (isHighlighted ? styles.highlighted : '')}> Content </div> ); } """ ## 3. Stylistic Consistency Consistency in style guides promotes a uniform codebase, making it easier to read, modify, and maintain. ### 3.1 Selectors and Specificity * **Do This:** Prefer class selectors over tag or ID selectors. * **Why:** Class selectors are easier to override and provide more flexibility when styling components. CSS Modules mitigate ID selector collisions, but class selectors still promote better component design. """css /* Do This */ .button { padding: 10px 20px; } """ * **Don't Do This:** Use tag or ID selectors unless absolutely necessary. """css /* Don't Do This */ button { padding: 10px 20px; } """ * **Do This:** Avoid overly specific selectors. Keep the selector depth minimal. * **Why:** Easier to override styles and maintain a flat structure. """css /* Do This */ .component .element { } /* Better */ .element { } """ * **Don't Do This:** Create deeply nested or chained selectors. """css /* Don't Do This */ .component .container .item .sub-item { } """ ### 3.2 Values and Units * **Do This:** Use relative units like "em", "rem", or "%" for most values. * **Why:** Promotes responsive design and better scalability. "rem" is preferred for font sizes to avoid compounding issues. """css /* Do This */ .text { font-size: 1.2rem; margin-bottom: 1em; } """ * **Don't Do This:** Use absolute units like "px" unless necessary (e.g., for borders). """css /* Don't Do This */ .text { font-size: 19.2px; margin-bottom: 16px; } """ * **Do This:** Favor CSS variables (custom properties) for reusable values like colors, spacing, and font families. * **Why:** Simplifies theming and ensures consistency across the application. """css /* Define variables in a global CSS file or :root */ :root { --primary-color: #007bff; --font-size-base: 16px; } .button { background-color: var(--primary-color); font-size: var(--font-size-base); } """ * **Don't Do This:** Hardcode values repetitively throughout the codebase """css .button { background-color: #007bff; font-size: 16px; } """ ### 3.3 Media Queries * **Do This:** Organize media queries logically, typically at the end of the component's CSS file. * **Why:** Improves readability and simplifies maintenance. """css .component { /* Base styles */ font-size: 16px; } @media (max-width: 768px) { .component { font-size: 14px; /* Styles for smaller screens */ } } """ * **Don't Do This:** Scatter media queries throughout the CSS file in a disorganized manner. """css @media (max-width: 768px) { .component { font-size: 14px; } } .component { font-size: 16px; } @media (min-width: 992px) { .component { font-size: 18px; } } """ * **Do This:** Utilize mobile-first approach. Define base styles for mobile devices and then use media queries to enhance styles for larger screens. * **Why:** Optimizes for performance and progressive enhancement. """css /* Default styles for mobile */ .component { font-size: 14px; } /* Styles for larger screens */ @media (min-width: 768px) { .component { font-size: 16px; } } """ ## 4. Advanced CSS Modules Features Take advantage of more advanced features offered by CSS Modules for better organization and reusability. ### 4.1 Composition * **Do This:** Use "composes" to inherit styles from other classes within the same or other CSS Modules. * **Why:** Promotes reusability and reduces redundancy in styles. """css /* base-styles.module.css */ .base { padding: 10px; border: 1px solid #ccc; } /* button.module.css */ .button { composes: base from './base-styles.module.css'; background-color: #007bff; color: white; } """ * **Don't Do This:** Redefine styles that already exist in other classes. """css /* Don't Do This */ .button { padding: 10px; /* Redundant */ border: 1px solid #ccc; /* Redundant */ background-color: #007bff; color: white; } """ * **Do This:** Compose styles judiciously, avoiding deeply nested compositions which can lead to increased specificity and maintenance difficulties. * **Why:** Maintainability. """css /* Example */ /* button.module.css */ .button { composes: base; background-color: #007bff; } /* Use directly rather than compose if the relationship is not inherently hierarchical */ <button className={"${styles.base} ${styles.button}"}> Button </button> """ ### 4.2 Global Exceptions * **Do This:** Use ":global" sparingly and only when necessary to style elements that are influenced by external CSS (e.g., third-party libraries). * **Why:** Reduces the risk of naming conflicts and maintains the encapsulation benefits of CSS Modules. """css /* Do This (Use sparingly!) */ :global(.ql-editor) { font-size: 16px; } """ * **Don't Do This:** Overuse ":global" for general styling purposes as this defeats the purpose of CSS Modules. """css /* Don't Do This */ :global(.container) { width: 100%; margin: 0 auto; } """ ### 4.3 Theming with CSS Variables * **Do This:** Define theme-related CSS variables within the ":root" selector or a dedicated theme file. * **Why:** Centralizes theme management and simplifies updates. """css /* theme.module.css */ :root { --primary-color: #007bff; --secondary-color: #6c757d; --background-color: #f8f9fa; --text-color: #343a40; } .lightTheme { --background-color: #f8f9fa; --text-color: #343a40; } .darkTheme { --background-color: #343a40; --text-color: #f8f9fa; } /* component.module.css */ .container { background-color: var(--background-color); color: var(--text-color); } """ * **Don't Do This:** Hardcode theme-related values directly within component-specific CSS. """css .container { background-color: #f8f9fa; /* Avoid hardcoding */ color: #343a40; /* Avoid hardcoding */ } """ ## 5. Performance Optimization Optimize CSS Modules for better performance. ### 5.1 Minimizing CSS Size * **Do This:** Remove unused CSS rules and classes regularly. Use tools like PurgeCSS or similar to automatically analyze and remove unused CSS. * **Why:** Reduces the size of CSS files, improving page load times. * **Do This:** Use shorthand properties where appropriate. * **Why:** Reduces code verbosity and file size. """css /* Do This */ .element { margin: 10px 20px 10px 20px; /* Shorthand version */ margin: 10px 20px; } """ * **Don't Do This:** Use longhand properties when shorthand would suffice. """css /* Don't Do This */ .element { margin-top: 10px; margin-right: 20px; margin-bottom: 10px; margin-left: 20px; } """ ### 5.2 Avoiding Performance Pitfalls * **Do This:** Limit the use of expensive CSS properties, such as "box-shadow", "filter", and "transform", especially on frequently updated elements. * **Why:** These properties can impact rendering performance, particularly on mobile devices. If you need to use such properties, consider using the "will-change" property to let the browser know that the element will be animated. """css .element { will-change: transform; transform: rotate(45deg); } """ * **Don't Do This:** Animate or transition properties that trigger layout or paint operations on every frame. * **Why:** Can lead to janky animations and poor user experience. """css /* Don't Do This */ .element { transition: margin-left 0.3s; /* Avoid animating layout properties */ } """ ### 5.3 Code Splitting * **Do This:** Leverage code splitting techniques to load only the CSS modules required for a specific page or component. * **Why:** Reduces initial load time by minimizing the amount of CSS that needs to be downloaded and parsed. Modern bundlers like Webpack, Parcel, and Rollup support code splitting for CSS Modules. """javascript // Example using dynamic imports in React import React, { Suspense, lazy } from 'react'; const MyComponent = lazy(() => import('./MyComponent')); function App() { return ( <Suspense fallback={<div>Loading...</div>}> <MyComponent /> </Suspense> ); } """ ## 6. Security Considerations Address security concerns when using CSS Modules. ### 6.1 Avoiding Injection Vulnerabilities * **Do This:** Ensure that user-provided data is properly sanitized and escaped before being used in CSS styles. * **Why:** Prevents CSS injection attacks. While CSS Modules mitigate some risks by scoping styles, it's still crucial to sanitize user inputs. * **Don't Do This:** Directly embed user-provided data into CSS styles without sanitization. * **Do This:** Be mindful of using external untrusted CSS files, even if they are CSS Modules, as they could potentially contain malicious code. Always review external dependencies. ### 6.2 Dependency Management * **Do This:** Regularly review and update your CSS Modules dependencies to patch security vulnerabilities. * **Why:** Keeps your project secure and up-to-date. Tools like "npm audit" or "yarn audit" can help identify and fix vulnerabilities in your dependencies. * **Don't Do This:** Use outdated or unmaintained CSS Modules libraries or tools. ## 7. Tooling and Automation Utilize tooling to enforce code style and automate formatting. ### 7.1 Linters and Formatters * **Do This:** Integrate linters like Stylelint and formatters like Prettier into your development workflow. * **Why:** Automates code style checks and formatting, ensuring consistency across the codebase. * **Do This:** Configure your editor or IDE to automatically format CSS Modules on save. ### 7.2 Husky and Lint-Staged * **Do This:** Use Husky and lint-staged to run linters and formatters before committing code. * **Why:** Prevents code with style violations from being committed to the repository. """json // package.json { "husky": { "hooks": { "pre-commit": "lint-staged" } }, "lint-staged": { "*.module.css": [ "stylelint --fix", "git add" ] } } """ ## 8. Documentation and Comments * **Do This:** Add comments to your CSS Modules code to explain complex logic or non-obvious styling decisions. * **Why:** Improves code understanding and maintainability, especially for other developers working on the same project. """css /* Styles for the main header */ .main-header { font-size: 2em; /* Increased font size for visual emphasis */ color: var(--primary-color); /* Uses the primary color defined in the theme */ } """ * **Do This:** Generate documentation for your CSS Modules using tools like documentation.js or similar, to provide a clear overview of the available styles and their purpose. * **Why:** Improves onboarding for new team members and enhances collaboration. This comprehensive guide provides a solid foundation for CSS Modules development, promoting consistency, maintainability, and performance. Adhering to these standards will ensure a robust and scalable codebase.
# 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.
# 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.