The CMS Tailwind JIT module
Using Tailwind classes in CSS content requires the classes to be present in the compiled styles.css
file.
Out of the box, only classes will work that also happen to be used in some frontend template file that is included in the Tailwind purge content configuration.
The CMS Tailwind JIT module provides automatic Tailwind CSS styles for CMS content in Hyvä Themes.
It currently features tailwind JIT compilation and adminhtml previews for
- CMS Blocks
- CMS Pages
- Product Description + Short Description
- Category Description
This module works with both TinyMCE and Magento_PageBuilder.
In PageBuilder the Content Preview works for CMS Block content and HTML Code content.
Installation
Choosing a version
Tailwindcss v3 or v2?
The CMS Tailwind JIT module releases 1.1.x support Tailwindcss v3, and releases 1.0.x support Tailwindcss v2.
Run the following command in your Magento root directory to see a list of all available versions:
For Hyvä license holders
-
To install the latest version via composer
To install the latest Tailwind v2 version, use -
Enable module and run apply database changes
For contributions
- Install via composer
- Enable module and run apply database changes
Disabling PageBuilder
If the PageBuilder modules are disabled on an instance, a JS mixin for the admin scope needs to be disabled.
Create a file view/adminhtml/requirejs-config.js
in a module with the following entry:
var config = {
config: {
mixins: {
'Magento_Ui/js/form/form': {
'Hyva_CmsTailwindJit/js/form/pagebuilder-form-submit-mixin': false
}
}
}
};
Next, be sure to add Hyva_CmsTailwindJit
as a dependency in the modules etc/module.xml
sequence, so it is loaded with a higher priority:
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="My_Module">
<sequence>
<module name="Hyva_CmsTailwindJit"/>
</sequence>
</module>
</config>
Then flush the cache and - if in production mode - run static-content:deploy.
Using tailwind classes containing single quotes in Alpine :class attributes
In this case the single quotes in the class name have to be quoted, because they are part of a JS string.
For example:
A bug in Tailwind causes the generated CSS to include the quotes in the generated styles. To work around the issue, add the unquoted class name in a HTML comment, like so:
This way the generated CSS will contain both versions of the class, but the result will work as expected.
How it works
When a CMS block, a CMS page, a category description, or a product description is edited in the adminhtml area, the CMS contents are passed to an embedded Tailwind JIT compiler running in the browser. Tailwind then compiles a list of styles used in the CMS content. The styles are compiled for each store view that uses a Hyvä theme and is assigned to the CMS entity.
When the entity is saved, the compiled CSS is persisted in the tables hyva_cms_block_tailwindcss
, hyva_cms_page_tailwindcss
, hyva_catalog_product_tailwindcss
and hyva_catalog_category_tailwindcss
.
When an entity is rendered in a Hyvä theme on the frontend, the compiled styles for that bit of content are rendered inline in a <style>
tag before each block or page content.
Custom Tailwind Configuration
You may specify a custom configuration in a web/tailwind/tailwind.browser-jit-config.js
file.
You can override this file name for a theme by creating a file etc/cms-tailwind-jit-theme-config.json
in a theme:
{
"tailwindBrowserJitConfigPath": "../../../../../app/design/frontend/My/theme/web/tailwind/tailwind.browser-jit-config.js"
}
/
character, it is treated as an absolute path. Any other character will cause the path
to be treated as relative.If a specified file doesn't exist or can't be read, it is silently ignored, and no custom tailwind config will be used for that theme.
Only a subset of the regular tailwind configuration is supported because it will be evaluated in the browser context.
First, only module.exports.theme
may be specified.
No calls to require()
or resolveConfig()
are allowed inside the module.exports
object.
Two plugins are available during the config processing in the browser (with exactly these constant names):
const { spacing } = require('tailwindcss/defaultTheme');
const colors = require('tailwindcss/colors');
Example tailwind.browser-jit-config.js
const { spacing } = require('tailwindcss/defaultTheme');
const colors = require('tailwindcss/colors');
module.exports = {
theme: {
container: {
center: true
},
extend: {
colors: {
'my-gray': '#888877',
primary: {
lighter: colors.purple['300'],
"DEFAULT": colors.purple['800'],
darker: colors.purple['900'],
},
}
},
}
}
Merging tailwind configs
To avoid declaring the same theme extends in both the tailwind.config.js
and the tailwind.browser-jit-config.js
, the
browser-jit-config can be merged into the regular config by appending the following snippet to the end of the
tailwind.config.js
file:
if (require('fs').existsSync('./tailwind.browser-jit-config.js')) {
function isObject(item) {
return (item && typeof item === 'object' && !Array.isArray(item));
}
function mergeDeep(target, ...sources) {
if (!sources.length) return target;
const source = sources.shift();
if (isObject(target) && isObject(source)) {
for (const key in source) {
if (isObject(source[key])) {
if (!target[key]) Object.assign(target, { [key]: {} });
mergeDeep(target[key], source[key]);
} else {
Object.assign(target, { [key]: source[key] });
}
}
}
return mergeDeep(target, ...sources);
}
mergeDeep(module.exports, require('./tailwind.browser-jit-config.js'));
}
Custom User CSS
The tailwind-source.css
is completely ignored, but it is possible to add custom CSS by creating a
web/tailwind/tailwind.browser-jit.css
file in your theme.
The contents will be passed to the JIT compiler in the browser and included in the styles for the CMS content.
The path to the file for the custom CSS can be configured for a theme by creating a file
etc/cms-tailwind-jit-theme-config.json
in a theme:
{
"tailwindBrowserJitCssPath": "../../../../../app/design/frontend/My/theme/web/tailwind/tailwind.browser-jit.css"
}
/
character, it is treated as an absolute path. Any other character will cause the path
to be treated as relative.If a specified file doesn't exist or can't be read, it is silently ignored, and no custom CSS will be used for that theme.
How to build
This step is only required if you want to customize the JIT compilation code!
This step is only required if you want to customize the JIT compilation code!
For 99.9% of all installations, this is not the case.
The build instructions are only included in the README, so it is easier to pick up development again when a new version of the Tailwind JIT compiler is released.
The embedded Tailwind JIT compiler is based on tailwind-jit-cdn.
The source can be found at src/view/adminhtml/tailwind-jit
.
To build, change into that directory and run yarn install
.
Then run yarn build
to compile the JIT to the src/view/adminhtml/web/js
directory.
A yarn watch
command is available for convenience during development.
How to re-use for other CMS entities
This module is designed to be re-used for other custom HTML content created on adminhtml pages.
There are some steps to follow when using the embedded JIT:
- Initialize the JIT on the admin page
- Observe content changes and pass them to the JIT
- Store the compiled CSS, so it is submitted when the content is saved
- Create a database table to store the CSS for the entity
- Observe the entity being saved and store the compiled CSS in the table
- When the entity content is rendered on the frontend, pass it through
\Hyva\CmsTailwindJit\Model\PrefixJitClasses::prefixJitClassesInHtml
first - Then pass the generated CSS through
\Hyva\CmsTailwindJit\Model\PrefixJitClasses::prefixJitClassesInCss
and render it in a<style>
tag
For an example of the last two steps, see \Hyva\CmsTailwindJit\ViewModel\CategoryTailwindCss::prefixCategoryCmsTailwindCssJitClasses
.
Initializing the JIT on a page
To use the JIT on your custom content, include the tailwind_jit
layout handle in the adminhml layout XML for your page to include the Tailwind JIT.
<iframe id="tailwindcss-jit"
in the source if you are interested.
Compiling content
Content can be passed to the JIT compiler by calling the JavaScript function
window.tailwindCSS.process(htmlContent, customConfig, customStyles)
.then((css) => {
// do something with the compiler output
});
customConfig
and customStyles
arguments are optional.
If you want to support custom tailwind.config.js
configurations, see below how to get the custom config for a given theme.
Custom styles passed in the third argument will be included unchanged in the JIT output.
Getting the custom tailwind-browser-jit-config
In order to call tailwindCSS.process
with custom configurations, it is necessary to get a list of the available configurations.
Then it is the responsibility of your code to iterate over these configurations and call process
with each configuration.
To get the custom tailwind config for a given store ID, call:
To get a themes tailwind config by theme identifier, use:
The theme identifier is what is used in the theme's registration.php
, for example, "frontend/Hyva/default"
.
Getting a map of store IDs to Hyvä theme identifiers:
To compile styles for different themes, it is necessary to know which stores have Hyvä themes.
To fetch a map of all stores that use Hyvä based themes to their respective theme identifiers, use:
To limit the map to specific stores:
The returned map only contains stores that have Hyvä based themes.
Getting a list of stores for given websites
If an entity is associated to websites instead of stores (for example, like products), it will be necessary to map website IDs to the list of store IDs, so the appropriate list of tailwind themes can be determined.
To fetch an array of all store IDs, use:
To limit the array of store IDs to specific websites: