Skip to content

feat: squircle-none/-full utilities and per-corner shape composition - #37

Merged
dogmar merged 10 commits into
mainfrom
claude/squircle-licensing-links-8j6ecz
Sep 1, 2026
Merged

feat: squircle-none/-full utilities and per-corner shape composition#37
dogmar merged 10 commits into
mainfrom
claude/squircle-licensing-links-8j6ecz

Conversation

@dogmar

@dogmar dogmar commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds squircle-none and squircle-full, and makes squircled and rounded corners compose per corner on the same element.

squircle-none / squircle-full

Adds both (plus every side/corner variant like squircle-t-full, squircle-tl-none), defined the way Tailwind defines rounded-none and rounded-full: static utilities with 0 and calc(infinity * 1px), registered alongside each variant root rather than as theme values. Tailwind v4 special-cases these inside the rounded-* registration instead of shipping --radius-none/--radius-full tokens, which is why squircle-full previously 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-full is rounded-full's value plus the superellipse shape; squircle-none is identical to rounded-none.

The JS plugin also drops the v3-compat theme's none/full keys from its functional values, so each utility is emitted once with Tailwind's current values rather than v3's 0px/9999px.

Per-corner shape composition

Corner shape was applied as the four-corner corner-shape shorthand, which broke mixing in both directions. Verified against Chrome 141 by reading computed styles and sampling rendered pixels:

  1. Shape bleed. Side and corner utilities reshaped all four corners even though they only set a radius on the ones they name — 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.
  2. No way back. Tailwind's 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 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. The reset accepts the same values squircle-* 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.
  3. 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-amt yourself already did; the amount still reaches each variant through the variable.

Together, these compose as you'd expect:

classes result
squircle-lg rounded-tl-lg round top-left, squircle on the other three
squircle-t-lg rounded-b-lg squircled top corners, ordinary rounded bottom corners

A 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 own rounded hierarchy 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 its rounded counterpart, and squircle-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-* with rounded-* in each tier, so the narrower utility lands later regardless of family.

Testing

289 tests pass, covering -none/-full on both paths, per-corner shape emission and the absence of bleed, the rounded-* resets across every value form (which match, which deliberately do not, and that values Tailwind itself rejects stay unmatched), reset-after-squircle ordering, squircle-amt applying 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

@dogmar
dogmar marked this pull request as ready for review September 1, 2026 02:09
@dogmar dogmar changed the title feat: add squircle-full variants for perfect circles feat: resolve squircle-full from the theme's full radius Sep 1, 2026
@dogmar dogmar changed the title feat: resolve squircle-full from the theme's full radius feat: add squircle-none and squircle-full, defined like rounded-none/rounded-full Sep 1, 2026
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
dogmar force-pushed the claude/squircle-licensing-links-8j6ecz branch from 80d1f8e to d59b57d Compare September 1, 2026 05:44
@dogmar dogmar changed the title feat: add squircle-none and squircle-full, defined like rounded-none/rounded-full feat: add squircle-none and squircle-full utilities Sep 1, 2026
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.
@dogmar dogmar changed the title feat: add squircle-none and squircle-full utilities feat: squircle-none/-full utilities and per-corner shape composition Sep 1, 2026
…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.
@dogmar
dogmar merged commit 10f4130 into main Sep 1, 2026
6 checks passed
@dogmar
dogmar deleted the claude/squircle-licensing-links-8j6ecz branch September 1, 2026 06:44
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 0.10.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants