Skip to content

About

Drupal theme built with Storybook and Webpack

Resources

Code of conduct

Contributing

Stars

94 stars

Watchers

19 watching

Forks

Latest commit

ย 

History

1,071 Commits

Folders and files

NameName
Last commit message
Last commit date
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Emulsify Design System

Emulsify Drupal

Emulsify is an open-source toolset for creating and implementing design systems on your website

Storybook, Emulsify Core 4, and a Vite-based build workflow for Drupal 11.3+

Emulsify Drupal is the official Drupal parent theme for Emulsify. It provides Storybook integration, Emulsify Core 4 tooling, and a Vite-based build workflow for Drupal 11.3+ with Drupal 12 forward compatibility. Your selected component library supplies the components.

Blocking CI verifies Drupal 11.3 on PHP 8.3, 8.4, and 8.5, and Drupal 11.4 on PHP 8.3. Drupal 12 beta readiness is blocking on PHP 8.5. dev-main remains an advisory compatibility check. Drupal 11.5 beta coverage is advisory on PHP 8.3.

The current 7.x series no longer depends on stable9; Emulsify now ships its own complete template layer instead of inheriting one from a Drupal parent theme.

Documentation

This README and UPGRADE.md are authoritative for the 7.x installation and upgrade workflow. The Emulsify documentation site currently documents the pre-7.x Webpack-based tooling and is being rewritten.

Quick Links

  1. Installation
  2. Usage
  3. Upgrade guide
  4. Twig component includes
  5. Sister-project parity contract
  6. Template override map
  7. Favicon generation lifecycle
  8. Optional design-token integration
  9. Release readiness checklist
  10. Twig hook variables and template suggestion precedence
  11. Extend parent templates
  12. Privacy and click-to-load media embeds
  13. Default social share images

Demo

  1. Storybook

License

Emulsify Drupal is licensed under GPL-2.0-or-later, matching Drupal.org Composer metadata guidance for hosted Drupal projects.

How To

Install or upgrade the parent theme

Run these commands from an existing Composer-managed Drupal site's project root, using that site's PHP/Composer environment. Examples assume a web document root and Drush 13+ on your path; use your project's wrapper (for example, ddev composer and ddev drush) when applicable.

The recommended distribution is drupal/emulsify from Drupal.org. Configure Drupal.org in the site's root composer.json if it is not already configured, then install the parent theme and its required companion module:

composer config repositories.drupal composer https://packages.drupal.org/8
composer require 'drupal/emulsify:^7.2' 'drupal/emulsify_tools:^2.2' --with-all-dependencies
drush en emulsify_tools -y

The parallel Packagist package is named emulsify-ds/emulsify-drupal. Existing sites using that package should keep that name when updating:

composer update emulsify-ds/emulsify-drupal drupal/emulsify_tools --with-all-dependencies

Both package names distribute the parent theme and Whisk starter. Choose one package name per site. The Packagist route also needs the Drupal.org repository in the site's root Composer configuration to resolve drupal/emulsify_tools; Composer does not load repository declarations from dependencies.

For an existing Drupal.org installation, update within your site's declared ranges and read the applicable upgrade notes:

composer update drupal/emulsify drupal/emulsify_tools --with-all-dependencies
drush updb -y
drush cr -y

The theme inherits its PHP floor from Drupal: PHP 8.3 for Drupal 11 and PHP 8.5 for Drupal 12. The declared core compatibility is ^11.3 || ^12; consult Drupal's PHP requirements for the supported PHP versions of your installed core release.

Identify your installed release

Composer's installed package version is authoritative for the parent theme. Run the command matching your site's package name:

composer show drupal/emulsify
composer show emulsify-ds/emulsify-drupal

The versions and source reference in that output identify what is installed; the matching composer.lock entry records what a subsequent composer install will reproduce. A development branch is identified by its source commit rather than by claiming the latest stable release. Published release history lives in GitHub Releases, with consumer actions in UPGRADE.md. The next-release draft contains unreleased changes.

