diff --git a/.github/workflows/sync-readme.yml b/.github/workflows/sync-readme.yml
index 9dcb827..0ce8ab5 100644
--- a/.github/workflows/sync-readme.yml
+++ b/.github/workflows/sync-readme.yml
@@ -35,13 +35,33 @@ jobs:
run: vp run sync-readme
- name: Commit and push if changed
+ id: sync
run: |
- git diff --quiet README.md && exit 0
+ if git diff --quiet README.md; then
+ echo "pushed=false" >> "$GITHUB_OUTPUT"
+ exit 0
+ fi
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add README.md
git commit -m "docs: sync README code blocks"
- git push || {
+ if ! git push; then
echo "::error::README is out of sync and could not push fix to branch"
exit 1
- }
+ fi
+ echo "pushed=true" >> "$GITHUB_OUTPUT"
+
+ # GitHub's anti-loop policy: pushes made with the default GITHUB_TOKEN
+ # don't fire `pull_request`/`push` events, so the synced commit would
+ # land on the PR with no CI run. Re-dispatch CI manually so the new HEAD
+ # gets validated. (For a "proper" fix that fires the synchronize event,
+ # plumb a PAT/App token through the checkout step's `token:` input.)
+ - name: Re-trigger CI on synced commit
+ if: steps.sync.outputs.pushed == 'true'
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ BRANCH: ${{ github.head_ref }}
+ run: |
+ gh workflow run ci.yml --ref "$BRANCH"
+ gh workflow run sync-readme.yml --ref "$BRANCH"
+ gh workflow run pr-title.yml --ref "$BRANCH"
diff --git a/README.md b/README.md
index 15e7e96..cd54174 100644
--- a/README.md
+++ b/README.md
@@ -540,50 +540,80 @@ Both are first-class. The copy/paste block is right below, and it's honestly may
## FAQ
-### Does this work in Safari/Firefox/Chrome today?
+
+Does this work in Safari/Firefox/Chrome today?
Partially, at time of writing — recent Chrome ships `corner-shape`, Safari and Firefox are still catching up. Check [caniuse](https://caniuse.com/?search=corner-shape) for the current state. Either way you're fine: in a browser without support, you get a plain `border-radius` at the pre-correction value, which visually matches `rounded-*`. No broken layouts, no visible fallback weirdness.
-### Does it work with Tailwind v3?
+
+
+
+Does it work with Tailwind v3?
The **CSS utilities** (`tailwind/utils.css`) are v4-only — they use `@utility` and `--value()`, which don't exist in v3.
The **JS plugin** uses only APIs that exist in both v3 and v4 (`plugin.withOptions`, `matchUtilities`, `type: "length" | "number"`, `theme()`), so it's likely to work in v3 via a `tailwind.config.js`-style registration — but it's not currently tested or declared against v3. Tracked in [#26](https://github.com/dogmar/squircle/pull/26).
-### Why do my corners look smaller with `corner-shape: superellipse` without this?
+
+
+
+Why do my corners look smaller with corner-shape: superellipse without this?
At the same `border-radius`, a squircle pokes further into the corner, so less of the box edge is rounded off. The fix is to scale the radius up so the visual roundness matches `rounded-*` — see [How the radius correction works](#how-the-radius-correction-works).
-### Does this add runtime JS?
+
+
+
+Does this add runtime JS?
No. Everything is static CSS — the Tailwind utilities expand at build time into declarations with a native `calc()` the browser evaluates. The JS plugin also runs at build time only. Zero JS ships to the browser.
-### What happens once `corner-shape` is universal?
+
+
+
+What happens once corner-shape is universal?
Nothing you need to do. The correction lives inside `@supports (corner-shape: superellipse(2))`, so it activates exactly when the shape does. Once the browser ships support, the shape applies _and_ the compensated radius applies, at the same moment. Your layout is identical before and after.
-### Do I need `tailwind-merge`?
+
+
+
+Do I need tailwind-merge?
Only if your project already uses it. The extra config (`squircleMergeConfig`) exists so `rounded-lg squircle-md` de-duplicates the way you'd expect — otherwise tailwind-merge doesn't know `squircle-*` and `rounded-*` occupy the same slot.
-### What's the difference between the utilities and the `squircle-radius()` function?
+
+
+
+What's the difference between the utilities and the squircle-radius() function?
The utilities expand to inline `calc()` at build time — they work in any browser that supports `calc()` + `pow()` (most current ones) and degrade to plain `border-radius` where `corner-shape` isn't supported.
The `@function` form runs the same math at CSS runtime via CSS Values 5's `@function` — which is [experimental](https://caniuse.com/?search=%40function) (Chrome behind a flag, nothing else yet). Use the utilities unless you're specifically building for a non-Tailwind setup.
-### Can I use a different `squircle-amt` for each corner?
+
+
+
+Can I use a different squircle-amt for each corner?
No. `corner-shape` is declared once per element, so all four corners share the same K. You can still mix per-corner _radii_ (`squircle-tl-lg squircle-br-sm`), but the squircle-ness is uniform across the element.
-### Why is the tone of this README all over the place?
+
+
+
+Why is the tone of this README all over the place?
Because I made Claude write most of it, got mad at claude, re-wrote a lot of stuff myself, then got tired and let Claude win.
-### Did you just let Claude write this?
+
+
+
+Did you just let Claude write this?
Kinda. Honestly I wrote the basic tailwind utilities by hand using a weird cobbled together formula I just kinda eyeballed to work for most values anyone would actually want to use for the `superellipse()` param. But then I thought, hey, robots are good at math, maybe they can make the formula _actually_ **correct**. And they could! The robots _could_ make a right formula. I was so happy. I cried tears of joy for days and days. So many tears I drowned my robot. And now I'll never code again. Alas.
+
+
## Copy/paste source
If you'd rather not add a dependency, copy the source directly. Click to expand each file.
@@ -757,12 +787,46 @@ If you'd rather not add a dependency, copy the source directly. Click to expand
+
+tailwind/radius.css — the squircle-radius() CSS function
+
+
+
+```css
+/* ── squircle-radius() ────────────────────────────────────────
+ * Computes the visually-corrected border-radius for use with
+ * corner-shape: superellipse(). A superellipse corner appears
+ * tighter than a circular arc at the same radius, so the radius
+ * must be scaled up to preserve the intended visual size.
+ *
+ * --radius The target border-radius (any ).
+ * --squircle-amt The superellipse exponent passed to corner-shape.
+ *
+ * Parameters are untyped to preserve standard CSS resolution of
+ * relative units (em, rem, etc.) — they resolve in the context
+ * where the function result is applied, not at call time.
+ *
+ * Usage:
+ * border-radius: squircle-radius(1rem, 1.5);
+ * corner-shape: superellipse(1.5);
+ * ──────────────────────────────────────────────────────────── */
+@function squircle-radius(--radius, --squircle-amt) {
+ result: calc(
+ var(--radius) * (1 - pow(2, -0.5)) / (1 - pow(2, -1 * pow(2, -1 * var(--squircle-amt))))
+ );
+}
+```
+
+
+
+
+
tailwind/index.mjs — the Tailwind plugin and tailwind-merge config
-````js
+```js
import { a as squircleCssObj, i as SUPPORTS_RULE, o as variantEntries } from "../variants-CUhqvLRq.mjs";
import plugin from "tailwindcss/plugin";
//#region src/tailwind.ts
@@ -829,7 +893,9 @@ const squircleMergeConfig = { extend: {
//#endregion
export { squircle as default, squircleMergeConfig };
-//# sourceMappingURL=index.mjs.map```
+//# sourceMappingURL=index.mjs.map
+```
+
@@ -838,7 +904,8 @@ export { squircle as default, squircleMergeConfig };
panda/index.mjs — the Panda CSS preset
-```js
+
+````js
import { a as squircleCssObj, i as SUPPORTS_RULE, o as variantEntries, t as CAMEL_VARIANTS } from "../variants-CUhqvLRq.mjs";
import { definePreset } from "@pandacss/dev";
//#region src/panda.ts
@@ -894,7 +961,9 @@ function squirclePandaPreset(options = {}) {
//#endregion
export { squirclePandaPreset as default, squirclePandaPreset };
-//# sourceMappingURL=index.mjs.map```
+//# sourceMappingURL=index.mjs.map
+````
+
@@ -903,7 +972,8 @@ export { squirclePandaPreset as default, squirclePandaPreset };
stylex/index.mjs — the StyleX dynamic-style preset
-```js
+
+````js
import * as stylex from "@stylexjs/stylex";
//#region src/stylex.ts
/**
@@ -1127,7 +1197,9 @@ const squircle = stylex.create({
//#endregion
export { squircle };
-//# sourceMappingURL=index.mjs.map```
+//# sourceMappingURL=index.mjs.map
+````
+
@@ -1141,4 +1213,3 @@ export { squircle };
## License
MIT
-````
diff --git a/scripts/sync-readme.sh b/scripts/sync-readme.sh
index f7f0230..7f7c830 100755
--- a/scripts/sync-readme.sh
+++ b/scripts/sync-readme.sh
@@ -12,11 +12,15 @@ sync_file() {
local begin=""
local end=""
- # Build replacement: marker, fenced code block, content, closing fence
+ # Build replacement: marker, fenced code block, content, closing fence.
+ # Some bundled dist files have no trailing newline, which would glue the
+ # closing fence to the last source line (e.g. `//# sourceMappingURL=…```)
+ # and prevent the fence from closing — so force a newline before it.
{
echo "$begin"
echo "\`\`\`$lang"
cat "$src"
+ [ -z "$(tail -c 1 "$src")" ] || echo ""
echo "\`\`\`"
} > "$REPO_ROOT/.sync-block.tmp"
@@ -76,6 +80,7 @@ sync_toc() {
}
sync_file "dist/tailwind/utils.css" "css" "package/dist/tailwind/utils.css"
+sync_file "dist/tailwind/radius.css" "css" "package/dist/tailwind/radius.css"
sync_file "dist/tailwind/index.mjs" "js" "package/dist/tailwind/index.mjs"
sync_file "dist/panda/index.mjs" "js" "package/dist/panda/index.mjs"
sync_file "dist/stylex/index.mjs" "js" "package/dist/stylex/index.mjs"