← Back to blog

Hyvä Tailwind Customization: Colors, Fonts and Safelists

Rodrigo Biazzi

Tailwind CSS is the part of Hyvä that changes the most for a Magento frontend team. On Luma you override LESS variables and mixins. On Hyvä you mostly write utility classes in templates and configure a design system in one place. Done well, a Hyvä theme ends up with less custom CSS than a typical Luma theme. Done badly, it ends up with the same color hex code typed in two hundred templates. This article is about doing it well: where the configuration lives, how to set brand colors and fonts, how to write reusable component styles, and how to deal with classes that come from the database.

Where the Tailwind setup lives

Each Hyvä theme has its own Tailwind build in web/tailwind. When you create a child theme you copy that folder from the parent theme. Your theme then owns its build, its configuration and its compiled web/css/styles.css. The setup steps are in the Hyvä theme setup guide.

The exact files inside web/tailwind depend on the Hyvä version. Hyvä releases based on Tailwind v3 use a JavaScript config file, tailwind.config.js, plus a source CSS file that imports component styles. Recent Hyvä releases moved to Tailwind v4, where most of the theme configuration is written in CSS with the @theme directive. The ideas below apply to both. I show both syntaxes where they differ. Check your package.json for the Tailwind version before following any example online.

Content sources: what Tailwind scans

Tailwind generates CSS only for class names it finds in the files it scans. On a Hyvä theme those files are:

  • Templates and layout XML in your own theme.
  • Templates in the parent Hyvä theme, so classes used there are not lost.
  • Templates in modules that register themselves for the Hyvä build, collected into app/etc/hyva-themes.json by bin/magento hyva:config:generate.

If a class is used in a file outside these paths, it will not be in the compiled CSS. This is the most common Tailwind problem on Hyvä stores, and it is almost always a missing content path or a class that only exists in the database.

A classic mistake is building class names dynamically in PHP or JavaScript:

<?php // Tailwind cannot see these classes: ?>
<div class="text-<?= $escaper->escapeHtmlAttr($color) ?>-600"></div>

<?php // Tailwind can see these, because the full class names appear in the file: ?>
<?php $classes = ['red' => 'text-red-600', 'green' => 'text-green-600']; ?>
<div class="<?= $escaper->escapeHtmlAttr($classes[$color] ?? 'text-gray-600') ?>"></div>

Tailwind scans text, it does not run your code. Always write complete class names somewhere it can read them.

Brand colors, fonts and spacing

Do not scatter hex codes across templates. Define the brand palette once and use semantic names. The Hyvä default theme already uses named colors such as primary and secondary in its templates, so changing those values restyles a large part of the store without touching markup.

Tailwind v3 style config

// web/tailwind/tailwind.config.js (excerpt, Tailwind v3 based Hyvä versions)
module.exports = {
  theme: {
    extend: {
      colors: {
        primary: {
          lighter: '#3b6fd8',
          DEFAULT: '#1d4fb8',
          darker: '#123a8c'
        }
      },
      fontFamily: {
        sans: ['Inter', 'system-ui', 'sans-serif']
      }
    }
  }
};

Keep the rest of the copied config as it is. It contains the content paths and the Hyvä helper that merges module configuration. Only change the theme section unless you know why the rest is there.

Tailwind v4 style config

/* in the theme's source CSS file, Tailwind v4 based Hyvä versions */
@theme {
  --color-primary: #1d4fb8;
  --color-primary-lighter: #3b6fd8;
  --color-primary-darker: #123a8c;
  --font-sans: "Inter", system-ui, sans-serif;
}

In v4 each CSS variable in @theme becomes a set of utilities. --color-primary gives you bg-primary, text-primary, border-primary and so on. The variables are also available as normal CSS custom properties, which is handy for the occasional rule that cannot be expressed with utilities.

Web fonts

Self host fonts in the theme's web/fonts folder and declare them with @font-face in the source CSS. Use font-display: swap and preload only the one or two files used above the fold. Loading five weights from an external font service will undo a good part of what Hyvä gains on Core Web Vitals. See Magento 2 Core Web Vitals for how fonts affect LCP and CLS.

