פריט
שורת תוכן גמישה הכוללת משבצות למדיה, כותרת, תיאור ופעולה עבור עדכוני התראות, רשימות הגדרות ושורות קבצים — בסגנון ניאו-ברוטליסטי.
import { BadgeCheckIcon, ChevronRightIcon } from "lucide-react"
import { Button } from "@/components/ui/button"ה-item הוא שורת flex עם חריצים בשם — media, content, actions — לדפוס "רשימת דברים" שכל אפליקציה מגיעה אליו. אין פרימיטיב headless מתחת, רק אלמנטים מעוצבים (ועוד useRender של Base UI ל-prop ה-render), בגמר המתכון הנאו-ברוטליסטי: מסגרת 2px — גלויה בווריאנט outline — וטבעת פוקוס בהיסט חד.
השתמשו בו כש:
- פידי התראות ופעילות — avatar, הודעה, חותמת זמן וכפתור סגירה או פעולה לכל שורה.
- רשימות הגדרות ואינטגרציות — אייקון, שם, שורת סטטוס, עם switch או כפתור בקצה הנגרר.
- דפדפני קבצים ותוצאות חיפוש — תמונה ממוזערת או אייקון ועוד מטא-דאטה, כל השורה קישור דרך
render.
רכיב Item הוא מכל flex פשוט שיכול להכיל כמעט כל סוג תוכן. השתמשו בו כדי להציג כותרת, תיאור ופעולות. קבצו אותו עם רכיב ItemGroup כדי ליצור רשימת פריטים.
התקנה#
pnpm dlx shadcn@latest add https://neobrutalism.com/r/base/item.jsonnpx shadcn@latest add https://neobrutalism.com/r/base/item.jsonyarn dlx shadcn@latest add https://neobrutalism.com/r/base/item.jsonbunx --bun shadcn@latest add https://neobrutalism.com/r/base/item.json
העתיקו והדביקו את הקוד הבא לתוך הפרויקט שלכם.
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,
}
עדכנו את נתיבי הייבוא כך שיתאימו למבנה הפרויקט שלכם.
שימוש#
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>קומפוזיציה#
השתמשו בקומפוזיציה הבאה כדי לבנות Item:
ItemGroup
└── Item
├── ItemHeader
├── ItemMedia
├── ItemContent
│ ├── ItemTitle
│ └── ItemDescription
├── ItemActions
└── ItemFooterItem vs Field#
השתמשו ב-Field אם אתם צריכים להציג שדה טופס כמו תיבת סימון, שדה קלט, כפתור רדיו או select.
אם אתם צריכים רק להציג תוכן כמו כותרת, תיאור ופעולות, השתמשו ב-Item.
וריאנט#
השתמשו ב-prop בשם variant כדי לשנות את הסגנון החזותי של הפריט.
import { InboxIcon } from "lucide-react"
import {גודל#
השתמשו ב-prop בשם size כדי לשנות את גודל הפריט. הגדלים הזמינים הם default, sm ו-xs.
import { InboxIcon } from "lucide-react"
import {דוגמאות#
אייקון#
השתמשו ב-ItemMedia עם variant="icon" כדי להציג אייקון.
import { ShieldAlertIcon } from "lucide-react"
import { Button } from "@/components/ui/button"Avatar#
אפשר להשתמש ב-ItemMedia עם variant="avatar" כדי להציג אווטאר.
import { Plus } from "lucide-react"
import {תמונה#
השתמשו ב-ItemMedia עם variant="image" כדי להציג תמונה.
import Image from "next/image"
import {קבוצה#
השתמשו ב-ItemGroup כדי לקבץ יחד פריטים קשורים.
import * as React from "react"
import { PlusIcon } from "lucide-react"
כותרת#
השתמשו ב-ItemHeader כדי להוסיף כותרת מעל תוכן הפריט.
import Image from "next/image"
import {קישור#
השתמשו ב-prop בשם render כדי לרנדר את הפריט כקישור. מצבי הריחוף והפוקוס יוחלו על אלמנט העוגן.
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>Dropdown#
"use client"
import { ChevronDownIcon } from "lucide-react"RTL#
כדי להפעיל RTL ב-Neobrutalism, עיינו במדריך ההגדרה של RTL.
"use client"
import * as React from "react"נגישות#
ה-item הוא מצגתי — אלמנטים מעוצבים בלי חיווט ARIA משלהם, אם כי ItemGroup מרנדר role="list" (הוסיפו role="listitem" לכל Item אם מבנה הרשימה צריך להיות מוכרז). לשורות אינטראקטיביות, אל תשימו click handler על ה-div — רנדרו <a> או <button> אמיתי דרך prop ה-render כדי לקבל פוקוס והתנהגות מקלדת מקוריים; טבעת הפוקוס הגלויה כבר מעוצבת. סמנו מדיית אייקון דקורטיבית ב-aria-hidden ותנו למדיית תמונה alt משמעותי.
תיעוד ה-API#
Item#
הרכיב המרכזי להצגת תוכן עם מדיה, כותרת, תיאור ופעולות.
| Prop | Type | Default |
|---|---|---|
variant | "default" | "outline" | "muted" | "default" |
size | "default" | "sm" | "xs" | "default" |
render | React.ReactElement |
ItemGroup#
מכל שמקבץ יחד פריטים קשורים בעיצוב אחיד.
<ItemGroup>
<Item />
<Item />
</ItemGroup>ItemSeparator#
מפריד בין פריטים בקבוצה.
<ItemGroup>
<Item />
<ItemSeparator />
<Item />
</ItemGroup>ItemMedia#
השתמשו ב-ItemMedia כדי להציג תוכן מדיה כמו אייקונים, תמונות או אווטארים.
| Prop | Type | Default |
|---|---|---|
variant | "default" | "icon" | "image" | "default" |
<ItemMedia variant="icon">
<Icon />
</ItemMedia><ItemMedia variant="image">
<img src="..." alt="..." />
</ItemMedia>ItemContent#
עוטף את הכותרת והתיאור של הפריט.
<ItemContent>
<ItemTitle>Title</ItemTitle>
<ItemDescription>Description</ItemDescription>
</ItemContent>ItemTitle#
מציג את כותרת הפריט.
<ItemTitle>Item Title</ItemTitle>ItemDescription#
מציג את תיאור הפריט.
<ItemDescription>Item description</ItemDescription>ItemActions#
מכל לכפתורי פעולה או אלמנטים אינטראקטיביים אחרים.
<ItemActions>
<Button>Action</Button>
</ItemActions>ItemHeader#
מציג כותרת מעל תוכן הפריט.
<Item>
<ItemHeader>Header</ItemHeader>
<ItemContent>...</ItemContent>
</Item>ItemFooter#
מציג כותרת תחתונה מתחת לתוכן הפריט.
<Item>
<ItemContent>...</ItemContent>
<ItemFooter>Footer</ItemFooter>
</Item>