chameleon-system-private/theme-extensions-bundle

This bundle provides an alternative to the default theme and assets handling. It supports multiple themes in the same portal and uses Webpack and Babel to build assets from TypeScript and SASS.

8.47.1 2025-12-15 13:55 UTC

README

This bundle provides an alternative to the default theme and assets handling.

Key Advantages

  • More flexible theme selection (based on domains, pages or anything else)
  • Modern JavaScript syntax + modules
  • TypeScript support
  • SASS Support
  • Possible to use JavaScript package manager like npm or yarn
  • Watch/development mode for CSS and JS
  • CSS/JS code splitting

Main differences:

  • We use webpack / nodejs to build assets.
  • config.yml files in the snippet chain are ignored, so no less or js is loaded from there.
  • GetHTMLHeadIncludes, GetHTMLFooterIncludes, AddGlobalHeadIncludesListener and similar mechanisms are ignored.
  • The field less_file in pkg_cms_theme as well as chameleon.less will be ignored.
  • Assets and where/when to include them is configured in a static config file.

The way how views/snippets are loaded via snippet chain stays the same.

Existing Themes / Standard Shop Theme

This bundle discards many of the concepts and practices used in the default handling of themes and assets. This means that existing themes need to be rewritten. Also, it is not possible to use the Standard Shop Theme or build on it.

Themes as Bundles

This bundle relies on the concept of creating a bundle for every theme. Assets for a theme are located in the Resources folder of the bundle, only the built assets will be put into Resources/public. Views and twig-templates should live in Resources/views.

Setup

Download the Bundle

Open a command console, enter your project directory and execute the following commands to download the latest stable version of this bundle and the generator-bundle for the dev-environment:

$ composer require chameleon-system/theme-extensions-bundle "~6.2.0"
$ composer require --dev chameleon-system/theme-extensions-generator-bundle "~6.2.0"

Enable the Bundle

Add this to the AppKernel.php file of your project to the $bundles array:

    new \ChameleonSystem\ThemeExtensionsBundle\ThemeExtensionsBundle(),

Add the generator bundle only in dev environment:

    if ('dev' === $this->getEnvironment()) {
        $bundles[] = new ChameleonSystem\ThemeExtensionsGeneratorBundle\ThemeExtensionsGeneratorBundle();
    }

Run Database Updates

$ php app/console chameleon:updates:run

Active Theme

We usually determine the active theme only based on the portal. In this bundle, we add domains, layout templates and pages to the mix. Use the service chameleon_system_theme_extensions.active_theme_service to get the active theme.

An active theme can only be provided if all handlers are ready. For example, an active page has been set for the provider of the page theme. If all handlers are ready and an active theme could be loaded, the active theme is added to the request attributes.

It is also possible to add handlers with your own logic, for example to make the theme user-selectable. To do so, implement ActiveThemeProviderInterface and tag with chameleon_system_theme_extensions.active_theme_provider.

Create a Theme

Use the command chameleon_system:theme_extensions:generate_theme_bundle which will generate a bundle for you that already has everything needed to get started. The generated bundles have a README.md file with further instructions.

Module Includes

"Module includes" (GetHtmlHeadIncludes() and GetHtmlFooterIncludes() methods in modules) often contain CSS or JavaScript not suitable for every theme. To be able to make a choice if they should be included or not, there is a respective option for themes in the backend, so it can be configured for every theme individually.

We do the same for AddGlobalHeadIncludesListener, AddJqueryIncludeListener and AddAntispamIncludesListener. We also use the theme's configuration.

To take this option into account, we need to change the behaviour of the AddModuleIncludesListener. Because we can't decorate or replace it as it doesn't implement an interface, we remove it and add our own. See ReplaceAddModuleIncludesListenerPass if there should be problems with module includes.