Reusable component styles

Utility classes in templates are the default. But some patterns repeat across dozens of templates: buttons, form inputs, badges, cards. For those, define a component class with @apply in the theme CSS so the markup stays short and the style changes in one place.

.btn-acme {
  @apply inline-flex items-center px-4 py-2 rounded-md font-semibold;
  @apply bg-primary text-white;
}
.btn-acme:hover {
  @apply bg-primary-darker;
}

The Hyvä default theme already ships component styles for buttons, forms and other common elements in its source CSS files. Look there first. Changing an existing class like btn-primary in your child theme is better than creating a parallel one that half the templates use.

A good rule for when to use @apply: if the same group of five or more utilities appears in more than three templates, it is a component. If it appears once, leave the utilities in the template.

Classes from the database

CMS blocks, CMS pages, Page Builder content and widget parameters live in the database. Tailwind never sees them. If an editor types class="bg-primary text-white p-6" in a CMS block and nothing else in the theme uses p-6, the block renders without that padding.

There are three ways to deal with this:

  1. Safelist the classes editors are allowed to use. In v3 this is the safelist key in the config. In Tailwind v4 the equivalent is the @source inline() directive in CSS, available from Tailwind 4.1.
  2. Export CMS content to files that Tailwind scans. Some teams keep CMS block HTML in the repository and deploy it with data patches. Then the HTML files are just another content path. This also gives you version control for content, which is a bonus. Data patches are covered in declarative schema and data patches.
  3. Give editors a small set of component classes. Instead of free utility classes, document .cms-banner, .cms-cta and similar classes defined in the theme CSS. Editors use those and the theme controls how they look.

Option 3 scales best on stores with many content editors. It also stops the site from slowly filling up with one off styles.

// Tailwind v3 safelist example
module.exports = {
  safelist: [
    'bg-primary',
    'text-white',
    { pattern: /^(p|px|py)-(4|6|8)$/ }
  ]
};

Be careful with pattern safelists. A broad pattern can add hundreds of kilobytes of CSS that nobody uses.

Page Builder

Page Builder output on a Hyvä store needs Hyvä compatible styling for its content types. The options for that have changed across Hyvä versions, so check the current Hyvä documentation and decide early whether the store will use Page Builder heavily. If marketing lives in Page Builder, test the most used content types (rows, columns, banners, sliders) on the new theme before you estimate the rest of the migration.

Keeping the CSS small

  • Check the size of web/css/styles.css after each build. A sudden jump usually means a new safelist pattern or a content path that now includes something huge, like a whole vendor folder.
  • Do not add Tailwind plugins you do not use.
  • Run the production build, not the development build, before deploy. The production script minifies the output.
  • Avoid writing large amounts of custom CSS in the source files. If you find yourself writing a lot, check whether a utility already does it.

Workflow tips

Run the watch script while working on templates so CSS rebuilds on save. Use the browser devtools to confirm a class is present in the compiled CSS before debugging anything else. When upgrading Hyvä, diff the parent theme's web/tailwind folder against your copy. New Hyvä versions sometimes change the build setup, and your child theme does not pick those changes up automatically because it has its own copy.

FAQ

Why is a Tailwind class missing on my Hyvä store?

Tailwind only generates classes it finds in scanned files. The class is probably used in a CMS block, built dynamically, or lives in a module that is not in app/etc/hyva-themes.json. Safelist it or add the path.

Should I still write LESS on a Hyvä theme?

No. Hyvä does not use Magento's LESS pipeline. Styling is Tailwind utilities in templates plus a small amount of CSS in the theme's Tailwind source files.

Is Hyvä on Tailwind v3 or v4?

Older Hyvä releases are on Tailwind v3 and recent ones moved to v4. Check the tailwindcss version in your theme's web/tailwind/package.json before following examples.

When should I use @apply?

For patterns repeated in many templates, such as buttons, inputs and badges. For one off styling, keep the utility classes in the template.

I am available for freelance Magento and Hyvä frontend work. If you need help with your theme, get in touch.

Related articles