From 2f975f433eb439c4103265a647a6df6fe55ee330 Mon Sep 17 00:00:00 2001 From: LukeGus Date: Thu, 8 Oct 2026 13:54:37 -0500 Subject: [PATCH] fix: keep plugin tailwind variants in core's layer so overrides keep their order --- packages/plugin-sdk/CHANGELOG.md | 2 +- packages/plugin-sdk/cli/lib/css-classes.mjs | 12 +++++++----- scripts/plugin-build.test.ts | 12 ++++++++++++ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/packages/plugin-sdk/CHANGELOG.md b/packages/plugin-sdk/CHANGELOG.md index 4d36e6e74..dda5ef171 100644 --- a/packages/plugin-sdk/CHANGELOG.md +++ b/packages/plugin-sdk/CHANGELOG.md @@ -4,7 +4,7 @@ ### Fixed -- `termix-plugin build` puts a plugin's own Tailwind classes in core's utilities layer, so responsive, hover and group variants and `className` overrides on SDK components work. Classes core already ships stay below core's utilities as before. Rebuild a plugin to pick it up +- `termix-plugin build` puts a plugin's own Tailwind classes in core's utilities layer, so responsive, hover and group variants and `className` overrides on SDK components work. Plain classes core already ships stay below core's utilities as before, while variants core ships go with the plugin's own so `max-h-56 md:max-h-none` keeps its order. Rebuild a plugin to pick it up ## 1.0.3 diff --git a/packages/plugin-sdk/cli/lib/css-classes.mjs b/packages/plugin-sdk/cli/lib/css-classes.mjs index 5296962fc..04b40ab9d 100644 --- a/packages/plugin-sdk/cli/lib/css-classes.mjs +++ b/packages/plugin-sdk/cli/lib/css-classes.mjs @@ -20,16 +20,18 @@ export function classNamesInCss(css) { /** * Splits a plugin's candidates by whether core's own CSS already has them. - * A class core ships keeps the plugin's copy below core's utilities, so it can - * never reorder core's rules. Any other class has no rule in core to fight, - * so it goes in core's utilities layer and overrides the way it would in one - * Tailwind build. + * A plain class core ships keeps the plugin's copy below core's utilities, so + * it can never reorder core's rules. Everything else goes in core's utilities + * layer: plugin-only classes, and every variant (md:, hover:, ...) even when + * core ships it, so `max-h-56 md:max-h-none` is ordered in one build and a + * variant still beats core's own base class. */ export function splitByCoreClasses(candidates, coreClasses) { const shared = []; const own = []; for (const candidate of candidates) { - (coreClasses.has(candidate) ? shared : own).push(candidate); + const plain = !candidate.includes(":"); + (plain && coreClasses.has(candidate) ? shared : own).push(candidate); } return { shared, own }; } diff --git a/scripts/plugin-build.test.ts b/scripts/plugin-build.test.ts index 0d2b125c5..e4b1caa96 100644 --- a/scripts/plugin-build.test.ts +++ b/scripts/plugin-build.test.ts @@ -142,6 +142,18 @@ describe("plugin CSS layers", () => { ).toEqual({ shared: ["w-28", "hidden"], own: ["md:w-44"] }); }); + it("keeps variants core ships with the plugin's own classes", () => { + expect( + splitByCoreClasses( + ["max-h-56", "md:max-h-none", "flex-col", "md:flex-row"], + new Set(["md:max-h-none", "flex-col", "md:flex-row"]), + ), + ).toEqual({ + shared: ["flex-col"], + own: ["max-h-56", "md:max-h-none", "md:flex-row"], + }); + }); + it("keeps classes core ships below core and the plugin's own beside core's", async () => { vi.spyOn(console, "log").mockImplementation(() => {}); // Core's class list comes from scanning this repo, this file included,