@klinking/squircle 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,47 @@
1
+ //#region src/tw-merge-cfg.d.ts
2
+ declare const squircleMergeConfig: {
3
+ readonly extend: {
4
+ readonly classGroups: {
5
+ readonly squircle: readonly [{
6
+ readonly squircle: readonly [() => boolean];
7
+ }, {
8
+ readonly "squircle-t": readonly [() => boolean];
9
+ }, {
10
+ readonly "squircle-r": readonly [() => boolean];
11
+ }, {
12
+ readonly "squircle-b": readonly [() => boolean];
13
+ }, {
14
+ readonly "squircle-l": readonly [() => boolean];
15
+ }, {
16
+ readonly "squircle-s": readonly [() => boolean];
17
+ }, {
18
+ readonly "squircle-e": readonly [() => boolean];
19
+ }, {
20
+ readonly "squircle-tl": readonly [() => boolean];
21
+ }, {
22
+ readonly "squircle-tr": readonly [() => boolean];
23
+ }, {
24
+ readonly "squircle-br": readonly [() => boolean];
25
+ }, {
26
+ readonly "squircle-bl": readonly [() => boolean];
27
+ }, {
28
+ readonly "squircle-ss": readonly [() => boolean];
29
+ }, {
30
+ readonly "squircle-se": readonly [() => boolean];
31
+ }, {
32
+ readonly "squircle-es": readonly [() => boolean];
33
+ }, {
34
+ readonly "squircle-ee": readonly [() => boolean];
35
+ }];
36
+ readonly "squircle-amt": readonly [{
37
+ readonly "squircle-amt": readonly [() => boolean];
38
+ }];
39
+ };
40
+ readonly conflictingClassGroups: {
41
+ readonly squircle: readonly [...string[], "squircle-amt"];
42
+ };
43
+ };
44
+ };
45
+ //#endregion
46
+ export { squircleMergeConfig };
47
+ //# sourceMappingURL=tw-merge-cfg.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tw-merge-cfg.d.mts","names":[],"sources":["../src/tw-merge-cfg.ts"],"mappings":";cAkBa,mBAAA;EAAA"}
@@ -0,0 +1,48 @@
1
+ //#region src/tw-merge-cfg.ts
2
+ const allRoundedGroups = [
3
+ "rounded",
4
+ "rounded-s",
5
+ "rounded-e",
6
+ "rounded-t",
7
+ "rounded-r",
8
+ "rounded-b",
9
+ "rounded-l",
10
+ "rounded-ss",
11
+ "rounded-se",
12
+ "rounded-es",
13
+ "rounded-ee",
14
+ "rounded-tl",
15
+ "rounded-tr",
16
+ "rounded-br",
17
+ "rounded-bl"
18
+ ];
19
+ const squircleMergeConfig = { extend: {
20
+ classGroups: {
21
+ squircle: [
22
+ { squircle: [() => true] },
23
+ { "squircle-t": [() => true] },
24
+ { "squircle-r": [() => true] },
25
+ { "squircle-b": [() => true] },
26
+ { "squircle-l": [() => true] },
27
+ { "squircle-s": [() => true] },
28
+ { "squircle-e": [() => true] },
29
+ { "squircle-tl": [() => true] },
30
+ { "squircle-tr": [() => true] },
31
+ { "squircle-br": [() => true] },
32
+ { "squircle-bl": [() => true] },
33
+ { "squircle-ss": [() => true] },
34
+ { "squircle-se": [() => true] },
35
+ { "squircle-es": [() => true] },
36
+ { "squircle-ee": [() => true] }
37
+ ],
38
+ "squircle-amt": [{ "squircle-amt": [() => true] }]
39
+ },
40
+ conflictingClassGroups: {
41
+ squircle: [...allRoundedGroups, "squircle-amt"],
42
+ ...Object.fromEntries(allRoundedGroups.map((g) => [g, ["squircle", "squircle-amt"]]))
43
+ }
44
+ } };
45
+ //#endregion
46
+ export { squircleMergeConfig };
47
+
48
+ //# sourceMappingURL=tw-merge-cfg.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tw-merge-cfg.mjs","names":[],"sources":["../src/tw-merge-cfg.ts"],"sourcesContent":["const allRoundedGroups: string[] = [\n \"rounded\",\n \"rounded-s\",\n \"rounded-e\",\n \"rounded-t\",\n \"rounded-r\",\n \"rounded-b\",\n \"rounded-l\",\n \"rounded-ss\",\n \"rounded-se\",\n \"rounded-es\",\n \"rounded-ee\",\n \"rounded-tl\",\n \"rounded-tr\",\n \"rounded-br\",\n \"rounded-bl\",\n];\n\nexport const squircleMergeConfig = {\n extend: {\n classGroups: {\n squircle: [\n { squircle: [() => true] },\n { \"squircle-t\": [() => true] },\n { \"squircle-r\": [() => true] },\n { \"squircle-b\": [() => true] },\n { \"squircle-l\": [() => true] },\n { \"squircle-s\": [() => true] },\n { \"squircle-e\": [() => true] },\n { \"squircle-tl\": [() => true] },\n { \"squircle-tr\": [() => true] },\n { \"squircle-br\": [() => true] },\n { \"squircle-bl\": [() => true] },\n { \"squircle-ss\": [() => true] },\n { \"squircle-se\": [() => true] },\n { \"squircle-es\": [() => true] },\n { \"squircle-ee\": [() => true] },\n ],\n \"squircle-amt\": [{ \"squircle-amt\": [() => true] }],\n },\n conflictingClassGroups: {\n squircle: [...allRoundedGroups, \"squircle-amt\"],\n ...Object.fromEntries(allRoundedGroups.map((g) => [g, [\"squircle\", \"squircle-amt\"]])),\n },\n },\n} as const;\n"],"mappings":";AAAA,MAAM,mBAA6B;CACjC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAED,MAAa,sBAAsB,EACjC,QAAQ;CACN,aAAa;EACX,UAAU;GACR,EAAE,UAAU,OAAO,KAAK,EAAE;GAC1B,EAAE,cAAc,OAAO,KAAK,EAAE;GAC9B,EAAE,cAAc,OAAO,KAAK,EAAE;GAC9B,EAAE,cAAc,OAAO,KAAK,EAAE;GAC9B,EAAE,cAAc,OAAO,KAAK,EAAE;GAC9B,EAAE,cAAc,OAAO,KAAK,EAAE;GAC9B,EAAE,cAAc,OAAO,KAAK,EAAE;GAC9B,EAAE,eAAe,OAAO,KAAK,EAAE;GAC/B,EAAE,eAAe,OAAO,KAAK,EAAE;GAC/B,EAAE,eAAe,OAAO,KAAK,EAAE;GAC/B,EAAE,eAAe,OAAO,KAAK,EAAE;GAC/B,EAAE,eAAe,OAAO,KAAK,EAAE;GAC/B,EAAE,eAAe,OAAO,KAAK,EAAE;GAC/B,EAAE,eAAe,OAAO,KAAK,EAAE;GAC/B,EAAE,eAAe,OAAO,KAAK,EAAE;GAChC;EACD,gBAAgB,CAAC,EAAE,gBAAgB,OAAO,KAAK,EAAE,CAAC;EACnD;CACD,wBAAwB;EACtB,UAAU,CAAC,GAAG,kBAAkB,eAAe;EAC/C,GAAG,OAAO,YAAY,iBAAiB,KAAK,MAAM,CAAC,GAAG,CAAC,YAAY,eAAe,CAAC,CAAC,CAAC;EACtF;CACF,EACF"}
@@ -0,0 +1,15 @@
1
+ import plugin from "tailwindcss/plugin";
2
+
3
+ //#region src/tw-plugin.d.ts
4
+ interface SquirclePluginOptions {
5
+ /** CSS custom property name for the superellipse amount (default: "--squircle-amt") */
6
+ amtVar?: string;
7
+ /** @plugin CSS alias for amtVar */
8
+ "amt-var"?: string;
9
+ /** Class name prefix for utilities (default: "squircle") */
10
+ prefix?: string;
11
+ }
12
+ declare const squircle: ReturnType<typeof plugin.withOptions<SquirclePluginOptions>>;
13
+ //#endregion
14
+ export { SquirclePluginOptions, squircle as default };
15
+ //# sourceMappingURL=tw-plugin.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tw-plugin.d.mts","names":[],"sources":["../src/tw-plugin.ts"],"mappings":";;;UASiB,qBAAA;;EAEf,MAAA;EAFoC;EAIpC,SAAA;EAJoC;EAMpC,MAAA;AAAA;AAAA,cAGI,QAAA,EAAU,UAAA,QAAkB,MAAA,CAAO,WAAA,CAAY,qBAAA"}
@@ -0,0 +1,85 @@
1
+ import plugin from "tailwindcss/plugin";
2
+ const DEFAULT_AMOUNT_VAR_NAME = "--squircle-amt";
3
+ const DEFAULT_AMT_CSS = `var(${DEFAULT_AMOUNT_VAR_NAME}, 2)`;
4
+ const getCornerShape = (varName = DEFAULT_AMOUNT_VAR_NAME) => `superellipse(var(${varName}, 2))`;
5
+ function correctedRadius(radius, amt = DEFAULT_AMT_CSS) {
6
+ return `calc(${radius} * (1 - pow(2, -0.5)) / (1 - pow(2, -1 * pow(2, -1 * ${amt}))))`;
7
+ }
8
+ function isComment(entry) {
9
+ return !Array.isArray(entry);
10
+ }
11
+ const VARIANTS = {
12
+ "": ["border-radius"],
13
+ "$comment-physical-sides": { comment: "/* --- Per-side physical variants --- */" },
14
+ t: ["border-top-left-radius", "border-top-right-radius"],
15
+ r: ["border-top-right-radius", "border-bottom-right-radius"],
16
+ b: ["border-bottom-left-radius", "border-bottom-right-radius"],
17
+ l: ["border-top-left-radius", "border-bottom-left-radius"],
18
+ "$comment-logical-sides": { comment: "/* --- Per-side logical variants --- */" },
19
+ s: ["border-start-start-radius", "border-end-start-radius"],
20
+ e: ["border-start-end-radius", "border-end-end-radius"],
21
+ "$comment-physical-corners": { comment: "/* --- Per-corner physical variants --- */" },
22
+ tl: ["border-top-left-radius"],
23
+ tr: ["border-top-right-radius"],
24
+ br: ["border-bottom-right-radius"],
25
+ bl: ["border-bottom-left-radius"],
26
+ "$comment-logical-corners": { comment: "/* --- Per-corner logical variants --- */" },
27
+ ss: ["border-start-start-radius"],
28
+ se: ["border-start-end-radius"],
29
+ es: ["border-end-start-radius"],
30
+ ee: ["border-end-end-radius"]
31
+ };
32
+ function variantEntries() {
33
+ return Object.entries(VARIANTS).filter((entry) => !isComment(entry[1]));
34
+ }
35
+ function usesIntermediateVar(suffix) {
36
+ const entry = VARIANTS[suffix];
37
+ if (!entry || isComment(entry)) return false;
38
+ return suffix === "" || entry.length > 1;
39
+ }
40
+ //#endregion
41
+ //#region src/tw-plugin.ts
42
+ const squircle = plugin.withOptions((options = {}) => ({ matchUtilities, theme }) => {
43
+ const amtVar = options.amtVar ?? options["amt-var"] ?? "--squircle-amt";
44
+ const prefix = options.prefix ?? "squircle";
45
+ const radiusValues = theme("borderRadius");
46
+ const amtCss = `var(${amtVar}, 2)`;
47
+ const cornerShape = getCornerShape(amtVar);
48
+ const supportsCornerShape = "@supports (corner-shape: superellipse())";
49
+ matchUtilities({ [`${prefix}-amt`]: (value) => ({
50
+ [amtVar]: value,
51
+ [supportsCornerShape]: { "corner-shape": `superellipse(var(${amtVar}))` }
52
+ }) }, { type: "number" });
53
+ for (const [suffix, props] of variantEntries()) {
54
+ const name = suffix ? `${prefix}-${suffix}` : prefix;
55
+ if (usesIntermediateVar(suffix)) matchUtilities({ [name]: (value) => ({
56
+ ...Object.fromEntries(props.map((p) => [p, value])),
57
+ [supportsCornerShape]: {
58
+ "--squircle-r": correctedRadius(value, amtCss),
59
+ ...Object.fromEntries(props.map((p) => [p, "var(--squircle-r)"])),
60
+ "corner-shape": cornerShape
61
+ }
62
+ }) }, {
63
+ type: "length",
64
+ values: radiusValues
65
+ });
66
+ else {
67
+ const prop = props[0];
68
+ matchUtilities({ [name]: (value) => {
69
+ const result = { [prop]: value };
70
+ result[supportsCornerShape] = {
71
+ [prop]: correctedRadius(value, amtCss),
72
+ "corner-shape": cornerShape
73
+ };
74
+ return result;
75
+ } }, {
76
+ type: "length",
77
+ values: radiusValues
78
+ });
79
+ }
80
+ }
81
+ });
82
+ //#endregion
83
+ export { squircle as default };
84
+
85
+ //# sourceMappingURL=tw-plugin.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tw-plugin.mjs","names":[],"sources":["../src/variants.ts","../src/tw-plugin.ts"],"sourcesContent":["export const DEFAULT_AMT = 2 as const;\nexport const DEFAULT_AMOUNT_VAR_NAME = \"--squircle-amt\" as const;\nexport const DEFAULT_AMT_CSS = `var(${DEFAULT_AMOUNT_VAR_NAME}, ${DEFAULT_AMT})` as const;\n\nexport const getCornerShape = (varName: string = DEFAULT_AMOUNT_VAR_NAME) =>\n `superellipse(var(${varName}, ${DEFAULT_AMT}))` as const;\n\nexport function correctedRadius(radius: string, amt: string = DEFAULT_AMT_CSS): string {\n return `calc(${radius} * (1 - pow(2, -0.5)) / (1 - pow(2, -1 * pow(2, -1 * ${amt}))))` as const;\n}\n\ntype SectionComment = { comment: string };\ntype VariantEntry = string[] | SectionComment;\n\nfunction isComment(entry: VariantEntry): entry is SectionComment {\n return !Array.isArray(entry);\n}\n\nexport const VARIANTS: Record<string, VariantEntry> & Record<`$comment-${string}`, SectionComment> =\n {\n \"\": [\"border-radius\"],\n \"$comment-physical-sides\": { comment: \"/* --- Per-side physical variants --- */\" },\n t: [\"border-top-left-radius\", \"border-top-right-radius\"],\n r: [\"border-top-right-radius\", \"border-bottom-right-radius\"],\n b: [\"border-bottom-left-radius\", \"border-bottom-right-radius\"],\n l: [\"border-top-left-radius\", \"border-bottom-left-radius\"],\n \"$comment-logical-sides\": { comment: \"/* --- Per-side logical variants --- */\" },\n s: [\"border-start-start-radius\", \"border-end-start-radius\"],\n e: [\"border-start-end-radius\", \"border-end-end-radius\"],\n \"$comment-physical-corners\": { comment: \"/* --- Per-corner physical variants --- */\" },\n tl: [\"border-top-left-radius\"],\n tr: [\"border-top-right-radius\"],\n br: [\"border-bottom-right-radius\"],\n bl: [\"border-bottom-left-radius\"],\n \"$comment-logical-corners\": { comment: \"/* --- Per-corner logical variants --- */\" },\n ss: [\"border-start-start-radius\"],\n se: [\"border-start-end-radius\"],\n es: [\"border-end-start-radius\"],\n ee: [\"border-end-end-radius\"],\n };\n\nexport function variantEntries(): [string, string[]][] {\n return Object.entries(VARIANTS).filter(\n (entry): entry is [string, string[]] => !isComment(entry[1]),\n );\n}\n\nexport function usesIntermediateVar(suffix: string): boolean {\n const entry = VARIANTS[suffix];\n if (!entry || isComment(entry)) return false;\n return suffix === \"\" || entry.length > 1;\n}\n\nexport { isComment };\nexport type { SectionComment, VariantEntry };\n","import plugin from \"tailwindcss/plugin\";\nimport {\n DEFAULT_AMOUNT_VAR_NAME,\n correctedRadius,\n getCornerShape,\n usesIntermediateVar,\n variantEntries,\n} from \"./variants\";\n\nexport interface SquirclePluginOptions {\n /** CSS custom property name for the superellipse amount (default: \"--squircle-amt\") */\n amtVar?: string;\n /** @plugin CSS alias for amtVar */\n \"amt-var\"?: string;\n /** Class name prefix for utilities (default: \"squircle\") */\n prefix?: string;\n}\n\nconst squircle: ReturnType<typeof plugin.withOptions<SquirclePluginOptions>> =\n plugin.withOptions<SquirclePluginOptions>((options = {}) =>\n // eslint-disable-next-line @typescript-eslint/unbound-method\n ({ matchUtilities, theme }) => {\n const amtVar = options.amtVar ?? options[\"amt-var\"] ?? DEFAULT_AMOUNT_VAR_NAME;\n const prefix = options.prefix ?? \"squircle\";\n const radiusValues = theme(\"borderRadius\");\n\n const amtCss = `var(${amtVar}, 2)`;\n const cornerShape = getCornerShape(amtVar);\n const supportsCornerShape: string = \"@supports (corner-shape: superellipse())\";\n\n matchUtilities(\n {\n [`${prefix}-amt`]: (value: string) => ({\n [amtVar]: value,\n [supportsCornerShape]: {\n \"corner-shape\": `superellipse(var(${amtVar}))`,\n },\n }),\n },\n { type: \"number\" },\n );\n\n for (const [suffix, props] of variantEntries()) {\n const name = suffix ? `${prefix}-${suffix}` : prefix;\n\n if (usesIntermediateVar(suffix)) {\n matchUtilities(\n {\n [name]: (value: string) => ({\n ...Object.fromEntries(props.map((p) => [p, value])),\n [supportsCornerShape]: {\n \"--squircle-r\": correctedRadius(value, amtCss),\n ...Object.fromEntries(props.map((p) => [p, \"var(--squircle-r)\"])),\n \"corner-shape\": cornerShape,\n },\n }),\n },\n { type: \"length\", values: radiusValues },\n );\n } else {\n const prop = props[0]!;\n matchUtilities(\n {\n [name]: (value: string) => {\n const result: Record<string, string | Record<string, string>> = {\n [prop]: value,\n };\n result[supportsCornerShape] = {\n [prop]: correctedRadius(value, amtCss),\n \"corner-shape\": cornerShape,\n };\n return result;\n },\n },\n { type: \"length\", values: radiusValues },\n );\n }\n }\n });\n\nexport default squircle;\n"],"mappings":";AACA,MAAa,0BAA0B;AACvC,MAAa,kBAAkB,OAAO,wBAAwB;AAE9D,MAAa,kBAAkB,UAAkB,4BAC/C,oBAAoB,QAAQ;AAE9B,SAAgB,gBAAgB,QAAgB,MAAc,iBAAyB;AACrF,QAAO,QAAQ,OAAO,uDAAuD,IAAI;;AAMnF,SAAS,UAAU,OAA8C;AAC/D,QAAO,CAAC,MAAM,QAAQ,MAAM;;AAG9B,MAAa,WACX;CACE,IAAI,CAAC,gBAAgB;CACrB,2BAA2B,EAAE,SAAS,4CAA4C;CAClF,GAAG,CAAC,0BAA0B,0BAA0B;CACxD,GAAG,CAAC,2BAA2B,6BAA6B;CAC5D,GAAG,CAAC,6BAA6B,6BAA6B;CAC9D,GAAG,CAAC,0BAA0B,4BAA4B;CAC1D,0BAA0B,EAAE,SAAS,2CAA2C;CAChF,GAAG,CAAC,6BAA6B,0BAA0B;CAC3D,GAAG,CAAC,2BAA2B,wBAAwB;CACvD,6BAA6B,EAAE,SAAS,8CAA8C;CACtF,IAAI,CAAC,yBAAyB;CAC9B,IAAI,CAAC,0BAA0B;CAC/B,IAAI,CAAC,6BAA6B;CAClC,IAAI,CAAC,4BAA4B;CACjC,4BAA4B,EAAE,SAAS,6CAA6C;CACpF,IAAI,CAAC,4BAA4B;CACjC,IAAI,CAAC,0BAA0B;CAC/B,IAAI,CAAC,0BAA0B;CAC/B,IAAI,CAAC,wBAAwB;CAC9B;AAEH,SAAgB,iBAAuC;AACrD,QAAO,OAAO,QAAQ,SAAS,CAAC,QAC7B,UAAuC,CAAC,UAAU,MAAM,GAAG,CAC7D;;AAGH,SAAgB,oBAAoB,QAAyB;CAC3D,MAAM,QAAQ,SAAS;AACvB,KAAI,CAAC,SAAS,UAAU,MAAM,CAAE,QAAO;AACvC,QAAO,WAAW,MAAM,MAAM,SAAS;;;;AChCzC,MAAM,WACJ,OAAO,aAAoC,UAAU,EAAE,MAEpD,EAAE,gBAAgB,YAAY;CAC/B,MAAM,SAAS,QAAQ,UAAU,QAAQ,cAAA;CACzC,MAAM,SAAS,QAAQ,UAAU;CACjC,MAAM,eAAe,MAAM,eAAe;CAE1C,MAAM,SAAS,OAAO,OAAO;CAC7B,MAAM,cAAc,eAAe,OAAO;CAC1C,MAAM,sBAA8B;AAEpC,gBACE,GACG,GAAG,OAAO,SAAS,WAAmB;GACpC,SAAS;GACT,sBAAsB,EACrB,gBAAgB,oBAAoB,OAAO,KAC5C;EACF,GACF,EACD,EAAE,MAAM,UAAU,CACnB;AAED,MAAK,MAAM,CAAC,QAAQ,UAAU,gBAAgB,EAAE;EAC9C,MAAM,OAAO,SAAS,GAAG,OAAO,GAAG,WAAW;AAE9C,MAAI,oBAAoB,OAAO,CAC7B,gBACE,GACG,QAAQ,WAAmB;GAC1B,GAAG,OAAO,YAAY,MAAM,KAAK,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC;IAClD,sBAAsB;IACrB,gBAAgB,gBAAgB,OAAO,OAAO;IAC9C,GAAG,OAAO,YAAY,MAAM,KAAK,MAAM,CAAC,GAAG,oBAAoB,CAAC,CAAC;IACjE,gBAAgB;IACjB;GACF,GACF,EACD;GAAE,MAAM;GAAU,QAAQ;GAAc,CACzC;OACI;GACL,MAAM,OAAO,MAAM;AACnB,kBACE,GACG,QAAQ,UAAkB;IACzB,MAAM,SAA0D,GAC7D,OAAO,OACT;AACD,WAAO,uBAAuB;MAC3B,OAAO,gBAAgB,OAAO,OAAO;KACtC,gBAAgB;KACjB;AACD,WAAO;MAEV,EACD;IAAE,MAAM;IAAU,QAAQ;IAAc,CACzC;;;EAGL"}
@@ -0,0 +1,207 @@
1
+ /* THIS FILE IS GENERATED — do not edit by hand.
2
+ * Source: scripts/generate-squircle-css.ts
3
+ * Formula: src/variants.ts
4
+ * Run: vp run generate:css */
5
+
6
+ /* ── Squircle utilities ─────────────────────────────────────── */
7
+ /* squircle-amt-[n] sets the superellipse amount (default 2) */
8
+ /* squircle-* mirrors rounded-* variants: all, t, r, b, l, s, e, tl, tr, br, bl, ss, se, es, ee */
9
+
10
+ @utility squircle-amt-* {
11
+ --squircle-amt: --value(--squircle-amt-*, number);
12
+ @supports (corner-shape: superellipse()) {
13
+ corner-shape: superellipse(var(--squircle-amt));
14
+ }
15
+ }
16
+
17
+ @utility squircle-* {
18
+ border-radius: --value(--radius-*);
19
+ @supports (corner-shape: superellipse()) {
20
+ --squircle-r: calc(
21
+ --value(--radius- *) * (1 - pow(2, -0.5)) /
22
+ (1 - pow(2, -1 * pow(2, -1 * var(--squircle-amt, 2))))
23
+ );
24
+ border-radius: var(--squircle-r);
25
+ corner-shape: superellipse(var(--squircle-amt, 2));
26
+ }
27
+ }
28
+
29
+ /* --- Per-side physical variants --- */
30
+
31
+ @utility squircle-t-* {
32
+ border-top-left-radius: --value(--radius-*);
33
+ border-top-right-radius: --value(--radius-*);
34
+ @supports (corner-shape: superellipse()) {
35
+ --squircle-r: calc(
36
+ --value(--radius- *) * (1 - pow(2, -0.5)) /
37
+ (1 - pow(2, -1 * pow(2, -1 * var(--squircle-amt, 2))))
38
+ );
39
+ border-top-left-radius: var(--squircle-r);
40
+ border-top-right-radius: var(--squircle-r);
41
+ corner-shape: superellipse(var(--squircle-amt, 2));
42
+ }
43
+ }
44
+
45
+ @utility squircle-r-* {
46
+ border-top-right-radius: --value(--radius-*);
47
+ border-bottom-right-radius: --value(--radius-*);
48
+ @supports (corner-shape: superellipse()) {
49
+ --squircle-r: calc(
50
+ --value(--radius- *) * (1 - pow(2, -0.5)) /
51
+ (1 - pow(2, -1 * pow(2, -1 * var(--squircle-amt, 2))))
52
+ );
53
+ border-top-right-radius: var(--squircle-r);
54
+ border-bottom-right-radius: var(--squircle-r);
55
+ corner-shape: superellipse(var(--squircle-amt, 2));
56
+ }
57
+ }
58
+
59
+ @utility squircle-b-* {
60
+ border-bottom-left-radius: --value(--radius-*);
61
+ border-bottom-right-radius: --value(--radius-*);
62
+ @supports (corner-shape: superellipse()) {
63
+ --squircle-r: calc(
64
+ --value(--radius- *) * (1 - pow(2, -0.5)) /
65
+ (1 - pow(2, -1 * pow(2, -1 * var(--squircle-amt, 2))))
66
+ );
67
+ border-bottom-left-radius: var(--squircle-r);
68
+ border-bottom-right-radius: var(--squircle-r);
69
+ corner-shape: superellipse(var(--squircle-amt, 2));
70
+ }
71
+ }
72
+
73
+ @utility squircle-l-* {
74
+ border-top-left-radius: --value(--radius-*);
75
+ border-bottom-left-radius: --value(--radius-*);
76
+ @supports (corner-shape: superellipse()) {
77
+ --squircle-r: calc(
78
+ --value(--radius- *) * (1 - pow(2, -0.5)) /
79
+ (1 - pow(2, -1 * pow(2, -1 * var(--squircle-amt, 2))))
80
+ );
81
+ border-top-left-radius: var(--squircle-r);
82
+ border-bottom-left-radius: var(--squircle-r);
83
+ corner-shape: superellipse(var(--squircle-amt, 2));
84
+ }
85
+ }
86
+
87
+ /* --- Per-side logical variants --- */
88
+
89
+ @utility squircle-s-* {
90
+ border-start-start-radius: --value(--radius-*);
91
+ border-end-start-radius: --value(--radius-*);
92
+ @supports (corner-shape: superellipse()) {
93
+ --squircle-r: calc(
94
+ --value(--radius- *) * (1 - pow(2, -0.5)) /
95
+ (1 - pow(2, -1 * pow(2, -1 * var(--squircle-amt, 2))))
96
+ );
97
+ border-start-start-radius: var(--squircle-r);
98
+ border-end-start-radius: var(--squircle-r);
99
+ corner-shape: superellipse(var(--squircle-amt, 2));
100
+ }
101
+ }
102
+
103
+ @utility squircle-e-* {
104
+ border-start-end-radius: --value(--radius-*);
105
+ border-end-end-radius: --value(--radius-*);
106
+ @supports (corner-shape: superellipse()) {
107
+ --squircle-r: calc(
108
+ --value(--radius- *) * (1 - pow(2, -0.5)) /
109
+ (1 - pow(2, -1 * pow(2, -1 * var(--squircle-amt, 2))))
110
+ );
111
+ border-start-end-radius: var(--squircle-r);
112
+ border-end-end-radius: var(--squircle-r);
113
+ corner-shape: superellipse(var(--squircle-amt, 2));
114
+ }
115
+ }
116
+
117
+ /* --- Per-corner physical variants --- */
118
+
119
+ @utility squircle-tl-* {
120
+ border-top-left-radius: --value(--radius-*);
121
+ @supports (corner-shape: superellipse()) {
122
+ border-top-left-radius: calc(
123
+ --value(--radius- *) * (1 - pow(2, -0.5)) /
124
+ (1 - pow(2, -1 * pow(2, -1 * var(--squircle-amt, 2))))
125
+ );
126
+ corner-shape: superellipse(var(--squircle-amt, 2));
127
+ }
128
+ }
129
+
130
+ @utility squircle-tr-* {
131
+ border-top-right-radius: --value(--radius-*);
132
+ @supports (corner-shape: superellipse()) {
133
+ border-top-right-radius: calc(
134
+ --value(--radius- *) * (1 - pow(2, -0.5)) /
135
+ (1 - pow(2, -1 * pow(2, -1 * var(--squircle-amt, 2))))
136
+ );
137
+ corner-shape: superellipse(var(--squircle-amt, 2));
138
+ }
139
+ }
140
+
141
+ @utility squircle-br-* {
142
+ border-bottom-right-radius: --value(--radius-*);
143
+ @supports (corner-shape: superellipse()) {
144
+ border-bottom-right-radius: calc(
145
+ --value(--radius- *) * (1 - pow(2, -0.5)) /
146
+ (1 - pow(2, -1 * pow(2, -1 * var(--squircle-amt, 2))))
147
+ );
148
+ corner-shape: superellipse(var(--squircle-amt, 2));
149
+ }
150
+ }
151
+
152
+ @utility squircle-bl-* {
153
+ border-bottom-left-radius: --value(--radius-*);
154
+ @supports (corner-shape: superellipse()) {
155
+ border-bottom-left-radius: calc(
156
+ --value(--radius- *) * (1 - pow(2, -0.5)) /
157
+ (1 - pow(2, -1 * pow(2, -1 * var(--squircle-amt, 2))))
158
+ );
159
+ corner-shape: superellipse(var(--squircle-amt, 2));
160
+ }
161
+ }
162
+
163
+ /* --- Per-corner logical variants --- */
164
+
165
+ @utility squircle-ss-* {
166
+ border-start-start-radius: --value(--radius-*);
167
+ @supports (corner-shape: superellipse()) {
168
+ border-start-start-radius: calc(
169
+ --value(--radius- *) * (1 - pow(2, -0.5)) /
170
+ (1 - pow(2, -1 * pow(2, -1 * var(--squircle-amt, 2))))
171
+ );
172
+ corner-shape: superellipse(var(--squircle-amt, 2));
173
+ }
174
+ }
175
+
176
+ @utility squircle-se-* {
177
+ border-start-end-radius: --value(--radius-*);
178
+ @supports (corner-shape: superellipse()) {
179
+ border-start-end-radius: calc(
180
+ --value(--radius- *) * (1 - pow(2, -0.5)) /
181
+ (1 - pow(2, -1 * pow(2, -1 * var(--squircle-amt, 2))))
182
+ );
183
+ corner-shape: superellipse(var(--squircle-amt, 2));
184
+ }
185
+ }
186
+
187
+ @utility squircle-es-* {
188
+ border-end-start-radius: --value(--radius-*);
189
+ @supports (corner-shape: superellipse()) {
190
+ border-end-start-radius: calc(
191
+ --value(--radius- *) * (1 - pow(2, -0.5)) /
192
+ (1 - pow(2, -1 * pow(2, -1 * var(--squircle-amt, 2))))
193
+ );
194
+ corner-shape: superellipse(var(--squircle-amt, 2));
195
+ }
196
+ }
197
+
198
+ @utility squircle-ee-* {
199
+ border-end-end-radius: --value(--radius-*);
200
+ @supports (corner-shape: superellipse()) {
201
+ border-end-end-radius: calc(
202
+ --value(--radius- *) * (1 - pow(2, -0.5)) /
203
+ (1 - pow(2, -1 * pow(2, -1 * var(--squircle-amt, 2))))
204
+ );
205
+ corner-shape: superellipse(var(--squircle-amt, 2));
206
+ }
207
+ }
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@klinking/squircle",
3
+ "version": "0.1.0",
4
+ "description": "Tailwind CSS v4 squircle (superellipse) corner utilities with visual radius correction",
5
+ "keywords": [
6
+ "border-radius",
7
+ "corner-shape",
8
+ "squircle",
9
+ "superellipse",
10
+ "tailwind-plugin",
11
+ "tailwindcss"
12
+ ],
13
+ "homepage": "https://dogmar.github.io/squircle",
14
+ "license": "MIT",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/dogmar/squircle.git"
18
+ },
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "type": "module",
23
+ "exports": {
24
+ "./tw-utils.css": "./dist/tw-utils.css",
25
+ "./squircle-radius.css": "./dist/squircle-radius.css",
26
+ "./tw-merge-cfg": {
27
+ "types": "./dist/tw-merge-cfg.d.mts",
28
+ "import": "./dist/tw-merge-cfg.mjs"
29
+ },
30
+ "./tw-plugin": {
31
+ "types": "./dist/tw-plugin.d.mts",
32
+ "import": "./dist/tw-plugin.mjs"
33
+ }
34
+ },
35
+ "scripts": {
36
+ "prepublishOnly": "vp run build"
37
+ },
38
+ "devDependencies": {
39
+ "@tailwindcss/vite": "^4.2.2",
40
+ "@types/node": "^25.5.2",
41
+ "oxlint": "^1.58.0",
42
+ "tsx": "^4.21.0",
43
+ "typescript": "^6.0.2",
44
+ "vite": "npm:@voidzero-dev/vite-plus-core@^0.1.15",
45
+ "vite-plus": "catalog:",
46
+ "vitest": "npm:@voidzero-dev/vite-plus-test@^0.1.15"
47
+ },
48
+ "peerDependencies": {
49
+ "tailwind-merge": ">=2.0.0",
50
+ "tailwindcss": ">=4.0.0"
51
+ },
52
+ "peerDependenciesMeta": {
53
+ "tailwind-merge": {
54
+ "optional": true
55
+ }
56
+ },
57
+ "packageManager": "pnpm@10.33.0"
58
+ }