The root package.json version is owned by @semantic-release/npm. That plugin writes the calculated version during release preparation, with npmPublish: false; @semantic-release/git then commits package.json and package-lock.json before the release is tagged. The checked-in version tracks the latest release or the next planned release while it is being prepared; Composer's installed version and source reference remain authoritative for an installed site. Future release versions are synchronized automatically. The release guard checks plugin order, committed files, and matching npm version metadata. The generated child theme's npm version belongs to that child project; generatedFromVersion records its starter lineage, not the currently installed parent theme version.

Generate a child theme

Emulsify Tools 2.2 or newer is required by the Emulsify Drupal parent theme. The drush emulsify and drush emulsify_tools:bake commands are implemented by Emulsify Tools, which must be installed and enabled. Its Drush helper delegates to Drupal Starterkit so both generation commands produce the same child theme. Generate a child theme with:

drush emulsify my_theme

The helper module also exposes the fully qualified command name:

drush emulsify_tools:bake my_theme

The whisk directory is the Whisk starter source used by both generation methods. Do not enable whisk directly; generated child themes keep emulsify as their runtime parent theme.

You can also generate the same child theme with Drupal core's standard Starterkit command from the root of your Drupal site. On Drupal 11.4 and newer, use the Composer-installed dr executable, which Drupal introduced as an experimental CLI:

vendor/bin/dr generate-theme my_theme --starterkit whisk --path themes/custom

For Drupal 11.3, which does not provide that executable, use the earlier entrypoint:

php web/core/scripts/drupal generate-theme my_theme --starterkit whisk --path themes/custom

These generation methods should be treated as equivalent:

  1. They generate the theme into web/themes/custom/my_theme.
  2. They use the Whisk starter source.
  3. They keep emulsify as the runtime parent theme for the generated child theme.
  4. They preserve project.emulsify.json so Emulsify Core can identify the generated Drupal project structure.
  5. They retain generatedFrom and generatedFromVersion metadata so support tooling can identify the Emulsify Drupal source and version used to create the child theme.

After generation:

  1. Enable the theme:
drush theme:enable my_theme -y
drush config:set system.theme default my_theme -y
drush cr -y
  1. Install the generated child theme's frontend dependencies:
cd web/themes/custom/my_theme
npm install
  1. Select and install the project's component library. Whisk does not provide a project source tree or build entrypoints. Its generated Drupal library file contains only commented CSS and JavaScript examples. Follow the component library's setup instructions; once it provides build inputs, update those examples and start the local tooling:
npm run develop

Generated child themes include the Vite and Storybook tooling from Emulsify Core 4. The selected component library owns the source structure and asset integration those tools consume.

AI-assisted theme development

Generated child themes include an AGENTS.md entrypoint, a library-neutral Drupal integration guide, and an emulsify-drupal-integration skill. They guide an AI through discovering the installed theme and component contracts, mapping Drupal content, and verifying the result in Drupal. The selected component library supplies its own structure, component APIs, tokens, and asset integration instructions.

For Codex, start in the generated child theme directory to discover its .agents/skills/ folder. When starting from the Drupal project root, point the AI to the generated child theme's AGENTS.md and integration skill, or add those references to the project's existing root guidance. Generation does not modify the Drupal project's root instructions. See the development guide for setup.

Write Twig component includes

For new project Twig, prefer Drupal Single Directory Component names:

{% include "my_theme:list" with {
  items: items,
} only %}

The Twig function form is also supported:

{{ include("my_theme:list", {
  items: items,
}, with_context = false) }}

Replace my_theme with the generated child theme machine name. Legacy namespace includes such as {% include "@components/button/button.twig" %} are still valid for existing projects and migrations, but they are not the recommended default for new project components. See docs/twig-component-includes.md for the component include guidance.

Verify your generated child theme

Run the install and test commands from the generated child theme directory, not from whisk:

cd web/themes/custom/my_theme
node --version
npm install
npm run test
npm run inspect:components

The component inspector discovers the project component inventory and reports component metadata, dependencies, configuration issues, and orphaned files. It does not require components to exist, so a newly generated component-neutral theme returns a valid empty report. Use JSON output for automation or view the command help:

