Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,18 @@
commit. After each release, main and beta are merged back down into the
branches below them automatically.
-->

## Release note

<!--
One or two sentences for people using the package: what's new or fixed, and
how to use it. Its first paragraph becomes this PR's line in the release
notes, as written, so leave how it works to the rest of the description.

Required for feat, fix and perf PRs and breaking changes (say what to change).
Write "None" if users won't notice the change. On a promotion PR, it's the
summary at the top of the release.

Example: Add `squircle-pill-ease-*` to stretch a pill's easing along its
straight edges.
-->
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ jobs:

- run: vp run ready

- name: Test the release notes
run: node --test 'release/*.test.mjs'

- run: npx semantic-release --dry-run
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
19 changes: 18 additions & 1 deletion .github/workflows/pr-title.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
name: PR title check
name: PR title and release note

# Checks the PR title against Conventional Commits, and the "## Release note"
# section of the description (release/check-release-note.mjs). Both report the
# required `check` status.

on:
pull_request:
Expand All @@ -14,3 +18,16 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
pass_on_octokit_error: false
configuration_path: .github/pr-title-checker.json

- uses: actions/checkout@v6
if: github.event_name == 'pull_request'
with:
sparse-checkout: release

- name: Check the release note
if: github.event_name == 'pull_request'
env:
PR_TITLE: ${{ github.event.pull_request.title }}
PR_BODY: ${{ github.event.pull_request.body }}
HEAD_REF: ${{ github.head_ref }}
run: node release/check-release-note.mjs
49 changes: 49 additions & 0 deletions .github/workflows/release-notes.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Release notes preview

# On a promotion PR (alpha or beta into main or beta), keeps one comment with
# the version and release notes that adding the `promote` label would
# publish, made by the same code as the real ones. See release/preview.mjs.

on:
pull_request:
branches: [main, beta]
types: [opened, edited, synchronize, reopened]

permissions: {}

