Packaging and publishing a theme
Packaging
A theme is packaged as a zip whose folder name is the theme name. The zip has the theme folder at its root:
example-theme-1.0.0.zip
└── example-theme/
├── theme.json
├── screenshot.png
└── Resources/
├── views/layout.html.twig
└── dist/style.css
What should not go into the package: your own source files (src/, node_modules/), .git, build configurations. What cannot be in the package, rather than what should not: .php, .phtml, .phar files, .htaccess, .user.ini, web.config and cp-core/ or cp-includes/ paths. If any of these is present the package is rejected.
Contract check
Before installation the package is checked automatically (details: Theme contract). Common rejection reasons and fixes:
| Message | Fix |
|---|---|
| Theme directory name must be kebab-case | Change the directory name to the form example-theme (lowercase, hyphen) |
| theme.json is missing or invalid | The file is missing or the JSON is broken; check with a JSON validator |
| theme.json is missing a "name" / "version" field | Add the field; the version cannot be 0.0.0 |
| Missing Resources/views/layout.html.twig | Add the main template |
| theme.json entry file "…" does not exist under Resources/ | The entry.css/entry.js path is written relative to Resources/; does the file exist |
| Package must not include server-side code or server config | Remove .php, .htaccess etc. files from the package |
| Package must not include core paths | The package contains cp-core/ or cp-includes/ |
| … must not declare namespace App\ | A theme cannot contain PHP; remove the file |
In addition, a syntax check (ThemeSourceLinter) is made when templates are saved: a broken Twig file gives a warning before saving (it can still be saved with confirmation).
Installation ways
- Zip upload: AACP → Appearance → Add theme → choose the zip. If a theme with the same name exists, confirmation to overwrite is asked. Installation writes only under
cp-content/themes/, never touching the core. - Store: AACP → Modules → Module store (for themes Appearance → Theme store); browse the signed catalogue with images and install.
- By hand: copy the folder under
cp-content/themes/. It appears in the AACP theme list.
After installing, activate it from AACP → Appearance → Theme list. Activation rebuilds the Twig paths, so it refreshes the cache; this may take a few seconds.
Resilience at runtime
- A broken
theme.jsondrops the theme from the list and does not affect others. - If the active theme setting is invalid, the system falls back to the fallback theme (
cpalius-website); error pages are drawn with the fallback theme even when the database is down. - A template error is caught at the response layer like every other error; it does not bring down the whole site.
Publishing to the store and the catalogue
The store offers a signed catalogue (extensions/index.json + index.sig): each package's version, SHA-256 digest and screenshot are in the catalogue. Before the downloaded package is extracted, the digest is verified; the catalogue is checked by signature, expiry and a non-decreasing serial number. This way someone who has taken over the catalogue cannot present an old, vulnerable version as new.
A direct publishing flow to the store for third-party developers is not open yet (it is on the roadmap). Until then you can distribute your theme as a zip directly to your customers or contact the CPalius team for publication.
Versioning and compatibility
- The
theme.jsonversion must beMAJOR.MINOR.PATCH. - When you override a template, your theme breaks if the module's template variables or block names change. Follow the "concerns theme authors" notes in module or core release notes.
- Copying and changing the templates in the default theme is the most fragile way: when the default theme changes, you have to carry the difference by hand. Override as few templates as possible (Githeme overrides only three files and takes the rest from the default).
Theme development checklist
Structure
- The directory name is kebab-case;
theme.jsonname,version,entryare filled -
screenshot.pngexists (store and theme list image) - No
.php,.htaccess,cp-core/in the package
Template
-
layout.html.twigdefines thetitle,meta_description,meta_robots,stylesheets,content,javascriptsblocks - The
theme.head.endandtheme.footer.beforehooks are present - On the front end
cp_user(),cp_is_granted(),cp_flashes()are used (notapp.user) - User data is escaped;
|rawonly on sanitized content - Inline scripts/styles carry a nonce
Features
- Menu locations are declared in
theme.jsonand drawn in the template - Widget areas are drawn with
cp_widget_area - Colours use the
--cp-*variables (so the administrator's colour choice works) -
cp_font_stylesheet()is linked after the theme's CSS - Cookie consent:
cp_consent_mode()andcp_cookie_consent() - Language switcher and
cp_hreflang_links() - TR and EN translations
Quality
- No external CDN calls (font, icon, script)
- Mobile, dark/light, print views were tried
- Accessibility: skip link (
skip-link), focus visibility, contrast,langattribute - It is drawn even when a module is off (
cp_path_if_exists,cp_widget_areamay return empty) - Templates that depend on the default theme are documented