Layouts for walking through the useful parts of a product. Replace the copy, icons, and imagery with your own.
Three up
Three outlined cards with image placeholders, titles, and short copy. They stack on narrow widths.

Token ready
Restyle blocks with your theme tokens.

Copy and ship
Install a block and drop it into a page.

Built to compose
Mix sections without fighting the layout.
"use client";import { cn } from "@/lib/utils";type Features01Props = { className?: string;};const FEATURES = [ { title: "Token ready", description: "Restyle blocks with your theme tokens.", image: "/assets/brand/demos/attachments/attachment-1.png", }, { title: "Copy and ship", description: "Install a block and drop it into a page.", image: "/assets/brand/demos/attachments/attachment-2.png", }, { title: "Built to compose", description: "Mix sections without fighting the layout.", image: "/assets/brand/demos/attachments/attachment-3.png", },] as const;export function Features01({ className }: Features01Props) { return ( <section className={cn("@container w-full bg-background", className)}> <div className="mx-auto w-full max-w-4xl px-6 py-14"> <ul className="grid w-full gap-4 @[40rem]:grid-cols-3"> {FEATURES.map((feature) => ( <li key={feature.title} className="rounded-xl border border-border p-2" > <div className="aspect-[16/10] w-full overflow-hidden rounded-sm bg-muted"> <img src={feature.image} alt="" className="size-full object-cover" /> </div> <div className="px-1 pt-3 pb-1"> <h3 className="text-sm font-medium tracking-wide text-foreground"> {feature.title} </h3> <p className="mt-1.5 text-sm tracking-wide text-muted-foreground"> {feature.description} </p> </div> </li> ))} </ul> </div> </section> );}Installation
npx @arctis-sh/@arctis-sh/ui@latest add features-01Usage
import { Features01 } from "@/components/blocks/features/features-01"<Features01 />Accordion split
An accordion on the left and a matching preview on the right. One item always remains open, with plus and minus controls.
"use client";import { useState } from "react";import { Accordion, AccordionContent, AccordionItem, AccordionTrigger,} from "@/components/ui/accordion";import { cn } from "@/lib/utils";type Features02Props = { className?: string;};const FEATURES = [ { value: "1", title: "Token ready", description: "Restyle every block with your colors, radii, and type.", image: "/assets/brand/demos/attachments/attachment-1.png", }, { value: "2", title: "Copy and ship", description: "Install a block, drop it in a page, and keep moving.", image: "/assets/brand/demos/attachments/attachment-2.png", }, { value: "3", title: "Built to compose", description: "Mix heroes, nav, and sections without fighting the layout.", image: "/assets/brand/demos/attachments/attachment-3.png", },] as const;export function Features02({ className }: Features02Props) { const [open, setOpen] = useState("1"); const index = Math.max( 0, FEATURES.findIndex((feature) => feature.value === open), ); return ( <section className={cn("@container w-full bg-background", className)}> <div data-slot="block-split" className="mx-auto flex w-full max-w-4xl flex-col gap-6 px-6 py-14 lg:flex-row lg:items-stretch lg:gap-10" > <div className="flex min-w-0 flex-1 flex-col justify-center"> <Accordion type="single" collapsible={false} value={open} onValueChange={(value) => { if (typeof value === "string" && value) setOpen(value); }} > {FEATURES.map((feature) => ( <AccordionItem key={feature.value} value={feature.value}> <AccordionTrigger indicator="plus"> {feature.title} </AccordionTrigger> <AccordionContent>{feature.description}</AccordionContent> </AccordionItem> ))} </Accordion> </div> <div className="relative min-h-[16rem] flex-1 overflow-hidden rounded-md bg-muted lg:min-h-[20rem]"> <div className="absolute inset-x-0 top-0 w-full will-change-transform transition-transform duration-500 ease-[cubic-bezier(0.22,1,0.36,1)]" style={{ height: `${FEATURES.length * 100}%`, transform: `translate3d(0, -${(index * 100) / FEATURES.length}%, 0)`, }} > {FEATURES.map((feature) => ( <div key={feature.value} className="w-full" style={{ height: `${100 / FEATURES.length}%` }} > <img src={feature.image} alt="" className="size-full object-cover" /> </div> ))} </div> </div> </div> </section> );}Installation
npx @arctis-sh/@arctis-sh/ui@latest add features-02Usage
import { Features02 } from "@/components/blocks/features/features-02"<Features02 />Alternating
Image-and-copy rows that alternate sides, then stack on narrow widths.

Token ready
Restyle every block with your colors, radii, and type.

Copy and ship
Install a block, drop it in a page, and keep moving.

