feat: squircle-none/-full utilities and per-corner shape composition - #37
Merged
Conversation
dogmar
marked this pull request as ready for review
September 1, 2026 02:09
Defined the same way Tailwind defines rounded-none and rounded-full: static utilities with 0 and calc(infinity * 1px) registered alongside each variant root, for both the generated CSS and the JS plugin.
Correcting zero or an infinite radius is a no-op (the factor is finite and positive, and the browser clamps either way), so the static -none/-full utilities keep the exact rounded-none/rounded-full values in both branches. squircle-full only adds corner-shape under @supports; squircle-none is identical to rounded-none.
dogmar
force-pushed
the
claude/squircle-licensing-links-8j6ecz
branch
from
September 1, 2026 05:44
80d1f8e to
d59b57d
Compare
A vp check --fix run from the package directory had reformatted the generated dist/tailwind/utils.css (breaking calc(infinity * 1px) and --value(--radius-*) tokens), and the README sync copied the mangled output. Regenerated the CSS and re-synced.
Splits the single catch-all squircle class group into per-variant groups with hierarchical conflicts matching tailwind-merge's own rounded config: a later all-corners utility cancels earlier side/corner utilities (in either family), a side cancels its two corners, and a narrower utility refines a broader one instead of canceling it. Also fixes squircle-amt being canceled by radius classes — it controls corner shape, not radius, so it is now orthogonal. Adds a test suite covering the merge semantics.
Keeping both classes only refines a corner if the narrower utility also wins the cascade, which comes down to emission order at equal specificity. Pins that down for every non-conflicting pair, on both the CSS-import and plugin paths, so a change in utility registration order fails loudly.
The test:tailwind filter matches tailwind-merge.test.ts as well, and its cascade-order cases compile the generated utils.css, so the task needs the same build dependency test:css and test:radius already declare.
…t them Side and corner squircle utilities set the corner-shape shorthand, which reshaped all four corners even though they only set a radius on the ones they name — so squircle-t-lg rounded-b-lg rendered the bottom corners as squircles at an uncorrected radius. They now emit the matching per-corner longhands (corner-top-left-shape and friends); the all-corners utility keeps the shorthand, since it does own all four. The converse did not work either: Tailwind's rounded-* utilities only set a radius, so they could never take a corner back from a squircle. This re-declares them to also reset their own corners to round. Tailwind still emits its own rule for the radius, and the reset is the initial value, so it is inert unless a squircle class is on the same element. Applies to the generated CSS, the JS plugin, and the StyleX preset.
…g corners The rounded-* resets only reached theme values and literal arbitrary lengths, so rounded-tl-(--my-radius) and rounded-tl-[var(--x)] silently kept their squircle shape. The plugin now registers the reset with type "any" and no radius of its own — its values map still bounds what matches — and the generated CSS widens --value() with [*]. It keeps declaring the radius there because without a --value() the utility would match any suffix at all, turning a typo like rounded-tl-garbage into a real class; the value it emits is the one core emits anyway. squircle-amt-* set the corner-shape shorthand, so it reshaped all four corners even next to a side variant, and it fought the new rounded-* resets depending on emission order. It now only sets the amount, which is exactly what writing --squircle-amt yourself already did; the amount still reaches each variant through the variable. Same change in the Panda preset.
The StyleX block and the table of contents were stale: scripts/sync-readme.sh calls bare `vp`, and under set -e a shell without it on PATH aborts the script after the CSS blocks but before the StyleX one, silently (its stderr is discarded). CI runs with vp on PATH and caught the difference.
Matches the validation the squircle-* utilities already document: theme keys and arbitrary lengths, not paren refs or non-length arbitraries. Both paths now accept exactly the same set, so a corner named with a paren ref keeps its squircle shape rather than resetting on one path only.
Contributor
|
🎉 This PR is included in version 0.10.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
squircle-noneandsquircle-full, and makes squircled and rounded corners compose per corner on the same element.squircle-none/squircle-fullAdds both (plus every side/corner variant like
squircle-t-full,squircle-tl-none), defined the way Tailwind definesrounded-noneandrounded-full: static utilities with0andcalc(infinity * 1px), registered alongside each variant root rather than as theme values. Tailwind v4 special-cases these inside therounded-*registration instead of shipping--radius-none/--radius-fulltokens, which is whysquircle-fullpreviously matched nothing on the CSS-import path.Neither applies the superellipse radius correction — correcting zero or an infinite radius is a no-op, since the factor is finite and positive and the browser clamps either way.
squircle-fullisrounded-full's value plus the superellipse shape;squircle-noneis identical torounded-none.The JS plugin also drops the v3-compat theme's
none/fullkeys from its functional values, so each utility is emitted once with Tailwind's current values rather than v3's0px/9999px.Per-corner shape composition
Corner shape was applied as the four-corner
corner-shapeshorthand, which broke mixing in both directions. Verified against Chrome 141 by reading computed styles and sampling rendered pixels:squircle-t-lg rounded-b-lgrendered the bottom corners as squircles at an uncorrected radius. They now emit the matching per-corner longhands (corner-top-left-shapeand friends). The all-corners utility keeps the shorthand, since it does own all four.rounded-*utilities only set a radius, so they could never reclaim a corner from a squircle. This re-declares them to also reset their own corners toround. Tailwind still emits its own rule for the radius, and the reset is the initial value, so it is inert unless a squircle class is on the same element. The reset accepts the same valuessquircle-*does — theme keys and arbitrary lengths — so a corner named with a paren ref or a non-length arbitrary keeps its squircle shape, consistent with the validation this package already documents.squircle-amt-*set the shorthand too, so it reshaped all four corners next to a side variant and fought the resets depending on emission order. It now only sets the amount, which is exactly what writing--squircle-amtyourself already did; the amount still reaches each variant through the variable.Together, these compose as you'd expect:
squircle-lg rounded-tl-lgsquircle-t-lg rounded-b-lgA corner reclaimed by
rounded-*uses the plain radius, not the corrected one — the correction exists only to make a squircle look the same size as a rounded corner. Applies to the generated CSS, the JS plugin, and the StyleX and Panda presets.tailwind-merge config
The old config put every squircle variant in one class group, so any squircle utility canceled any other, and radius classes canceled
squircle-amt-*. It now mirrors tailwind-merge's ownroundedhierarchy with per-variant groups: an all-corners utility cancels earlier side/corner utilities in either family, a side cancels its two corners, a narrower utility refines a broader one instead of canceling it, each squircle group conflicts with itsroundedcounterpart, andsquircle-amt-*is orthogonal.Keeping both classes only refines a corner if the narrower one also wins the cascade, so that is pinned down too: Tailwind sorts radius utilities by property breadth (shorthand → two-corner sides → single corners) and interleaves
squircle-*withrounded-*in each tier, so the narrower utility lands later regardless of family.Testing
289 tests pass, covering
-none/-fullon both paths, per-corner shape emission and the absence of bleed, therounded-*resets across every value form (which match, which deliberately do not, and that values Tailwind itself rejects stay unmatched), reset-after-squircle ordering,squircle-amtapplying no shape, the tailwind-merge hierarchy against the real package, and cascade order. README copy-paste blocks re-synced and verified idempotent.Generated by Claude Code