npm run inspect:components -- --json
npm run inspect:components -- --help

After the selected component library supplies project sources, also run its asset-dependent checks:

npm run build
npm run storybook-build

New child themes declare @emulsify/core: ^4.5.0. Whisk advertises Node.js >=24, while Emulsify Core 4.5.0 requires >=24.13.0; the effective frontend floor is 24.13.0. Root release tooling separately requires 24.15 or newer. Both .nvmrc files select the Node 24 line without pinning its minor version. These declared requirements differ; a Node 24.0 installation does not satisfy Emulsify Core. Check the resolved Core package's engines when updating dependencies. Use npm install for the first local install, or npm ci when the generated child theme already has a committed package-lock.json.

These checks verify the expected local workflow:

  1. Compare node --version with the effective Node.js floor above.
  2. npm install installs Emulsify Core 4 and the generated child theme tooling.
  3. npm run test runs project tests and collects coverage. Add a project test first; the generated setup fails when no tests are found.
  4. npm run inspect:components reports the component inventory and related project health information.
  5. After component-library installation, npm run build compiles its Drupal-facing assets with the Vite build workflow.
  6. npm run storybook-build verifies the component library's static Storybook build.

Optional browser-based accessibility check:

npm run a11y

npm run a11y builds Storybook and runs the Emulsify Core accessibility check. Run it after component-library installation in local or CI environments that can use the required browser-based tooling.

Manage generated favicon packages

The generated favicon workflow is built around one portable SVG source stored in theme settings.

Emulsify Drupal owns the theme-facing parts of that workflow: the theme settings form, config defaults and schema, admin previews, frontend head tags, generated asset references in <theme>.settings, and sanitized SVG storage for config portability.

  1. Configure the package in the theme settings form for emulsify or a generated child theme.
  2. Save the theme settings form to generate or update the package during normal admin changes.
  3. Review package and portable-source diagnostics in the theme settings UI.

Emulsify Tools owns deployment-oriented Drush operations for those same settings. After configuration import or deploy, use the Emulsify Tools favicon commands to generate, inspect, or reset environment-local package files before public traffic reaches the environment. See the Emulsify Tools README for the full command documentation.

Runtime page requests never generate favicon files. If the configured package is missing, Emulsify skips favicon head tags until the theme settings form or the Emulsify Tools generate command creates the package.

Generated favicon packages require the PHP gd extension and the Imagick extension for SVG rasterization. If either extension is unavailable, the uploaded SVG can still be stored in configuration, but PNG and ICO package generation will fail until those extensions are installed.

The theme settings UI surfaces the current portable-source and package status. Portable SVG copies larger than 256 KB are flagged because very large config payloads are awkward to review and deploy.

See docs/favicon-generation.md for generated files, package location, generator limits, and deployment expectations.

Configure a default social share image

Open the child theme's settings, expand Social share image, and enable it. Upload a JPG, PNG, WebP, or GIF, or enter a path to an existing public image. The feature works independently of favicon generation and preserves existing social metadata, including Metatag images. Saved images have approximate Facebook, X/Twitter, LinkedIn, and Pinterest previews with desktop/mobile layouts. Optional title, description, and page URL defaults fill missing page metadata; a live table confirms their tag names and values before saving. See social share images for supported paths, preview controls, and deployment.

Contributing

The project maintainers have adopted a Code of Conduct that we expect project participants to adhere to. Please read the full text so that you can understand what actions will and will not be tolerated.

Contribution Guide

AI contributors should start with the repository's AGENTS.md, which distinguishes parent/starter maintenance from generated-theme work.

Please also follow the issue template and pull request templates provided. See below for the correct places to post issues:

  1. Emulsify Drupal
  2. Emulsify Tools Drupal Module
  3. Emulsify Twig Extensions

Build the parent theme assets

The parent theme's global styles and settings previews are authored in assets/ and built with @emulsify/core. Run these commands from this repository root using Node.js 24.15 or newer:

npm ci --ignore-scripts
npm run build
npm run lint

Use npm run develop to watch the Sass and JavaScript sources and produce readable output with source maps. Run npm run build before committing to restore minified production output without maps.