Built to compose
Mix heroes, nav, and sections without fighting the layout.
"use client";import { cn } from "@/lib/utils";type Features03Props = { className?: string;};const FEATURES = [ { title: "Token ready", description: "Restyle every block with your colors, radii, and type.", image: "/assets/brand/demos/attachments/attachment-1.png", }, { title: "Copy and ship", description: "Install a block, drop it in a page, and keep moving.", image: "/assets/brand/demos/attachments/attachment-2.png", }, { title: "Built to compose", description: "Mix heroes, nav, and sections without fighting the layout.", image: "/assets/brand/demos/attachments/attachment-3.png", },] as const;export function Features03({ className }: Features03Props) { return ( <section className={cn("w-full bg-background", className)}> <div className="mx-auto flex w-full max-w-4xl flex-col gap-12 px-6 py-14 lg:gap-16"> {FEATURES.map((feature, index) => { const flip = index % 2 === 1; return ( <div key={feature.title} data-slot="block-split" className={cn( "flex flex-col gap-6 lg:flex-row lg:items-center lg:gap-10", flip && "lg:flex-row-reverse", )} > <div className="min-h-[14rem] flex-1 overflow-hidden rounded-md bg-muted lg:min-h-[18rem]"> <img src={feature.image} alt="" className="size-full object-cover" /> </div> <div className="flex flex-1 flex-col justify-center"> <h3 className="text-base font-medium tracking-wide text-foreground"> {feature.title} </h3> <p className="mt-2 max-w-sm text-sm tracking-wide text-muted-foreground"> {feature.description} </p> </div> </div> ); })} </div> </section> );}Installation
npx @arctis-sh/@arctis-sh/ui@latest add features-03Usage
import { Features03 } from "@/components/blocks/features/features-03"<Features03 />Comparison
A feature table comparing one product mark with a stacked group of logos.
Demo brands
Demo logos are placeholders. Swap in your own before shipping.
"use client";import { Avatar, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarImage,} from "@/components/ui/avatar";import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow,} from "@/components/ui/table";import { cn } from "@/lib/utils";type Features04Props = { className?: string;};const ROWS = [ { feature: "Realtime sync", ours: true, others: false }, { feature: "Shared workspaces", ours: true, others: true }, { feature: "Keyboard-first UI", ours: true, others: false }, { feature: "Guest access", ours: true, others: false }, { feature: "Audit history", ours: true, others: false },] as const;const OTHER_LOGOS = [ "/assets/icons/logos/slack.svg", "/assets/icons/logos/notion.svg", "/assets/icons/logos/figma.svg",] as const;function CheckIcon({ className }: { className?: string }) { return ( <svg viewBox="0 0 24 24" fill="none" aria-hidden="true" className={className}> <path d="M5 12.5 10 17.5 19 7.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" /> </svg> );}function DashIcon({ className }: { className?: string }) { return ( <svg viewBox="0 0 24 24" fill="none" aria-hidden="true" className={className}> <path d="M6 12h12" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" /> </svg> );}function CellMark({ on }: { on: boolean }) { return on ? ( <CheckIcon className="size-4 text-foreground" /> ) : ( <DashIcon className="size-4 text-muted-foreground" /> );}function LogoAvatar({ src }: { src: string }) { return ( <Avatar size="xs"> <AvatarImage src={src} alt="" className="object-contain p-1 brightness-0 dark:invert" /> <AvatarFallback /> </Avatar> );}export function Features04({ className }: Features04Props) { return ( <section className={cn("w-full bg-background", className)}> <div className="mx-auto w-full max-w-3xl px-6 py-14"> <Table> <TableHeader> <TableRow> <TableHead className="w-[46%]">Feature</TableHead> <TableHead className="w-[27%]"> <span className="inline-flex items-center gap-2 text-foreground"> <LogoAvatar src="/assets/icons/logos/linear.svg" /> Linear </span> </TableHead> <TableHead className="w-[27%]"> <AvatarGroup overlap="sm"> {OTHER_LOGOS.map((src) => ( <LogoAvatar key={src} src={src} /> ))} <AvatarGroupCount className="size-6 text-[10px]"> +4 </AvatarGroupCount> </AvatarGroup> </TableHead> </TableRow> </TableHeader> <TableBody> {ROWS.map((row) => ( <TableRow key={row.feature}> <TableCell className="text-foreground">{row.feature}</TableCell> <TableCell> <CellMark on={row.ours} /> </TableCell> <TableCell> <CellMark on={row.others} /> </TableCell> </TableRow> ))} </TableBody> </Table> </div> </section> );}Installation
npx @arctis-sh/@arctis-sh/ui@latest add features-04Usage
import { Features04 } from "@/components/blocks/features/features-04"<Features04 />