building-themes
npx machina-cli add skill MacareuxDigital/concretecms-skills/building-themes --openclawBuilding Themes
Overview
This skill enables the development of professional, feature-rich themes for Concrete CMS. It covers the essential components like page_theme.php, page templates, asset registration, and advanced features such as grid support and containers.
For the most up-to-date best practices and implementation examples, refer to the concrete/themes/atomik directory in the core. It serves as the primary reference for modern theme development in Concrete CMS.
Workflow
1. Setup Theme Directory
Themes should be placed within a package for better portability. Otherwise, they can be stored in the application directory.
- Path:
packages/your_package_handle/themes/your_theme_handle/ - Path:
application/themes/your_theme_handle/
2. Create Core Files
Every theme requires at least:
page_theme.php: The main configuration class for the theme.default.php: The default page template.view.php: Required for single pages. Should include thesystem_errorselement and$innerContent.elements/header.php&elements/footer.php: Shared header and footer.- Recommended: Sub-divide elements for simplicity (e.g.,
elements/header_top.php,elements/header.php,elements/footer.php,elements/footer_bottom.php).
3. Configure page_theme.php
- Define namespace:
Concrete\Package\YourPackageHandle\Theme\YourThemeHandle - Required Methods:
getThemeName(): Return the localized name of the theme.getThemeDescription(): Return the localized description of the theme.
- Implement
getThemeSupportedFeatures()to optimize asset loading.- JS Library Themes: If the theme includes a full-featured JavaScript library (e.g., Bootstrap 5 JS, jQuery), list all supported features (e.g.,
FeatureConstants::NAVIGATION,FeatureConstants::FORMS) to avoid conflicts with Concrete's internal asset loading. - No-JS Themes: If the theme does not include any JavaScript, it is recommended not to implement this method.
- JS Library Themes: If the theme includes a full-featured JavaScript library (e.g., Bootstrap 5 JS, jQuery), list all supported features (e.g.,
- Configure grid support via
$pThemeGridFrameworkHandle.- Core Frameworks: If using a supported framework (e.g.,
bootstrap5,bootstrap4), set this property to the corresponding handle. - Custom Frameworks: If the theme uses a custom or unsupported CSS framework (e.g., Tailwind, Bulma), do not set this property.
- Supported Handles: Find available frameworks in
concrete/src/Page/Theme/GridFramework/Type(e.g.,bootstrap5,foundation,nine_sixty).
- Core Frameworks: If using a supported framework (e.g.,
4. Implement Templates
- Use
$view->inc('elements/header.php')to include shared elements. For more modularity, use multiple includes like$view->inc('elements/header_top.php')and$view->inc('elements/header.php'). - Ensure
header_requiredandfooter_requiredelements are present.- Note on
header_required: It is recommended to pass metadata variables to this element. See the header_required snippet for the recommended implementation.
- Note on
- Use
Areafor editable regions. - Single Page Templates (
view.php): Must include thesystem_errorselement andecho $innerContent;to display system messages and single page content. See the system_errors snippet for the recommended implementation. - Wrap the main theme content in a
divelement with$c->getPageWrapperClass(). Do not apply this class to thebodyelement.
5. Manage Assets
- Use
registerAssets()inPageThemeto require core assets or provide theme-specific ones. - Prefer Core Assets: Always require core assets (e.g.,
jquery,bootstrap,vue) even if the theme directory contains the same libraries. This avoids conflicts and ensures compatibility with Concrete CMS's internal logic. - Minimize redundant CSS/JS by declaring what the theme provides.
6. Register Theme in Package
- Update the package
controller.phpor use a CIF XML file to install the theme.
Guidelines
- Namespacing: Always follow Concrete CMS PSR-4 naming conventions.
- Reference: Use
concrete/themes/atomikas the gold standard for implementation details. Other bundled themes may be legacy or for internal use. - Security: Include
defined('C5_EXECUTE') or die("Access Denied.");at the top of every PHP file. - Localization: Use
t()for all user-facing strings. - Modular Elements: For complex themes, it is recommended to split header and footer elements (e.g.,
header_top.phpfor metadata/scripts andheader.phpfor navigation) to keep individual files simple and maintainable. - Page Wrapper: Always use
$c->getPageWrapperClass()on a wrapperdivimmediately inside thebodytag, rather than on thebodyitself. This ensures that Concrete CMS's UI and dialogs work correctly within the theme's layout. - Asset Management:
- Core Libraries: Always prefer core assets over bundling your own for common libraries like jQuery, Bootstrap, moment.js, vue.js, and FullCalendar.
- Check Bundled Assets: To see which libraries are bundled in the core, check the
assetsarray inconcrete/config/app.php. - Conflict Prevention: Using core assets prevents multiple versions of the same library from being loaded, which can cause significant JavaScript errors.
- Theme Features:
- Use
getThemeSupportedFeatures()to declare what your theme provides. This prevents Concrete CMS from loading redundant or conflicting core assets. - If your theme is a "fully featured" theme with its own JS bundle, you should return an array containing all relevant
FeatureConstants. - If your theme is simple/static and has no JavaScript, omitting this method allows Concrete CMS to handle asset injection more predictably.
- Use
- Grid Framework:
- The
$pThemeGridFrameworkHandleproperty enables the built-in Layout system to use your theme's grid classes. - Only use this if your theme is compatible with one of the core grid frameworks found in
concrete/src/Page/Theme/GridFramework/Type. - Common handles include
bootstrap5,bootstrap4,bootstrap3,foundation, andnine_sixty. - If your theme uses a different CSS framework (like Tailwind CSS) or a custom grid system, do not define
$pThemeGridFrameworkHandle.
- The
- Modern Features: Favor Containers over hardcoded areas for flexible layouts in Concrete CMS v9+.
- Containers:
- Naming: Always ask the user for clarification before naming a container if the name is not explicitly provided.
- Element Path:
themes/your_theme_handle/elements/containers/your_container_handle.php - Implementation: Use
Concrete\Core\Area\ContainerAreato define editable regions within the container. - Registration: Define containers in the package's
config/install.xmlfile using the<containers>tag. - Usage: Containers are added to areas in the CMS by users, providing structured, reusable layouts.
Source
git clone https://github.com/MacareuxDigital/concretecms-skills/blob/main/building-themes/SKILL.mdView on GitHub Overview
Learn how to build professional Concrete CMS themes from scratch. It covers directory structure, core files, asset management, and modern features like containers and grid support, with a reference to the Atomik theme for best practices.
How This Skill Works
Work starts by placing themes in a package or application directory, then creating core files such as page_theme.php, default.php, and view.php plus modular elements like header and footer. Assets are registered through PageTheme, and grid framework or container features are configured in page_theme.php. Templates should leverage Area regions and the recommended header_required and system_errors usage for robust rendering.
When to Use It
- Starting a new Concrete CMS theme from scratch
- Modifying an existing theme to enable containers and grid support (v9+)
- Packaging a theme for portability within a Concrete CMS project
- Creating modular header/footer elements for reuse across pages
- Optimizing asset loading and framework selection with getThemeSupportedFeatures and grid handles
Quick Start
- Step 1: Create the theme directory in packages/your_package_handle/themes/your_theme_handle or application/themes/your_theme_handle
- Step 2: Add core files: page_theme.php, default.php, view.php, and header/footer elements; organize elements (header_top.php, header.php, footer.php, footer_bottom.php)
- Step 3: Implement page_theme.php with namespace, getThemeName()/getThemeDescription(), grid framework handle if needed, and registerAssets to load core assets
Best Practices
- Place themes under a package (packages/your_package_handle/themes/your_theme_handle) for portability, or use application/themes/your_theme_handle/
- Create core files: page_theme.php, default.php, view.php, and shared elements/header.php & elements/footer.php; split elements for clarity (e.g., header_top.php)
- Configure page_theme.php with a proper namespace, getThemeName(), getThemeDescription(), and getThemeSupportedFeatures() if using a JS library; only set grid framework handle when using a core framework like bootstrap5 or bootstrap4
- Use Area blocks for editable regions and wrap main content in a div with $c->getPageWrapperClass(); do not apply this class to the body
- Register assets with registerAssets() and prefer core assets (jquery, bootstrap, vue) to ensure compatibility
Example Use Cases
- A package-scoped corporate theme at Packages/MyCompanyTheme with Bootstrap 5 grid and a custom header/footer set
- A blog theme using default.php and view.php templates, including system_errors and echo $innerContent; assets loaded via registerAssets
- A modular theme that splits header into header_top.php, header.php, and header_bottom.php for flexible composition
- A no-JS theme that omits getThemeSupportedFeatures() to avoid asset conflicts
- An Atomik-style reference implementation in concrete/themes/atomik used as a practical guide for modern theme development