Source Drupal library output
assets/theme/global.scss dist/css/theme/global.css
assets/admin/favicon-preview.scss dist/css/admin/favicon-preview.css
assets/admin/favicon-preview.js dist/js/admin/favicon-preview.js
assets/admin/social-share-preview.scss dist/css/admin/social-share-preview.css
assets/admin/social-share-preview.js dist/js/admin/social-share-preview.js

Edit the sources, then include the rebuilt dist/ files in the same change. Production assets are committed so Composer installations do not require Node or an asset build. CI rebuilds them and checks that they match the committed output. The existing emulsify/global and emulsify/favicon_admin library names remain the attachment points; emulsify/social_share_admin supplies the social image preview.

project.emulsify.json restricts Core's source discovery to assets/, keeping the Drupal PHP classes in src/ outside the frontend build. Root linting covers PHP plus the JavaScript and Sass sources using Core's shared lint configuration. Generated child themes retain their own component-library build workflow.

Committing Changes

To facilitate automatic semantic release versioning, we utilize the Conventional Changelog standard through Commitizen. Follow these steps when committing your work to ensure semantic release can version correctly.

  1. Stage your changes, ensuring they encompass exactly what you wish to change, no more.
  2. Create a Conventional Commit message, either manually or with your preferred commit helper.
  3. Your commit message will be used to create the changelog for the next version that includes that commit.

Release Readiness

Run the release guard before merging packaging, Whisk starter, favicon settings, or release metadata changes, and before preparing a 7.x release:

npm run release:check

Use Node.js 24.15 or newer for local release tooling.

Release automation publishes from main with non-prefixed SemVer tags.

Use npm run release:check -- --skip-smoke when you only want the static metadata, README, duplicate-script, and schema checks. The static checks verify that favicon settings stay aligned across FaviconSettings::DEFAULTS, config/install/emulsify.settings.yml, and config/schema/emulsify.schema.yml.

Author

Emulsifyยฎ is a product of Four Kitchens โ€” We make BIG websites.

Contributors

Brian Lewis
Brian Lewis

๐Ÿ’ป ๐Ÿ“–
Randy Oest
Randy Oest

๐Ÿ’ป ๐Ÿ“–
Callin Mullaney
Callin Mullaney

๐Ÿ’ป ๐Ÿ“–
Patrick Coffey
Patrick Coffey

๐Ÿ’ป ๐Ÿ“–
Luke Herrington
Luke Herrington

๐Ÿ’ป ๐Ÿ“–
Aaron Couch
Aaron Couch

๐Ÿ’ป ๐Ÿ“–
Marc Berger
Marc Berger

๐Ÿ’ป ๐Ÿ“–
James Todd
James Todd

๐Ÿ’ป ๐Ÿ“–
Kurt Trowbridge
Kurt Trowbridge

๐Ÿ’ป ๐Ÿ“–
Chris Martin
Chris Martin

๐Ÿ’ป ๐Ÿ“–
Adam Erickson
Adam Erickson

๐Ÿ’ป ๐Ÿ“–
Chris Runo
Chris Runo

๐Ÿ’ป ๐Ÿ“–
Andy Carlberg
Andy Carlberg

๐Ÿ’ป ๐Ÿ“–
eatsmarter-benny
eatsmarter-benny

๐Ÿ’ป ๐Ÿ“–
Brian Perry
Brian Perry

๐Ÿ’ป ๐Ÿ“–
Israel Shmueli
Israel Shmueli

๐Ÿ’ป ๐Ÿ“–
John Karahalis
John Karahalis

๐Ÿ’ป ๐Ÿ“–
Mihaic100
Mihaic100

๐Ÿ’ป ๐Ÿ“–
Paul Sebborn
Paul Sebborn

๐Ÿ’ป ๐Ÿ“–

This project follows the all-contributors specification. Contributions of any kind welcome!

About

Drupal theme built with Storybook and Webpack

Resources

Code of conduct

Contributing

Stars

94 stars

Watchers

19 watching

Forks

Releases

Packages

Used by

Contributors

Languages