fix(css): scope utilities under the view root so tailwind plugins stop outranking each other
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { scopeCss } from "../scripts/scope-css.mjs";
|
||||
|
||||
const one = (css: string) => scopeCss(css).replace(/\s+/gu, " ").trim();
|
||||
|
||||
describe("scoping the built stylesheet", () => {
|
||||
it("prefixes a utility with the view root, keeping the root itself in reach", () => {
|
||||
expect(one(".flex{display:flex!important}")).toBe(
|
||||
":is(.beaver-root, .beaver-root *).flex { display: flex !important; }"
|
||||
);
|
||||
});
|
||||
|
||||
it("outranks the same utility from another plugin's Tailwind", () => {
|
||||
// Class specificity: theirs is (0,1,0); ours must be (0,2,0).
|
||||
const out = one(".hidden{display:none!important}");
|
||||
expect(out.startsWith(":is(.beaver-root, .beaver-root *).hidden")).toBe(
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps pseudo-elements at the end where they are valid", () => {
|
||||
expect(one(".x::placeholder{color:red}")).toBe(
|
||||
":is(.beaver-root, .beaver-root *).x::placeholder { color: red; }"
|
||||
);
|
||||
expect(one(".y::first-letter{t:1}")).toContain(".y:first-letter");
|
||||
});
|
||||
|
||||
it("scopes rules nested in container queries and layers", () => {
|
||||
const out = one(
|
||||
"@layer utilities{@container (min-width:30rem){.a{display:inline-block!important}}}"
|
||||
);
|
||||
// lightningcss rewrites the query into range syntax; Obsidian's Chromium
|
||||
// has understood that inside @container since container queries landed.
|
||||
expect(out).toContain("@container (width >= 30rem)");
|
||||
expect(out).toContain(":is(.beaver-root, .beaver-root *).a");
|
||||
});
|
||||
|
||||
it("drops a leading universal selector instead of producing `:is()*`", () => {
|
||||
const out = one("*,:before,::backdrop{--tw-a:initial}");
|
||||
expect(out).toContain(":is(.beaver-root, .beaver-root *),");
|
||||
expect(out).toContain(":is(.beaver-root, .beaver-root *):before");
|
||||
expect(out).not.toContain("*)*");
|
||||
});
|
||||
|
||||
it("moves theme variables from :root onto the view root", () => {
|
||||
expect(one(":root,:host{--spacing:.25rem}")).toBe(
|
||||
".beaver-root { --spacing: .25rem; }"
|
||||
);
|
||||
});
|
||||
|
||||
it("leaves rules that already name the root alone", () => {
|
||||
const reset = ".beaver-root.beaver-root button{cursor:pointer}";
|
||||
expect(one(reset)).toBe(
|
||||
".beaver-root.beaver-root button { cursor: pointer; }"
|
||||
);
|
||||
expect(one(".theme-dark .beaver-root{--note:red}")).toBe(
|
||||
".theme-dark .beaver-root { --note: red; }"
|
||||
);
|
||||
});
|
||||
|
||||
it("scopes variant selectors without breaking their inner :where()", () => {
|
||||
const out = one(".dark\\:bg-x:where(.theme-dark,.theme-dark *){b:1}");
|
||||
expect(out).toBe(
|
||||
":is(.beaver-root, .beaver-root *).dark\\:bg-x:where(.theme-dark, .theme-dark *) { b: 1; }"
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user