How do you add spacing to theme.json with tailwind?

Is there any way to have the vite plugin provide spacing sizes to theme.json via the tailwind @theme static {...} css in the same way we can provide colours and typography styles?

As far as I know, spacing in TW4 is dynamic now. You’d need to define the spacing yourself in the base theme.json so it gets merged in when built.

Yes, define spacing directly in the theme.js. This is not a Tailwind approach but as it is a central, sensible set of defaults I have no issue using this. I like naming the options for their actual size but you can use anything. Changes to theme.js are not reflected in dev, only build.
Remember to add

"defaultSpacingSizes": false,

to override the defaults in v3.
A set that I use often:

"spacing": {
      "padding": true,
      "customSpacingSize": false,
      "defaultSpacingSizes": false,
      "blockGap": false,
      "margin": false,
      "spacingSizes": [
        {
          "size": "0.5rem",
          "slug": "20",
          "name": "8px"
        },
        {
          "size": "1rem",
          "slug": "30",
          "name": "16px"
        },
        {
          "size": "1.5rem",
          "slug": "40",
          "name": "24px"
        },
        {
          "size": "2rem",
          "slug": "50",
          "name": "32px"
        },
        {
          "size": "clamp(2rem, 1.6835rem + 1.3502vw, 3rem)",
          "slug": "60",
          "name": "48px(var)"
        },
        {
          "size": "clamp(2.5rem, 2.7089rem + 3.3755vw, 4rem)",
          "slug": "70",
          "name": "64px(var)"
        },
        {
          "size": "clamp(3rem, 2.3671rem + 2.7004vw, 5rem)",
          "slug": "80",
          "name": "80px(var)"
        },
        {
          "size": "clamp(5rem, 4.0506rem + 4.0506vw, 8rem)",
          "slug": "90",
          "name": "128px(var)"
        },
        {
          "size": "clamp(5rem, 4.0506rem + 4.0506vw, 10rem)",
          "slug": "95",
          "name": "160px(var)"
        }
      ]
    },
2 Likes

Ok cool, that’s what I thought.

The issue I’m having is that with this approach, you can’t use the WP auto generated css custom properties in css files because TW4 uses cascade layers.

Edit - you can use the WP auto generated layers, but TW4’s cascade layers approach means there are some specificity conflicts to be aware of if you’re writing css files alongside your tailwind classes. See below.

Theme.json doesn’t get built by the vite plugin if the cascade layers are removed (although I’m not sure if this is a bug or by design).

Here’s an example:

/* theme.json is built successfully */
@import "tailwindcss" theme(static);

/* theme.json is built successfully */
@import 'tailwindcss/preflight.css' layer(base);
@import 'tailwindcss/utilities.css' layer(utilities);
@import 'tailwindcss/theme.css' layer(theme);

/* theme.json is not built */
@import 'tailwindcss/preflight.css';
@import 'tailwindcss/utilities.css';
@import 'tailwindcss/theme.css';

I’m using tailwind to generate custom properties, and provide utility classes, but I’m not using it exclusively - I’m writing a lot styles in css files.

I would like to use the css custom properties generated by WP from the Global Styles in my css, like the spacing options:

/* theme.json: */
{...
  "spacingSizes": [
    ...
    {
      "name": "Default",
      "slug": "flow",
      "size": "clamp(0.8125rem, 0.2321rem + 1.8571vw, 1.625rem)"
    },
...}

/* header.css - imported to layer(theme) */
.header {
  display: flex;
  flex-flow: row wrap;
  gap: var(--wp--preset--spacing--flow);
}

The trouble is, it’s not possible to access these from within inside a cascade layer, and I don’t know if it’s possible to control which layer Wordpress places the custom properties in.

Do you have any recommendations? Or should I abandon the variable spacing and maintain the same spacing separately in theme.json and TW?

Edit - I was wrong about this, you absolutely can use custom properties between cascade layers.

I was actually seeing 2 different problems:

  1. The auto-generated custom properties will be converted to single hyphens. My theme.json had doubles. eg "name": "flow--half", so they are converted to --wp--preset--spacing--flow-half
  2. In my tailwind definitions, I’m only declaring --font-display and --font-body. But WP tries to use --wp--preset--font-family--heading on wp-block-heading elements, even if it’s not defined. Where I was tripping up is that the non-layered css takes precedence over layered css, and WP adds these styles as non-layered css. I had declared my typography styles like this: @import './common/typography.css' layer(theme); so it was being overridden.

Hope someone finds these ramblings useful!

That does appear to be by design (see:vite-plugin/src/index.ts at 4ee218334c28c5cd8d8529a818586887a4df37ad · roots/vite-plugin · GitHub)

You could probably fork the vite-plugin and remove the theme layer matching parts if that helps.

With tailwind v4, I just create custom --space- vars and access them like pb-(--space-s). That works well enough for me but I am not heavily using utility classes.

You could also modify the vite plugin to look for a custom var like --space- to generate your spacingSizes in the theme.json