İçeriğe atla

Öğe

Bildirim akışları, ayar listeleri ve dosya satırları için medya, başlık, açıklama ve eylem alanlarına sahip esnek bir satır — neobrütalist tarzında.

import { BadgeCheckIcon, ChevronRightIcon } from "lucide-react"

import { Button } from "@/components/ui/button"

Item, her uygulamanın er ya da geç ihtiyaç duyduğu “nesne listesi” deseni için adlandırılmış yuvalara — medya, içerik, eylemler — sahip bir flex satırıdır. Altta headless bir primitif yoktur; yalnızca stillendirilmiş öğeler (artı render prop'u için Base UI useRender) neobrütalist reçeteyle tamamlanmıştır: 2 piksellik bir kenarlık — outline varyantında görünür — ve keskin, ofsetli bir odak halkası.

Şu durumlarda tercih edin:

  • Bildirim ve etkinlik akışları — her satırda avatar, mesaj, zaman damgası ve bir kapatma veya eylem düğmesi.
  • Ayar ve entegrasyon listeleri — simge, ad, durum satırı; uç tarafta bir anahtar veya düğme.
  • Dosya gezginleri ve arama sonuçları — küçük resim veya simge artı meta veriler; render ile tüm satır bir bağlantıya dönüşür.

Item bileşeni, hemen hemen her tür içeriği barındırabilen sade bir flex kapsayıcısıdır. Bir başlık, açıklama ve eylemler göstermek için kullanın. Bir öğe listesi oluşturmak için ItemGroup bileşeniyle gruplayın.

Kurulum

pnpm dlx shadcn@latest add https://neobrutalism.com/r/base/item.json
npx shadcn@latest add https://neobrutalism.com/r/base/item.json
yarn dlx shadcn@latest add https://neobrutalism.com/r/base/item.json
bunx --bun shadcn@latest add https://neobrutalism.com/r/base/item.json

Aşağıdaki kodu kopyalayıp projenize yapıştırın.

components/ui/item.tsx
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { Slot } from "radix-ui"

import { cn } from "@/lib/utils"
import { Separator } from "@/components/ui/separator"

function ItemGroup({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      role="list"
      data-slot="item-group"
      className={cn(
        "group/item-group flex w-full flex-col gap-4 has-data-[size=sm]:gap-2.5 has-data-[size=xs]:gap-2",
        className
      )}
      {...props}
    />
  )
}

function ItemSeparator({
  className,
  ...props
}: React.ComponentProps<typeof Separator>) {
  return (
    <Separator
      data-slot="item-separator"
      orientation="horizontal"
      className={cn("my-2", className)}
      {...props}
    />
  )
}

const itemVariants = cva(
  "group/item flex w-full flex-wrap items-center rounded border-2 text-sm transition-colors duration-100 outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary [a]:transition-colors [a]:hover:bg-accent",
  {
    variants: {
      variant: {
        default: "border-transparent",
        outline: "border-border",
        muted: "border-transparent bg-muted/50",
      },
      size: {
        default: "gap-2.5 px-3 py-2.5",
        sm: "gap-2.5 px-3 py-2.5",
        xs: "gap-2 px-2.5 py-2 in-data-[slot=dropdown-menu-content]:p-0",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
)

function Item({
  className,
  variant = "default",
  size = "default",
  asChild = false,
  ...props
}: React.ComponentProps<"div"> &
  VariantProps<typeof itemVariants> & { asChild?: boolean }) {
  const Comp = asChild ? Slot.Root : "div"
  return (
    <Comp
      data-slot="item"
      data-variant={variant}
      data-size={size}
      className={cn(itemVariants({ variant, size, className }))}
      {...props}
    />
  )
}

const itemMediaVariants = cva(
  "flex shrink-0 items-center justify-center gap-2 group-has-data-[slot=item-description]/item:translate-y-0.5 group-has-data-[slot=item-description]/item:self-start [&_svg]:pointer-events-none",
  {
    variants: {
      variant: {
        default: "bg-transparent",
        icon: "[&_svg:not([class*='size-'])]:size-4",
        image:
          "size-10 overflow-hidden rounded-sm group-data-[size=sm]/item:size-8 group-data-[size=xs]/item:size-6 [&_img]:size-full [&_img]:object-cover",
      },
    },
    defaultVariants: {
      variant: "default",
    },
  }
)

function ItemMedia({
  className,
  variant = "default",
  ...props
}: React.ComponentProps<"div"> & VariantProps<typeof itemMediaVariants>) {
  return (
    <div
      data-slot="item-media"
      data-variant={variant}
      className={cn(itemMediaVariants({ variant, className }))}
      {...props}
    />
  )
}

function ItemContent({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="item-content"
      className={cn(
        "flex flex-1 flex-col gap-1 group-data-[size=xs]/item:gap-0 [&+[data-slot=item-content]]:flex-none",
        className
      )}
      {...props}
    />
  )
}

function ItemTitle({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="item-title"
      className={cn(
        "line-clamp-1 flex w-fit items-center gap-2 text-sm leading-snug font-medium underline-offset-4",
        className
      )}
      {...props}
    />
  )
}

function ItemDescription({ className, ...props }: React.ComponentProps<"p">) {
  return (
    <p
      data-slot="item-description"
      className={cn(
        "line-clamp-2 text-left text-sm leading-normal font-normal text-muted-foreground group-data-[size=xs]/item:text-xs [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
        className
      )}
      {...props}
    />
  )
}

function ItemActions({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="item-actions"
      className={cn("flex items-center gap-2", className)}
      {...props}
    />
  )
}

function ItemHeader({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="item-header"
      className={cn(
        "flex basis-full items-center justify-between gap-2",
        className
      )}
      {...props}
    />
  )
}

function ItemFooter({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="item-footer"
      className={cn(
        "flex basis-full items-center justify-between gap-2",
        className
      )}
      {...props}
    />
  )
}

export {
  Item,
  ItemMedia,
  ItemContent,
  ItemActions,
  ItemGroup,
  ItemSeparator,
  ItemTitle,
  ItemDescription,
  ItemHeader,
  ItemFooter,
}

İçe aktarma yollarını proje kurulumunuza göre güncelleyin.

Kullanım

import {
  Item,
  ItemActions,
  ItemContent,
  ItemDescription,
  ItemMedia,
  ItemTitle,
} from "@/components/ui/item"
<Item>
  <ItemMedia variant="icon">
    <Icon />
  </ItemMedia>
  <ItemContent>
    <ItemTitle>Title</ItemTitle>
    <ItemDescription>Description</ItemDescription>
  </ItemContent>
  <ItemActions>
    <Button>Action</Button>
  </ItemActions>
</Item>

Kompozisyon

Bir Item oluşturmak için aşağıdaki kompozisyonu kullanın:

ItemGroup
└── Item
    ├── ItemHeader
    ├── ItemMedia
    ├── ItemContent
    │   ├── ItemTitle
    │   └── ItemDescription
    ├── ItemActions
    └── ItemFooter

Item vs Field

Onay kutusu, giriş, radyo düğmesi veya select gibi bir form girişi göstermeniz gerekiyorsa Field kullanın.

Yalnızca başlık, açıklama ve eylemler gibi içerik göstermeniz gerekiyorsa Item kullanın.

Variant

Öğenin görsel stilini değiştirmek için variant prop'unu kullanın.

import { InboxIcon } from "lucide-react"

import {

Size

Öğenin boyutunu değiştirmek için size prop'unu kullanın. Kullanılabilir boyutlar default, sm ve xs'dir.

import { InboxIcon } from "lucide-react"

import {

Örnekler

Simge

Bir simge göstermek için ItemMedia'yı variant="icon" ile kullanın.

import { ShieldAlertIcon } from "lucide-react"

import { Button } from "@/components/ui/button"

Avatar

Bir avatar göstermek için ItemMedia'yı variant="avatar" ile kullanabilirsiniz.

import { Plus } from "lucide-react"

import {

Görsel

Bir görsel göstermek için ItemMedia'yı variant="image" ile kullanın.

import Image from "next/image"

import {

Grup

İlgili öğeleri bir arada gruplamak için ItemGroup kullanın.

import * as React from "react"
import { PlusIcon } from "lucide-react"

Üst Bilgi

Öğe içeriğinin üstüne bir üst bilgi eklemek için ItemHeader kullanın.

import Image from "next/image"

import {

Bağlantı

Öğeyi bir bağlantı olarak render etmek için render prop'unu kullanın. Hover ve focus durumları anchor öğesine uygulanır.

import { ChevronRightIcon, ExternalLinkIcon } from "lucide-react"

import {
<Item render={<a href="/dashboard" />}>
  <ItemMedia variant="icon">
    <HomeIcon />
  </ItemMedia>
  <ItemContent>
    <ItemTitle>Dashboard</ItemTitle>
    <ItemDescription>Overview of your account and activity.</ItemDescription>
  </ItemContent>
</Item>
"use client"

import { ChevronDownIcon } from "lucide-react"

RTL

Neobrutalism'de RTL desteğini etkinleştirmek için RTL yapılandırma kılavuzuna bakın.

"use client"

import * as React from "react"

Erişilebilirlik

Item yalnızca görseldir — kendine ait ARIA bağlantısı olmayan stillendirilmiş öğelerden oluşur; yine de ItemGroup role="list" olarak işlenir (liste yapısının duyurulması gerekiyorsa her Item öğesine role="listitem" ekleyin). Etkileşimli satırlar için tıklama işleyicisini div'e koymayın — yerel odak ve klavye davranışı kazanması için render prop'u aracılığıyla gerçek bir <a> veya <button> işleyin; görünür odak halkası zaten stillendirilmiştir. Dekoratif simge medyasını aria-hidden ile işaretleyin ve resim medyasına anlamlı alt metni verin.

API Referansı

Item

Medya, başlık, açıklama ve eylemlerle içerik göstermeye yarayan ana bileşen.

PropTypeDefault
variant"default" | "outline" | "muted""default"
size"default" | "sm" | "xs""default"
renderReact.ReactElement

ItemGroup

İlgili öğeleri tutarlı bir stille bir arada gruplayan bir kapsayıcı.

<ItemGroup>
  <Item />
  <Item />
</ItemGroup>

ItemSeparator

Bir gruptaki öğeler arasındaki bir ayırıcı.

<ItemGroup>
  <Item />
  <ItemSeparator />
  <Item />
</ItemGroup>

ItemMedia

Simge, görsel veya avatar gibi medya içeriği göstermek için ItemMedia kullanın.

PropTypeDefault
variant"default" | "icon" | "image""default"
<ItemMedia variant="icon">
  <Icon />
</ItemMedia>
<ItemMedia variant="image">
  <img src="..." alt="..." />
</ItemMedia>

ItemContent

Öğenin başlığını ve açıklamasını sarar.

<ItemContent>
  <ItemTitle>Title</ItemTitle>
  <ItemDescription>Description</ItemDescription>
</ItemContent>

ItemTitle

Öğenin başlığını gösterir.

<ItemTitle>Item Title</ItemTitle>

ItemDescription

Öğenin açıklamasını gösterir.

<ItemDescription>Item description</ItemDescription>

ItemActions

Eylem düğmeleri veya diğer etkileşimli öğeler için kapsayıcı.

<ItemActions>
  <Button>Action</Button>
</ItemActions>

ItemHeader

Öğe içeriğinin üstünde bir üst bilgi gösterir.

<Item>
  <ItemHeader>Header</ItemHeader>
  <ItemContent>...</ItemContent>
</Item>

ItemFooter

Öğe içeriğinin altında bir alt bilgi gösterir.

<Item>
  <ItemContent>...</ItemContent>
  <ItemFooter>Footer</ItemFooter>
</Item>