Features

Feature sections for explaining what matters clearly.

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.

tsx
"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-01

Usage

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.

Restyle every block with your colors, radii, and type.
tsx
"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-02

Usage

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.

tsx
"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-03

Usage

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.

Feature Linear
+4
Realtime sync
Shared workspaces
Keyboard-first UI
Guest access
Audit history
tsx
"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-04

Usage

import { Features04 } from "@/components/blocks/features/features-04"
<Features04 />