Radio Group
Pencil circles that fill with a blot of ink.
Install
$ npx shadcn@latest add https://ephemera.kerrshift.com/r/radio-group.jsonOr copy the source below into components/ui/radio-group.tsx. Core components need the Paper Theme and Radix where noted.
Usage
example.tsx
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
<RadioGroup defaultValue="post">
<div className="flex items-center gap-3">
<RadioGroupItem value="post" id="post" />
<Label htmlFor="post">First-class post</Label>
</div>
<div className="flex items-center gap-3">
<RadioGroupItem value="pigeon" id="pigeon" />
<Label htmlFor="pigeon">Carrier pigeon</Label>
</div>
</RadioGroup>Built on the Radix Radio Group, so keyboard navigation and screen reader support come for free.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| value / defaultValue / onValueChange | string | — | Selected option. |
| RadioGroupItem value | string | — | The value this circle represents. |
| orientation | "vertical" | "horizontal" | "vertical" | Arrow-key direction. |
Any other props are passed to the root element.
Fonts
Components read optional CSS variables and fall back to system fonts: --font-display, --font-receipt, --font-type, --font-hand and --font-stamp. This site uses Fraunces, IBM Plex Mono, Courier Prime, Caveat and Anton.
Source
components/ui/radio-group.tsx
"use client";
import * as React from "react";
import { RadioGroup as RadioGroupPrimitive } from "radix-ui";
/** Pencil circles that fill with a blot of ink. */
export function RadioGroup({ className = "", ...props }: React.ComponentProps<typeof RadioGroupPrimitive.Root>) {
return <RadioGroupPrimitive.Root className={`grid gap-3 ${className}`} {...props} />;
}
export function RadioGroupItem({ className = "", ...props }: React.ComponentProps<typeof RadioGroupPrimitive.Item>) {
return (
<RadioGroupPrimitive.Item
className={`peer relative inline-flex size-[20px] shrink-0 cursor-pointer items-center justify-center border-[1.5px] border-(--ink-muted) bg-(--paper-raised) outline-none transition-colors hover:border-(--ink) focus-visible:ring-2 focus-visible:ring-(--pen) focus-visible:ring-offset-2 focus-visible:ring-offset-(--paper) disabled:cursor-not-allowed disabled:opacity-50 ${className}`}
style={{ borderRadius: "50% 46% 52% 48% / 48% 52% 46% 50%" }}
{...props}
>
<RadioGroupPrimitive.Indicator
data-paper-motion
className="block size-[10px] bg-(--pen) animate-[paper-blot_.25s_ease-out_both]"
style={{ borderRadius: "55% 45% 50% 50% / 45% 55% 48% 52%" }}
/>
</RadioGroupPrimitive.Item>
);
}