Dynamically created classes in twig templates not generated using PostCSS

Created on 17 February 2025, about 2 months ago

Problem/Motivation

Using the 5.0.x-dev branch to create a website based on TailwindCSS v4 and DaisyUI 5.0-beta - and to generate and deliver the CSS code locally, I am testing a slightly modified version of this theme that has DaisyUI, TailwindCSS, typography all installed as NPM packages, and use postcss to build the CSS files from all the Twig templates.

This workflow works perfectly - except for one _slight_ issue - in that Twig templates that dynamically generate DaisyUI classes are basically skipped by PostCSS.

Steps to reproduce

Configure and use PostCSS to generate local versions of the CSS files - website will not display completely correctly.
- Visit the `admin/appearance/ui/components` page to see if all components display correctly.

Proposed resolution

There are a couple of solutions
1. Replace all joins of variables for a class name with explicitly `if` and `elseif` to test for each combination and explicitly assign a DaisyUI or TailwindCSS class - e.g. use `badge-sm` instead of `'badge-' ~ size`
2. Include a header block in the Twig template, explicitly mentioning all the classes that could be used - e.g.

{% if false %}
{% set size = ['badge-xs', 'badge-sm', 'badge-md', 'badge-lg', 'badge-xl'] %}
{% endif %}

I have applied a fix such as this to define all grid, column, and gap classes for the grid regions templates with 1, 2, and 3 regions - and with a rebuild of the CSS files, the UI components page displayed all the regions correctly.

Remaining tasks

- Decide if providing a simpler path for users that use processors such as PostCSS to generate optimal CSS files is of interest to this project.

- Decide on how best to explicitly declare CSS classes that could be dynamically generated by the Twig template

User interface changes

- none

API changes

- none
- If Twig template included documentation on possible classes, this may improve user understanding.

My workaround does not improve documentation of the Twig template in any manner or form - it just shows where _my_ problems lie and gives me a workaround.

To include documentation into the Twig template would require using a template that could be consistent with all other component Twig templates. The component YML files could be an excellent source of this structure and content.

Data model changes

- none

💬 Support request
Status

Active

Version

5.0

Component

Code

Created by

🇱🇺Luxembourg paddy_deburca

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @paddy_deburca
  • 🇫🇷France G4MBINI Bègles

    Arf ... sh....

    Just went to Tailwind4 docs and it says "Don't construct class names dynamically"... So I guess the whitelist mechanism of Tailwind3 doesn't exist anymore... sh...

    From a UI Suite / UI Patterns point of view it's very useful to construct classes dinamically. Let's take for example the grid 2 regions component where we do things like:
    From yml definition:

    gap:
          title: "Gap (mobile first)"
          description: "Utilities for controlling gutters between grid and flexbox items, for mobile first."
          type: integer
          enum:
            - 0
            - 2
            - 4
            - 8
            - 12
          "meta:enum":
            0: "0rem"
            2: "0.5rem"
            4: "1rem"
            8: "2rem"
            12: "3rem"
    

    Used in twig template it gives:
    {% set grid_classes = ['grid', gap ? 'gap-' ~ gap, ...] %}

    It then gives the opportunity in a content entity in Drupal to create an integer field (which is simpler than a big select field) that we map to the gap integer property from the yml definition of the component. With this approach, contributors can have control on gaps, cols, etc... from node edit or any other entities ...

    I would really want to stay close to those UI Suite best practices.

    Do you think a workaround could be to write explicitly those classes in comments in twig files ? (I don't really like that but it could be a solution ...)

  • 🇫🇷France G4MBINI Bègles

    Found that on stackoverflow:

    Good point. For now I have added @source "../../safelist.txt"; to file main app.css file. safelist.txt have all classes I need and it seems to work. Thanks. Also I found a Roadmap 4.0 statement that the safelist/blocklist is not implemented yet.

    source: https://stackoverflow.com/questions/79323991/safelist-in-tailwindcss-4-i...

    Could be a solution !

  • 🇱🇺Luxembourg paddy_deburca

    Hi, In TailwindCSS V4 - there is no need for a tailwind.config.js - you just have in your app.css something like the following

    @import "tailwindcss"; 
    
    @plugin "@tailwindcss/typography";
    @plugin "@tailwindcss/forms";
    @plugin "daisyui";

    Having a safe list that is rather removed from the component definition may lead to omissions.

    It could also be possible to have a safelist.css file per component definition, that defines all the classes defined for each component. Such a file could be as simple as (for Badge component)

    .badge-neutral,
    .badge-primary,
    .badge-secondary,
    .badge-accent,
    .badge-ghost,
    .badge-info,
    .badge-success,
    .badge-warning,
    .badge-error,
    .badge-xs,
    .badge-sm,
    .badge-md,
    .badge-lg,
    .badge-xl {}
    

    The preprocessor may be able to find these files and maybe add the class names to the token list for including in the generated CSS file.

    This may be a neat solution - the code remains unchanged with its continued use of variables.

  • 🇱🇺Luxembourg paddy_deburca

    Created an extra file in the avatar folder called avatar.safelist.twig containing

    {% set safelist = ['avatar-online', 'avatar-offline', 'w-8', 'w-10', 'w-12', 'w-16', 'w-20', 'w-24', 'w-28', 'w-32'] %}
    {% set safelist = ['mask-squircle', 'mask-heart', 'mask-hexagon', 'mask-hexagon-2', 'mask-decagon', 'mask-pentagon', 'mask-diamond', 'mask-square', 'mask-circle', 'mask-parallelogram', 'mask-parallelogram-2', 'mask-parallelogram-3', 'mask-parallelogram-4', 'mask-star', 'mask-star-2', 'mask-triangle', 'mask-triangle-2', 'mask-triangle-3', 'mask-triangle-4'] %}
    {% set safelist = ['rounded', 'rounded-xl', 'rounded-full'] %}

    All of the class names were manually siphoned out of the avatar.component.yml file.

    As it is in a Twig file, it was automatically picked up by the preprocessor.

    I tried with a CSS file - but as the class assignment was {} my editor picked it up as an error. I did not like that, so I move to Twig instead :-)

Production build 0.71.5 2024