jobs:
preview:
if: github.head_ref == 'alpha' || github.head_ref == 'beta'
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Write the preview
env:
GITHUB_TOKEN: ${{ github.token }}
BASE_REF: ${{ github.base_ref }}
HEAD_REF: ${{ github.head_ref }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_BODY: ${{ github.event.pull_request.body }}
run: node release/preview.mjs >"$RUNNER_TEMP/preview.md"

- name: Post or update the comment
env:
GH_TOKEN: ${{ github.token }}
PR: ${{ github.event.pull_request.number }}
run: |
existing=$(gh api "repos/$GITHUB_REPOSITORY/issues/$PR/comments" --paginate \
--jq '.[] | select(.body | startswith("<!-- release-notes-preview -->")) | .id' | head -1)
if [[ -n $existing ]]; then
gh api --method PATCH "repos/$GITHUB_REPOSITORY/issues/comments/$existing" \
-F body=@"$RUNNER_TEMP/preview.md" >/dev/null
else
gh pr comment "$PR" --repo "$GITHUB_REPOSITORY" --body-file "$RUNNER_TEMP/preview.md"
fi
2 changes: 2 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ on:
push:
branches:
- main
- beta
- alpha
workflow_dispatch:

permissions:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/sync-readme.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Sync README code blocks

on:
pull_request:
branches: [main]
branches: [main, beta, alpha]
workflow_dispatch:

permissions:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/sync-stylex.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Sync generated stylex.ts

on:
pull_request:
branches: [main]
branches: [main, beta, alpha]
paths:
- package/src/stylex.template.ts
- package/src/stylex.ts
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ dist/
.pnpm-store/
.DS_Store
.playwright-mcp
.superpowers/
.superpowers/.netlify/
273 changes: 228 additions & 45 deletions README.md

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions netlify.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Deploy previews for pull requests. Production stays on GitHub Pages
# (.github/workflows/deploy-site.yml).
#
# Netlify reads this file from the repository root. `base` is set here rather
# than left to the site's UI settings, and `publish` is relative to it.
#
# Node comes from .node-version and pnpm from the packageManager field, both
# of which Netlify reads on its own.
#
# The steps are called directly rather than through `vp run website#build`:
# the vite-plus task runner builds fine locally and in GitHub Actions but
# hangs on Netlify's build image until the build times out. The package has
# to be built first because the website imports it from dist/.

[build]
base = "website"
# Netlify skips a build when nothing under `base` changed, but the site
# bundles the package from ../package, so a package-only change would leave
# the preview stale. Exit 1 means build; skip only when neither changed.
ignore = "git diff --quiet \"$CACHED_COMMIT_REF\" \"$COMMIT_REF\" -- . ../package ../pnpm-lock.yaml ../netlify.toml"
command = "pnpm --filter @klinking/squircle exec tsx scripts/build.ts && pnpm --filter website exec astro build"
publish = "dist"
31 changes: 28 additions & 3 deletions package/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,29 @@
"import": "./dist/tailwind/index.mjs"
},
"./tailwind/utils.css": "./dist/tailwind/utils.css",
"./tailwind-pill": {
"types": "./dist/tailwind-pill/index.d.mts",
"import": "./dist/tailwind-pill/index.mjs"
},
"./tailwind-pill-border": {
"types": "./dist/tailwind-pill-border/index.d.mts",
"import": "./dist/tailwind-pill-border/index.mjs"
},
"./radius-function.css": "./dist/radius-function.css",
"./squircle-pill.css": "./dist/squircle-pill.css",
"./pill-polyfill": {
"types": "./dist/pill-polyfill.d.mts",
"import": "./dist/pill-polyfill.mjs"
},
"./pill-worklet": {
"types": "./dist/pill-worklet.d.mts",
"import": "./dist/pill-worklet.mjs"
},
"./pill-shape.worklet": {
"types": "./dist/pill-shape.worklet.d.mts",
"import": "./dist/pill-shape.worklet.mjs"
},
"./pill-shape.worklet.js": "./dist/pill-shape.worklet.mjs",
"./panda": {
"types": "./dist/panda/index.d.mts",
"import": "./dist/panda/index.mjs"
Expand All @@ -50,9 +72,9 @@
"oxlint": "^1.58.0",
"tsx": "^4.21.0",
"typescript": "^6.0.2",
"vite": "npm:@voidzero-dev/vite-plus-core@^0.1.15",
"vite": "catalog:",
"vite-plus": "catalog:",
"vitest": "npm:@voidzero-dev/vite-plus-test@^0.1.15"
"vitest": "catalog:"
},
"peerDependencies": {
"@pandacss/dev": ">=0.40.0",
Expand All @@ -74,5 +96,8 @@
"optional": true
}
},
"packageManager": "pnpm@10.33.0"
"packageManager": "pnpm@10.33.0",
"squircle": {
"cssNamespace": "squircle"
}
}
27 changes: 27 additions & 0 deletions package/scripts/build.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { execFileSync } from "node:child_process";
import { existsSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";

/*
* The whole package build, as one step.
*
* `vp run` caches each `&&`-separated segment of a task command on its own, so
* a chain of `vp pack && tsx generate-*.ts` could re-run the pack — which
* empties dist/ first — while replaying the generators from cache, leaving
* dist/ without the generated CSS. One script is one cache entry.
*/
const pkgDir = join(dirname(fileURLToPath(import.meta.url)), "..");
const bin = (name: string) =>
[
join(pkgDir, "node_modules", ".bin", name),
join(pkgDir, "..", "node_modules", ".bin", name),
].find(existsSync) ?? name;

const run = (file: string, args: string[]) =>
execFileSync(file, args, { cwd: pkgDir, stdio: "inherit" });

run(bin("tsx"), ["scripts/generate-stylex.ts"]);
run(bin("vp"), ["pack"]);
run(bin("tsx"), ["scripts/generate-squircle-css.ts"]);
run(bin("tsx"), ["scripts/generate-pill-css.ts"]);
17 changes: 17 additions & 0 deletions package/scripts/generate-pill-css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Pins the namespace before variants.ts is evaluated; keep it first.
import { CSS_NAMESPACE } from "./load-namespace";
import { mkdirSync, writeFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { renderPillCss } from "../src/pill-css";

const __dirname = dirname(fileURLToPath(import.meta.url));

const distDir = join(__dirname, "..", "dist");
mkdirSync(distDir, { recursive: true });

// Rendered from the same rules the Tailwind utility carries, so the two
// cannot drift, and under the same namespace the worklet was built with.
const outPath = join(distDir, "squircle-pill.css");
writeFileSync(outPath, renderPillCss());
console.log(`Generated ${outPath} (namespace: ${CSS_NAMESPACE})`);
2 changes: 2 additions & 0 deletions package/scripts/generate-squircle-css.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Pins the namespace before variants.ts is evaluated; keep it first.
import "./load-namespace";
import { copyFileSync, mkdirSync, writeFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
Expand Down
24 changes: 24 additions & 0 deletions package/scripts/load-namespace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { readFileSync } from "node:fs";

/**
* The one place the custom-property namespace is resolved.
*
* `SQUIRCLE_CSS_NAMESPACE` overrides it for a one-off build;
* `squircle.cssNamespace` in package.json is the committed default for a
* fork that wants it permanently. Importing this module first pins the
* resolved value into the environment, which is where `variants.ts` reads it
* from when it runs outside Vite (under `tsx`) and so has no define.
*/
export const DEFAULT_CSS_NAMESPACE = "squircle";

export function resolveCssNamespace(): string {
return (
process.env["SQUIRCLE_CSS_NAMESPACE"] ||
JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).squircle
?.cssNamespace ||
DEFAULT_CSS_NAMESPACE
);
}

export const CSS_NAMESPACE: string = resolveCssNamespace();
process.env["SQUIRCLE_CSS_NAMESPACE"] = CSS_NAMESPACE;
12 changes: 11 additions & 1 deletion package/src/panda.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,25 @@ import { definePreset, type PropertyConfig } from "@pandacss/dev";
import {
CAMEL_VARIANTS,
DEFAULT_AMOUNT_VAR_NAME,
DEFAULT_R_VAR_NAME,
SUPPORTS_RULE,
squircleCssObj,
variantEntries,
} from "./variants";

export interface SquirclePandaPresetOptions {
/** CSS custom property name for the superellipse amount (default: "--squircle-amt"). */
/**
* @deprecated Set the namespace instead, with `SQUIRCLE_CSS_NAMESPACE` at
* build time, which renames every property this package owns together. This
* option still works and still wins.
*/
amtVar?: string;
/** CSS custom property name for the intermediate corrected radius (default: "--squircle-r"). */
/**
* @deprecated Set the namespace instead, with `SQUIRCLE_CSS_NAMESPACE` at
* build time. This option still works and still wins.
*/
rVar?: string;
}

Expand All @@ -38,7 +48,7 @@ export interface SquirclePandaPresetOptions {
*/
export function squirclePandaPreset(options: SquirclePandaPresetOptions = {}) {
const amtVar = options.amtVar ?? DEFAULT_AMOUNT_VAR_NAME;
const rVar = options.rVar ?? "--squircle-r";
const rVar = options.rVar ?? DEFAULT_R_VAR_NAME;

const utilities: Record<string, PropertyConfig> = {};

Expand Down
Loading
Loading