Linen
Linen texture: a woven overlay of fine horizontal and vertical threads on coprime periods so the repeat stays hidden. White threads by default, recolored for light grounds; three intensities.
pnpm dlx shadcn add @01orbital/linenimport { cn } from "cn"
type Intensity = "fine" | "default" | "rich"
// One scale for every texture; `default` is the unscaled value.
const INTENSITY_MULT: Record<Intensity, number> = {
fine: 0.6,
default: 1,
rich: 1.45,
}
const alpha = (base: number, mult: number) => (base * mult).toFixed(4)
// Full-bleed art layer — drop inside a `relative` parent; sits behind content.
// Two-direction weave — 1px threads at 0°+90°. 5px and 6px are the thread
// PERIODS, not gaps, and they are coprime, so the weave repeats every 30px.
function Linen({
intensity = "default",
className,
...props
}: React.ComponentProps<"div"> & {
// Thread alpha multiplier. Ink is white; set --linen-color on a light ground.
intensity?: Intensity
}) {
const m = INTENSITY_MULT[intensity]
return (
<div
className={cn("pointer-events-none absolute inset-0", className)}
aria-hidden="true"
data-slot="linen"
{...props}
>
<div
className="absolute inset-0"
style={{
backgroundImage: `repeating-linear-gradient(90deg in srgb, transparent 0px, transparent 2px, oklch(from var(--linen-color, oklch(1 0 0)) l c h / ${alpha(0.05, m)}) 2px, oklch(from var(--linen-color, oklch(1 0 0)) l c h / ${alpha(0.02, m)}) 3px, transparent 3px, transparent 6px), repeating-linear-gradient(0deg in srgb, transparent 0px, transparent 2px, oklch(from var(--linen-color, oklch(1 0 0)) l c h / ${alpha(0.06, m)}) 2px, oklch(from var(--linen-color, oklch(1 0 0)) l c h / ${alpha(0.02, m)}) 3px, transparent 3px, transparent 5px)`,
}}
/>
</div>
)
}
export { Linen }
import { Linen } from "@/components/texture/linen"
import {
Card,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card"
/** A sized linen surface with readable studio details. */
export default function LinenDemo() {
return (
<div className="flex min-h-full w-full flex-1 items-center justify-center p-4 sm:p-8">
<Card className="relative isolate w-full max-w-sm gap-6 overflow-hidden rounded-xl bg-[#e0d9ce] p-6 wrap-anywhere text-[#38332c] shadow-sm ring-1 ring-black/10 [--muted-foreground:#51483d] sm:p-8">
<Linen intensity="rich" className="[--linen-color:#38332c]" />
<CardHeader className="relative z-20 gap-6 px-0">
<p className="text-sm font-medium">Selvedge Studio</p>
<div className="grid gap-1">
<CardTitle className="text-2xl/8 font-medium tracking-tight">
Nora Ellis
</CardTitle>
<CardDescription>Textile designer</CardDescription>
</div>
</CardHeader>
<CardFooter className="relative z-20 flex-wrap justify-between gap-3 border-t border-current/20 px-0 pt-5 text-xs/5">
<span>8 Loom Street, Leeds</span>
<a
href="mailto:hello@selvedge.example"
className="break-all underline underline-offset-4 focus-visible:outline-2 focus-visible:outline-offset-4"
>
hello@selvedge.example
</a>
</CardFooter>
</Card>
</div>
)
}
The example also uses card. Install it too:
pnpm dlx shadcn add @01orbital/cardnpx shadcn add @01orbital/cardyarn shadcn add @01orbital/cardbunx --bun shadcn add @01orbital/cardUsage
import { Linen } from "@/components/texture/linen"
import {
Card,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card"
/** A sized linen surface with readable studio details. */
export default function LinenDemo() {
return (
<div className="flex min-h-full w-full flex-1 items-center justify-center p-4 sm:p-8">
<Card className="relative isolate w-full max-w-sm gap-6 overflow-hidden rounded-xl bg-[#e0d9ce] p-6 wrap-anywhere text-[#38332c] shadow-sm ring-1 ring-black/10 [--muted-foreground:#51483d] sm:p-8">
<Linen intensity="rich" className="[--linen-color:#38332c]" />
<CardHeader className="relative z-20 gap-6 px-0">
<p className="text-sm font-medium">Selvedge Studio</p>
<div className="grid gap-1">
<CardTitle className="text-2xl/8 font-medium tracking-tight">
Nora Ellis
</CardTitle>
<CardDescription>Textile designer</CardDescription>
</div>
</CardHeader>
<CardFooter className="relative z-20 flex-wrap justify-between gap-3 border-t border-current/20 px-0 pt-5 text-xs/5">
<span>8 Loom Street, Leeds</span>
<a
href="mailto:hello@selvedge.example"
className="break-all underline underline-offset-4 focus-visible:outline-2 focus-visible:outline-offset-4"
>
hello@selvedge.example
</a>
</CardFooter>
</Card>
</div>
)
}Examples
New
linen-studio-card
New
linen-workshop-invitation
New
linen-seasonal-menu
New
linen-field-note
New
linen-edition-cover
New
linen-order-receipt
New
linen-collection-label
New
linen-studio-letter
New
linen-quote-card
New
linen-day-programme
API reference
Prop
Type
It also accepts the HTML attributes of the element it renders.
Dependencies
npm: cn. The CLI installs them for you.
No other registry pieces.