Safe Tailwind classes for editors
CMS blocks in Magento are edited by content editors in the WYSIWYG, not by developers in phtml files. Without fixed Hyvä CMS block styling conventions, inconsistent layouts appear, along with broken utility classes the Tailwind JIT compiler never generated, and CSP violations from inline styles. A small, documented class set solves this problem for good.
Table of Contents
- 1. Why CMS blocks need their own class system
- 2. The content scanning problem: Tailwind cannot see a database
- 3. Defining a fixed class set for editors
- 4. Safelist instead of free class choice
- 5. Block variants through data attributes
- 6. CSP compliance: no inline styles in CMS content
- 7. Semantics and accessibility in CMS blocks
- 8. Common mistakes in CMS block styling
- 9. Approaches to CMS block styling compared
- 10. Summary
- 11. FAQ
1. Why CMS blocks need their own class system
Static phtml templates are written by developers, checked against the Tailwind configuration, and built before deployment. CMS blocks, in contrast, live as HTML in the database and are edited by content editors in the WYSIWYG or directly in source code mode. This is exactly where the problem for Hyvä CMS block styling begins: an editor can type any Tailwind class into the editor, but the Tailwind JIT compiler only generates CSS for classes it actually finds in the configured files at build time.
Without clear conventions for Hyvä CMS block styling, a patchwork quickly emerges: one editor uses bg-blue-600, another uses bg-sky-500, a third copies an old class from a Word document that does not even exist in Tailwind. The result is blocks that look fine in the editor but stay unstyled on the live shop, because the corresponding CSS is missing from the build. The following sections show how to solve this problem structurally instead of reactively.
The framing matters: CMS content in Magento is typically stored as free text and rendered through \Magento\Cms\Model\Template\FilterProvider, with widget directives like {{widget type="Magento\\Cms\\Block\\Block" block_id="..."}}. This rendering has nothing to do with the Tailwind build, the CSS must already exist independently of it before the editor saves the block.
2. The content scanning problem: Tailwind cannot see a database
Tailwind CSS v4 scans the files specified in the @source directory or the configuration for class names and only generates actual CSS for the matches it finds. Database content, such as CMS block content, does not belong to these sources by default. This is the core of the problem in Hyvä CMS block styling: even a syntactically correct Tailwind class that only exists in the database produces no CSS if it never appears anywhere in the scanned source code.
One solution is a periodic export of all CMS block and CMS page content into a single text file that is additionally registered as a Tailwind source. This works but is fragile, because it requires an extra build step and is easily forgotten when an editor uses a new class after the last export. A more robust approach is the one from section 3: a fixed, pre built class set that is guaranteed to land in the CSS bundle regardless of the actual CMS content.
// tailwind.config.js — include exported CMS content as an additional source
// Regenerate cms-content-dump.html via a cron job or deploy hook
export default {
content: [
'./app/design/frontend/Vendor/theme/**/*.phtml',
'./app/design/frontend/Vendor/theme/web/js/**/*.js',
// Fallback source: exported CMS block/page HTML for JIT scanning
'./var/tailwind/cms-content-dump.html'
]
};
3. Defining a fixed class set for editors
The most robust approach for Hyvä CMS block styling is not to give editors raw Tailwind utilities at all, but a small set of semantic component classes defined through @layer components. Instead of class="bg-sky-600 text-white font-bold py-3 px-6 rounded-xl", the editor simply types class="cms-btn". This class is guaranteed to exist in the CSS bundle, because it is defined in a real phtml or CSS file and therefore picked up by the build, regardless of the database content.
This pattern drastically reduces the error rate: there are no more misspelled color values, no inconsistent spacing between different editors, and no classes that simply do not exist. For Hyvä CMS block styling this means developers define the vocabulary once, editors combine it freely, and the visual consistency of the entire shop is preserved, all without editors having to understand Tailwind itself.
/* app/design/frontend/Vendor/theme/web/tailwind/tailwind-source.css */
@layer components {
.cms-btn {
@apply inline-flex items-center justify-center bg-sky-600 text-white font-bold
py-3 px-6 rounded-xl hover:bg-sky-700 transition-colors;
}
.cms-btn-outline {
@apply inline-flex items-center justify-center border-2 border-sky-600 text-sky-700
font-bold py-3 px-6 rounded-xl hover:bg-sky-50 transition-colors;
}
.cms-grid-2 {
@apply grid grid-cols-1 md:grid-cols-2 gap-6 my-8;
}
.cms-grid-3 {
@apply grid grid-cols-1 md:grid-cols-3 gap-6 my-8;
}
.cms-card {
@apply rounded-2xl border border-slate-200 p-6 bg-white shadow-sm;
}
}
4. Safelist instead of free class choice
Some editors still need flexibility, for example different background colors for seasonal campaign blocks. For this limited case, an explicit safelist in the Tailwind configuration is the right solution: a defined, finite list of allowed classes is always included in the CSS bundle regardless of content scanning. This differs fundamentally from fully free access to all Tailwind utilities, because the list is deliberately curated and documented.
What matters for Hyvä CMS block styling is keeping the safelist as small as possible and making it available to editors as a documented selection, for example as a short table in an internal wiki. An uncontrolled safelist growing to hundreds of entries leads to the same CSS bundle size problems as the old Tailwind v2 without a JIT compiler, and defeats the actual purpose of the utility first approach.
// tailwind.config.js — curated safelist for seasonal campaign blocks
export default {
safelist: [
'bg-sky-600', 'bg-rose-600', 'bg-amber-600', 'bg-emerald-600',
'text-sky-700', 'text-rose-700', 'text-amber-700', 'text-emerald-700',
{ pattern: /^(bg|text)-(sky|rose|amber|emerald)-(50|100)$/ }
]
};
5. Block variants through data attributes
For more complex block variants, for example a hero section with centered versus left aligned text, a data-block-variant attribute is more robust than multiple Tailwind classes in the CMS content. The editor selects a documented value like data-block-variant="hero-centered" in source code mode, while the entire visual behaviour is defined through CSS attribute selectors in the theme. This reduces the attack surface for Hyvä CMS block styling mistakes to a minimum, because only a few, reviewed values exist.
This approach has another benefit: if the design of a variant changes, only the CSS rule in the theme needs adjusting, not every single CMS block in the database. Across hundreds of blocks spread over multiple store views, that is the decisive difference between a maintainable and an unmaintainable Hyvä CMS block styling strategy.
<!-- CMS block content, entered by an editor in source code mode -->
<div class="cms-hero" data-block-variant="hero-centered">
<h2>Summer Collection 2026</h2>
<p>Up to 40 percent off, while stock lasts.</p>
<a class="cms-btn" href="/sale.html">Discover now</a>
</div>
<!-- Referencing an existing CMS block via widget directive -->
{{widget type="Magento\Cms\Block\Block" block_id="footer_trust_badges"}}
6. CSP compliance: no inline styles in CMS content
The Hyvä CSP theme requires a strict content security policy that blocks inline styles without a nonce by default. Editors tend to add colors and spacing directly as style="color: red;" in the WYSIWYG editor, because the visual editor interface offers that option prominently. For consistent Hyvä CMS block styling, this option must be disabled in the TinyMCE editor, or at least clearly documented as forbidden.
The replacement is exclusively the predefined classes from section 3 and the safelist from section 4. Both paths produce valid, CSP compliant CSS, because they run through the Tailwind build pipeline instead of ending up as inline attributes in the HTML. A once configured TinyMCE style formats dropdown that offers exactly the documented Hyvä CMS block styling classes even makes this path more convenient for editors than free inline styling.
7. Semantics and accessibility in CMS blocks
CMS blocks are often embedded in existing pages, for example as a footer trust badge or a category page description. A common problem: editors use an <h1> heading inside a block in the WYSIWYG, even though the page already has its own <h1>. Clean Hyvä CMS block styling includes a fixed heading convention, typically starting at <h2> inside every CMS block, so the document structure stays consistent across all embedded blocks.
Equally important is the correct role for blocks that act as standalone regions, for example role="region" with aria-label for a newsletter signup block. These semantic attributes can be baked into the same predefined classes from section 3, so editors get them automatically without having to learn ARIA rules themselves.
8. Common mistakes in CMS block styling
The most common mistake is copying Tailwind classes directly from the developer theme into the CMS editor, without checking whether these classes actually exist in the built CSS bundle. Classes like lg:grid-cols-[1fr_380px] with arbitrary values work in a phtml template because they get scanned at build time, but disappear without a trace in CMS content if no additional scanning is set up.
A second mistake is the lack of central documentation for allowed CMS classes. Without this reference, every editor invents their own solutions, and Hyvä CMS block styling slowly drifts apart over months. A third problem is forgetting responsive variants for images inside CMS blocks, for example missing object-cover and aspect-ratio, which leads to distorted images on mobile devices.
9. Approaches to CMS block styling compared
There are several strategies for connecting Tailwind CSS with dynamic CMS content. The table below ranks them by robustness and maintenance effort for Hyvä CMS block styling in practice.
| Approach | Risk | Maintenance effort | Recommendation |
|---|---|---|---|
| Free Tailwind classes in the WYSIWYG | Missing CSS, typos | High | Avoid |
| CMS content dump as scan source | Forgotten re export | Medium | Only as a supplement |
| Fixed component classes (cms-btn) | Very low | Low | Primary strategy |
| Curated safelist for exceptions | Low, with a small list | Low | Supplementary for variants |
Mironsoft
Hyvä theme development and Magento CMS conventions
Editors who build consistent blocks without developers?
We build documented CMS block class sets for your Hyvä theme, including TinyMCE integration, safelisting and a CSP compliant setup without inline styles.
Class library
Documented set of cms-btn, cms-grid and cms-card components
Editor integration
TinyMCE style formats for editors with no Tailwind knowledge
CSP audit
Review of existing CMS blocks for inline style violations
10. Summary
Consistent Hyvä CMS block styling does not come from policing every single editor, but from a small, documented vocabulary of component classes like cms-btn, cms-grid-2 and cms-card, defined through @layer components. These classes are guaranteed to exist in the CSS bundle, regardless of whether the actual CMS block content was ever picked up by the Tailwind scanner. For edge cases, a curated safelist adds limited flexibility without letting the bundle size grow out of control.
On top of that, a fixed heading convention and avoiding inline styles secure accessibility and CSP compliance for every single block. Once these conventions are established and made visible in the TinyMCE editor, support requests from editors drop drastically, and the visual appearance stays consistent across hundreds of CMS blocks.
Hyvä CMS Block Styling Conventions — The Essentials at a Glance
Component classes
@layer components defines cms-btn, cms-grid-2, cms-card for editors instead of raw utilities.
Safelist for exceptions
A small, curated safelist allows limited color variants without blowing up the CSS bundle size.
CSP compliance
No inline styles in CMS content, only predefined, built classes should be used.
Semantics
Headings consistently start at h2, regions get role and aria-label through the component class.