Components
This guide outlines the standard file structure and characteristics for CivicTheme components, based on the established patterns in the codebase.
Overview
Each component follows a consistent file structure with specific purposes for each file type. This ensures maintainability, reusability, and proper integration with the CivicTheme design system.
File Types and Purposes
*.component.yml - Component Metadata and Schema (SDC)
*.component.yml - Component Metadata and Schema (SDC)Purpose: Defines the component's properties, validation rules, and documentation as a Drupal Single Directory Component (SDC).
CivicTheme components are SDCs — each component lives in its own directory with all related files (Twig, SCSS, JS, stories, and this metadata file). Drupal's SDC system uses the .component.yml file to register the component and validate the props passed to it at render time.
Characteristics:
Uses JSON Schema format with Drupal's metadata schema for prop validation
Defines all available props with types, descriptions, and validation rules
Includes enums for constrained values (e.g., theme options, size variants)
Provides default values where appropriate
Serves as the single source of truth for component configuration
Enables automatic form generation in Storybook and Drupal
Drupal validates incoming props against this schema at render time — missing required props or invalid types will raise errors
Key sections:
$schema: References Drupal's metadata schema (https://git.drupalcode.org/...metadata.schema.json)name: Human-readable component namestatus: Component stability status (e.g.,stable,experimental)description: Component purpose and usageprops: Detailed property definitions with JSON Schema validation (types, enums, defaults)slots: Named content areas that accept rendered markup (used instead of props for rich content)
To see how CivicTheme structures its components, examine the CivicTheme UI Kit SDC components.
Standard enums: CivicTheme defines standard enum values for commonly used props such as theme (light, dark), spacing (top, bottom, both, none), and other widely shared properties. When creating or overriding components in your sub-theme, use the same enum values to ensure your component schemas remain compatible with CivicTheme's base components. Mismatched enums may cause incompatibilities with CivicTheme and Drupal. Review the base theme's .component.yml files to see which standard values are expected.
SDC component referencing: Components are referenced by their namespace and machine name (e.g., civictheme:button). In sub-themes, use the replaces key to override a base theme component:
*.twig - Template File
*.twig - Template FilePurpose: Defines the HTML structure and logic for rendering the component.
Characteristics:
Contains comprehensive documentation in the header comment block
Lists all available props with types and descriptions
Implements conditional logic for different component states
Uses Twig's template inheritance and includes
Handles multiple component variants (e.g., different types, sizes, themes)
Includes accessibility attributes and ARIA labels
Supports both static and dynamic content rendering
Key patterns:
Prop validation — validate props at the top of the template using ternary operators:
Class construction — build modifier classes dynamically:
Nested component includes — use only to limit the scope of variables passed to child components:
Blocks for extensibility — use Twig blocks to allow further overriding by child templates:
*.stories.js - Storybook Configuration
*.stories.js - Storybook ConfigurationPurpose: Creates interactive documentation and testing environment for the component.
Characteristics:
Defines Storybook metadata and component configuration
Maps component props to Storybook controls
Provides default values for all component variants
Enables interactive testing of different prop combinations
Imports constants and design tokens for consistent options
Supports component categorisation in Storybook navigation
Key features:
Control types (radio, text, boolean, select) for different prop types
Default story with sensible fallback values
Integration with design system constants
Component organisation in Storybook hierarchy
*.js - JavaScript Functionality
*.js - JavaScript FunctionalityPurpose: Provides interactive behaviour and event handling for the component.
Characteristics:
Implements component-specific functionality and interactions
Handles user events (click, focus, keyboard navigation)
Manages component state and lifecycle
Provides accessibility enhancements
Uses vanilla JavaScript for broad compatibility
Includes comprehensive event handling and error prevention
Uses
data-attributes for element selection to ensure HTML changes don't break functionalityAvoids dependencies on jQuery, using standard DOM APIs
Automatically wrapped with Drupal behaviours at build time
Key patterns:
Constructor pattern for component initialisation
Event delegation and proper cleanup
Accessibility support (keyboard navigation, ARIA)
State management and visual feedback
Integration with design system classes
Data attribute-based element selection for reusability
Standard DOM methods (querySelector, addEventListener, etc.)
Proper error handling and validation
Implementation approach:
Common utilities are managed in
/components/00-baseElement connections use
data-attributes instead of classes, IDs, or element tagsEnsures minimal impact on JS when HTML structure changes
Promotes reusability across different HTML structures
Build process automatically wraps with
Drupal.behaviors.attachfunctions
*.scss - Source Styles
*.scss - Source StylesPurpose: Defines the component's visual styling using SCSS.
Characteristics:
Uses SCSS features (variables, mixins, nesting)
Implements BEM methodology for class naming
Integrates with design system tokens and variables
Provides responsive design support
Includes focus states and accessibility styling
Uses mixins for consistent styling patterns
Supports light and dark theme variants
Uses CivicTheme's color palette system for consistent theming
Key patterns:
BEM naming — all classes use the pattern ct-<component>__<element>--<modifier>:
Design tokens — use token functions instead of hardcoded values:
Theme support — use the component theme mixin for light/dark theme support:
Key features:
Design system integration via variables and mixins
Responsive breakpoints and media queries
State-based styling (hover, focus, active, disabled)
Theme support (light/dark variants)
Modular and maintainable structure
Color palette integration using
$ct-colorsvariable mapBEM naming convention for maintainable CSS architecture
Implementation approach:
Most styles managed within components in
/componentsdirectory/assets/sassreserved for Drupal-specific styles (admin blocks, CKEditor, Layout Builder)Color application via
/subtheme/components/variables.base.scssusing$ct-colorsFine-tuning colors in
/subtheme/components/variables.components.scssDefault color map reference:
civictheme/components/00-base/_variables.base.scssComponent color variables reference:
civictheme/components/00-base/_variables.components.scssEncouraged to support both light and dark themes for new components
Particularly important for giveback components
*.css - Compiled Styles
*.css - Compiled StylesPurpose: Generated CSS file for production use.
Characteristics:
Automatically generated from SCSS source files
Contains vendor-prefixed and optimised CSS
Includes responsive design rules
Provides fallback values and browser compatibility
Optimised for performance and file size
Important notes:
Do not edit directly - changes will be overwritten
Generated via build process (
npm run dist)Contains design system variables and custom properties
Includes comprehensive state styling and accessibility features
Development Guidelines
File Naming Convention
All files use the same base name as the component
Follow kebab-case for component names
Maintain consistent file extensions
Component Structure
Start with the
*.component.ymlto define the component's interfaceCreate the
*.twigtemplate with proper documentationAdd
*.stories.jsfor interactive documentationImplement
*.jsfor any required interactivityStyle with
*.scssusing design system patternsBuild generates the final
*.cssfile
Best Practices
File header documentation is generated from the *.component.yml document
Use design system tokens and variables consistently
Implement proper accessibility features
Test all component variants in Storybook
Follow established naming conventions and patterns
Ensure responsive design support
Include proper error handling and validation
This structure ensures components are maintainable, reusable, and properly integrated with the CivicTheme design system while providing excellent developer experience through comprehensive documentation and testing tools.
Common Pitfalls
Forgetting to rebuild
Changes to SCSS, JavaScript, or Twig files require running npm run dist. Storybook's watch mode handles this automatically, but if you are testing in Drupal you need to rebuild and clear caches.
Namespace conflicts
Your overridden component must use replaces: 'civictheme:component_name' in its .component.yml file. Without this, Drupal will see two components with conflicting names.
Breaking the prop schema
When overriding a component's .component.yml, ensure existing props remain compatible. Adding new props is safe; removing or changing the type of existing props will break templates that depend on them.
Preprocess hook dependencies
Some components receive their data from preprocess hooks in the base CivicTheme theme's includes/ directory. If your override adds new props, you will need to provide data for them through your sub-theme's own preprocess hooks. The mapping system explains how Drupal entities are connected to component templates through preprocess functions.
Last updated
Was this helpful?