cobalt-ui icon indicating copy to clipboard operation
cobalt-ui copied to clipboard

Feature request: Add option in plugin-tailwind to control `@import "tailwindcss";`

Open Hashversion opened this issue 3 months ago • 8 comments

Summary

Currently, @terrazzo/plugin-tailwind outputs a theme file that always includes @import "tailwindcss"; at the top. In some setups (monorepos, shared tokens packages, or when consumers already import Tailwind globally), this creates duplication or unwanted imports.

It would be useful to make this behavior configurable in the plugin, so projects can decide whether or not to include the @import "tailwindcss"; line.

Proposal & prior art

  1. Syntax

Add a new plugin option in terrazzo.config.js when configuring the Tailwind plugin. For example:

tailwind({
  filename: "tailwind-theme.css",
  includeTailwindImport: true // default
})

true → output file includes @import "tailwindcss"; (current behavior) false → omit the import (useful if Tailwind is already imported elsewhere)

  1. Alternatives

Option A (recommended): Boolean flag (includeTailwindImport), simple, minimal change. Option B: Always omit the import, breaking change for existing users who expect it.

  1. Breaking change?

If we default to true, this is not breaking (preserves current behavior). Only users who want to suppress the import would opt into the new flag.

  1. Prior art

Tools like Style Dictionary allow flexible control of output templates. PostCSS/Tailwind setups often separate concerns: apps import Tailwind globally, while design tokens packages only provide variables.

  1. Expected output

Current output (always):

@import "tailwindcss";

@theme {
  --color-primary: #3b82f6;
}

With includeTailwindImport: false:

@theme {
  --color-primary: #3b82f6;
}

Extra

Hashversion avatar Sep 12 '25 11:09 Hashversion

The use case makes sense!

Thinking about this more holistically, we might consider a more generalized name for this config value to cover potential cases where other imports may need to be included/excluded from a config.

This is just the first thought I had, feel free to disagree.

tailwind({
  filename: "tailwind-theme.css",
  includeImports: true // default
})

or maybe something more configurable (perhaps overkill):

tailwind({
  filename: "tailwind-theme.css",
  imports: {
    "tailwindcss": true,  // default
    "tw-animate-css": true, // additional import
  }
})

davejsdev avatar Sep 12 '25 13:09 davejsdev

or maybe something more configurable (perhaps overkill):

I actually like the 2nd one! I have come across scenarios where individual imports need to be adjusted. It’s also expandable—I believe people should be able to add arbitrary imports in case there’s something we don’t know about

drwpow avatar Sep 12 '25 15:09 drwpow

yes! I totally agree, I like the 2nd one too! how about making a bit more semantic, injectImports or prependImports?

tailwind({
  filename: "tailwind-theme.css",
  injectImports: { // semantic (inject)
    "tailwindcss": true,  
    "tw-animate-css": true,
  }
})

or

tailwind({
  filename: "tailwind-theme.css",
  prependImports: { // semantic (prepend)
    "tailwindcss": true, 
    "tw-animate-css": true,
  }
})

Hashversion avatar Sep 12 '25 15:09 Hashversion

If that's not overkill, then what about this?

An even more flexible option would be to include a template file:

tailwind({
  filename: "tailwind-theme.css",
  template: "template.tz.css"
})
/* template.tz.css */
@import 'tailwindcss';
@import 'tw-animate-css';

@custom-variant dark (&:is(.dark *));

@terrazzo-slot; // gets replaced with generated content in the output

/* other rules... */

If no overrides, the default template would just be the tailwind import:

@import "tailwindcss";

@terrazzo-slot;

To omit the tailwind import:

tailwind({
  filename: "tailwind-theme.css",
  template: null
})

davejsdev avatar Sep 12 '25 16:09 davejsdev

Ooh I like the template idea! Having @[custom name] is also great because any CSS parsers (and even browsers) ignore that.

To omit the tailwind import:

  tailwind({
   filename: "tailwind-theme.css",
   template: null
 })

I would just say the template is opt-in. Just omitting template in config would use the built-in one (and internally we should even use our own @terrazzo-slot or whatever to make sure it always works)

drwpow avatar Sep 12 '25 16:09 drwpow

Oh, I mean for bypassing the default template in order to just get the config without the tailwind import.

A template file with only @terrazzo-slot; would have the same effect, just a bit more work for the consumer.

davejsdev avatar Sep 12 '25 17:09 davejsdev

Do you recommend to add a comment after generating the theme?

1. if the template is null or undefined:

  tailwind({
   filename: "tailwind-theme.css",
   template: null
 })

in generated file tailwind-theme.css it says may be:

@import 'tailwindcss'; // add by default if template is not null

`/* ------------------------------------------
 *  Autogenerated by ⛋ Terrazzo. DO NOT EDIT!
 * ------------------------------------------- */`
@theme ... (generated theme)

2. if the template is provided:

  tailwind({
   filename: "tailwind-theme.css",
   template: "./template.tz.css"
 })

in generated file tailwind-theme.css it says may be:

`/* ----------------------------------------------------------
 *  @terrazzo-slot (Autogenerated by ⛋ Terrazzo. DO NOT EDIT!)
 * -----------------------------------------------------------*/`
@theme ... (generated theme)

Hashversion avatar Sep 13 '25 05:09 Hashversion

Wouldn't we still want the comment to be inserted at the top of the file? We'd still want to discourage editing the template content in the generated file, right?

We could direct the user to the template file by including the template in the comment when one is provided.

/* ------------------------------------------
 *  Autogenerated by ⛋ Terrazzo. DO NOT EDIT!
 *  template: ./template.tz.css
 * ------------------------------------------- */

@import 'tailwindcss';
@import 'tw-animate-css';

/* @terrazzo-slot start */
@theme ...
/* @terrazzo-slot end */

davejsdev avatar Sep 14 '25 19:09 davejsdev