Figma • iOS style corner smoothing using CSS Houdini Paint Worklet.
Preview • Source Code • SpaceUI.one
@usespaceui/squircle brings iOS-style squircles (mathematically perfect superellipses) to your Tailwind CSS projects. It mimics the continuous curvature corners found in Figma and Apple devices.
By utilizing the CSS Houdini Paint Worklet API, it provides a native-like performance and seamlessly integrates with Tailwind CSS.
pnpm add @usespaceui/squircle
# or
npm install @usespaceui/squircle
# or
yarn add @usespaceui/squircleZero dependencies. Add the plugin to your Tailwind configuration (v3) or your CSS entry file (v4).
Because the plugin relies on a CSS Houdini worklet to draw the shape, you must initialize the script once in your app.
Since the worklet interacts with the browser DOM, initialize it inside a Client Component provider.
// components/SquircleProvider.tsx
'use client'
import * as React from 'react'
import { initSquircle } from '@usespaceui/squircle'
export function SquircleProvider({ children }: { children: React.ReactNode }) {
React.useEffect(() => {
initSquircle()
}, [])
return <>{children}</>
}Wrap your root layout:
import { SquircleProvider } from './SquircleProvider'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<SquircleProvider>{children}</SquircleProvider>
</body>
</html>
)
}Initialize the worklet directly in your client-side entry file:
import { initSquircle } from '@usespaceui/squircle'
if (typeof window !== 'undefined') {
initSquircle()
}In Tailwind CSS v4, add the plugin directly to your CSS entry point (e.g. globals.css or styles.css):
@import 'tailwindcss';
@plugin "@usespaceui/squircle";Add the squircle plugin to your tailwind.config.js or tailwind.config.ts:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./src/**/*.{js,ts,jsx,tsx}'],
theme: {
extend: {},
},
plugins: [require('@usespaceui/squircle')],
}import type { Config } from 'tailwindcss'
import squircle from '@usespaceui/squircle'
const config: Config = {
content: ['./src/**/*.{js,ts,jsx,tsx}'],
theme: {
extend: {},
},
plugins: [squircle],
}
export default configThe plugin generates squircle utilities based on your existing Tailwind borderRadius theme. If your Tailwind config has rounded-2xl, you automatically have squircle-2xl.
<div class="squircle-md p-4 bg-black text-white">Space UI Squircle!</div>
<div class="squircle-2xl">...</div>
<div class="squircle-full">...</div>
<!-- Automatically clamped to pill/circle -->
<!-- Arbitrary values are fully supported -->
<div class="squircle-[40px]">...</div>By default, the plugin applies a 60% smooth transition between the straight edge and the corner arc, perfectly matching Apple and Figma's native smoothing.
You can adjust this intensity using modifiers (/0, /20, /40, /60, /80, /100).
<!-- Maximum smoothing -->
<div class="squircle-2xl/100">...</div>
<!-- Minimal smoothing (closer to a plain rounded rectangle) -->
<div class="squircle-2xl/20">...</div>
<!-- Arbitrary smoothing -->
<div class="squircle-2xl/[25]">...</div>Use standard Tailwind border utilities. The Houdini worklet reads your native CSS border classes and draws the squircle border perfectly. No workarounds needed.
<!-- 8px red border -->
<div class="squircle-2xl border-8 border-red-500 bg-black">...</div>
<!-- 1px semi-transparent blue border -->
<div class="squircle-[30px] border border-blue-500/50 bg-white">...</div>Note on border placement: By default, the plugin draws the border outside the element (like an outline or a ring) to preserve the interior space for your content. If you want the border drawn inward (standard CSS border-box behavior), you can override the custom CSS variable:
<div class="squircle-2xl border-4 border-red-500 [--tw-squircle-outset:0]">...</div>
If you use tailwind-merge (standard in UI libraries like shadcn/ui for their cn() utility), you should configure it so that squircle-* classes properly override standard rounded-* classes when both are applied.
Extend your merge configuration to group squircle into the rounded class group:
import { clsx, type ClassValue } from 'clsx'
import { extendTailwindMerge } from 'tailwind-merge'
const customTwMerge = extendTailwindMerge({
extend: {
classGroups: {
rounded: [
{ squircle: [() => true] }, // Merges any squircle-* class into the rounded group
],
},
},
})
export function cn(...inputs: ClassValue[]) {
return customTwMerge(clsx(inputs))
}-
initSquircle()Registers the CSS Houdini Paint Worklet and sets up the custom CSS properties (--tw-squircle-wand--tw-squircle-smooth). It is safe to call multiple times and fails gracefully in environments where Houdini is not supported. -
plugin(tailwindcss)The default export is a Tailwind CSS plugin that provides thesquircle-*utility classes. It automatically provides fallbackborder-radiusstyles for browsers that don't support the Paint API.
- 💃 Native Tailwind Integration: Use utility classes like
squircle-2xljust like you wouldrounded-2xl. No framework-specific wrappers required, and if the browser doesn't support the Paint API, standard rounded fallback values apply automatically. - ⚡ Zero-JS Layout Thrashing: Driven by the native CSS Paint API (Houdini). No
ResizeObserver, no DOM manipulation, no SVG paths. Repaints happen instantly on the compositor thread. - 👌 Framework Agnostic: Works in React, Vue, Svelte, Solid, or plain HTML.
| Package | Description |
|---|---|
@usespaceui/avatars |
Generative deterministic avatars |
@usespaceui/sounds |
UI sound effects and audio interactions |
MIT — Free for commercial and personal use.
If you find a bug or have a feature request, please open an issue on GitHub. Engine internals are intentionally not part of the public API.