The required plugin to power Mai themes.
Mai Engine makes use of two package managers, NPM for JavaScript and Composer for PHP packages.
Important: Remember to specify the --url parameter if on multisite. E.g --url=demo.bizbudding.com/success-business.
wp mai generate- Generates starter content for a site (Home page etc).wp option delete 'mai-engine'- Deletes all setting options.
-
Clone this repository into your WordPress site's
pluginsdirectory.git clone https://github.com/maithemewp/mai-engine.git
-
Change directories into the plugin folder from the command line:
cd mai-engine -
Install Composer and any PHP dependencies with the following command:
composer install
Please note that this step requires that you have Composer installed globally on your machine. We recommend using Homebrew to install Composer:
brew install composerexport PATH="$HOME/.composer/vendor/bin:$PATH"
-
Install Node packages:
npm install
Please note that this step requires that you have Node installed globally on your machine. We recommend using Homebrew to install Node:
brew install node
Mai Engine uses PHP Code Sniffer for linting and fixing coding standards. To lint all PHP files against WordPress coding standards run the following command:
composer phpcsTo have PHP Code Sniffer attempt to automatically fix any warnings run the following:
composer phpcbfTwo suites. The unit suite runs with no WordPress and no database, using brain/monkey to
mock WordPress functions. The integration suite boots real WordPress via wp-phpunit and
needs MySQL.
Test dependencies live in their own Composer project at tests/composer.json and install to
tests/vendor/. That is deliberate: the plugin deploys as a raw git tree with no build step,
so the committed vendor/ autoloader must never contain dev entries. Keeping the test
dependencies out of the root project means running the suites cannot regenerate it.
composer test-setup # installs tests/vendor, one time
mysql -u root -e "CREATE DATABASE mai_engine_tests" # integration suite only, one timenpm install and composer install do not install the test dependencies. If
composer test-unit reports tests/vendor/bin/phpunit: No such file or directory, run
composer test-setup.
composer test-unit # no WordPress, no database, sub-second
composer test-integration # boots WordPress, needs MySQL
composer test # bothDatabase connection is read from WP_TESTS_DB_NAME, WP_TESTS_DB_USER, WP_TESTS_DB_PASS
and WP_TESTS_DB_HOST, defaulting to mai_engine_tests / root / empty / 127.0.0.1.
Warning: the WordPress test bootstrap drops the WordPress core tables carrying the
configured $table_prefix in the configured database, on every run. It does not drop every
table, but pointing it at a real site's database with a matching prefix will destroy that
site's content. Keep the dedicated database name.
wp-phpunit/wp-phpunitandroots/wordpress-no-contentintests/composer.jsonare meant to track together. Bump both, and only viacomposer update -d tests.- The integration suite boots WordPress but does not activate the plugin, because
lib/init.phpexpects Genesis as the parent theme. Tests load the specificlib/files they exercise viatests/phpunit/integration/plugin-loader.php. - Integration tests extend
MaiIntegrationTestCase, notWP_UnitTestCasedirectly. The base class works aroundWP_UnitTestCasecalling PHPUnit 9 APIs that PHPUnit 10 removed. - Encoding fixture goldens are generated, not hand-written. Regenerate with
php tests/phpunit/unit/fixtures/generate.phpand review every changed golden by hand.
Mai Engine utilizes Gulp and Sass to automate tedious tasks, such as automatically generating the many stylesheets required by the child themes.
First you will need to install NPM on your machine:
brew install npmIt is also recommended to install NVM (Node Version Manager) to allow easy switching of Node versions:
brew install nvmNext, install the Gulp CLI globally on your machine. To install Gulp CLI run the following command from the terminal:
sudo npm install gulp-cli -gNow that all of the global packages are installed, navigate to the root directory of this plugin, e.g:
cd Sites/my-project/wp-content/plugins/mai-engineFrom there, make sure that Node is running the correct version (11.15.0). To do this, you will first need to run some commands to configure nvm correctly. A simple composer script is provided:
composer setup-nvm| Command | Description |
|---|---|
npm start |
Build all assets and watch for changes |
npm run release |
Full production build: validate composer, build assets, strip dev deps |
npm run beta |
Build assets, commit, push develop, merge/push beta, restore dev deps |
npm run dev |
Restore dev dependencies after a release build |
Beta release workflow:
- Bump the version in
mai-engine.php(e.g.2.39.0-beta.1) - Update
CHANGES.mdwith changelog entries - Run
npm run beta
Production release workflow:
- Set the final version in
mai-engine.php(e.g.2.39.0) - Update
CHANGES.md - Run
npm run release - Commit and push manually
Once the Gulp CLI and Node packages have been installed, you are ready to begin using the following Gulp tasks to automate development:
Default
Running the default gulp task will kick of development and Gulp will watch files for changes. When a change to a file is detected Gulp will run the build tasks and recompile assets.
gulpCSS
gulp build:cssJS
gulp build:jsTo create a new engine theme
gulp create --name=themename --composerGoals of the CSS system: Keep it DRY. Prioritize performance.
Please note: files in the assets/css/ directory should never be edited directly as any changes will be overridden when running the gulp build task. All changes should be made to the SCSS files in the assets/scss/ directory and then compiled using the gulp build:css command.
Organization
This project follows the ITCSS principal to organize the CSS files in such a way that they can better deal with CSS specificity. One of the key principles of ITCSS is that it separates your CSS codebase to several sections (called layers), which take the form of the inverted triangle. The structure is also based on the Sass Guidelines. More information about ITCSS can be found here.
- Abstracts – used with preprocessors and contain font, colors definitions, globally used mixins and functions. It’s important not to output any CSS in this layer.
- Base – reset and/or normalize styles, box-sizing definition, styling for bare HTML elements (like H1, A, etc.). These come with default styling from the browser so we can redefine them here. This is the first layer which generates actual CSS.
- Layout – the layout/ folder contains everything that takes part in laying out the site or application. These elements are usually only in one place and contain multiple components.
- Components – specific UI components. This is where the majority of our work takes place and our UI components are often composed of Objects and Components
- Utilities – utilities and helper classes with ability to override anything which goes before in the triangle, eg. hide helper class
- Plugins - styling for third party plugins. Not imported in the main stylesheet.
- Themes - theme specific styling. Should only contain custom property overrides if possible. Should be thought of as a config file.
Mai Engine uses both element-color and color-element naming convention for color variables, here's an explanation on how to use them:
These custom properties are the ones automatically generated by the theme config and Customizer settings. They should be thought of as the "color palette" and never be changed directly via CSS.
In the theme or engine CSS we use them in places like this:
/* Correct way to set a dark background for the site footer */
.site-footer {
background-color: var(--color-heading);
}We would never want to change the values like this, as it could affect the parent element if it is using a .has-heading- utility class:
/* Wrong way to make all headings in the site footer white */
.site-footer {
--color-heading: var(--color-white);
}These are the element custom properties that can be changed depending on their context, for example we can do this:
/* Right way to make all headings in the site footer white */
.site-footer {
--heading-color: var(--color-white);
}But we shouldn't use them like this, because the custom property may not be set:
/* Wrong way to set a dark background for the site footer */
.site-footer {
background-color: var(--heading-color);
}To summarise, we have the "color palette", which are globals and should never be changed in the CSS. They can only be changed from the theme config or the Customizer settings. Then we also have the "element properties" which should be used to change an elements styles depending on the context.
Kirki is vendored by hand and carries local patches. See patches/README.md for the authoritative list, with real diffs, the reasoning behind each one, and the paths they move to in Kirki 5.2.x.
Summary as of 2026-08-26:
kirki-packages/data-option/src/Option.phpreturns a field's declared$defaultrather than an empty string when a nested option key is missing.kirki-packages/module-webfonts/src/Webfonts/Downloader.phpcarries one commented out line recording the old Safari user-agent. It is inert and safe to drop.
Two entries previously listed here were wrong and have been removed. The Firefox user-agent was never our change, upstream shipped it in 5.1.0 (commit 6908691b) 49 minutes before the commit we vendored. And the md5( $url ) filename change was removed on 2026-08-26 because it stripped the .woff2 extension, so servers could not match font MIME or cache rules and every repeat visitor re-downloaded the fonts. patches/README.md records that with the measurements.