컴포넌트
칩
태그와 필터에 쓰는 알약이에요. 주소가 있으면 링크, 없으면 라벨이에요.
- #arc
- #component
- #chip
- #tag
- #filter
- #pill
- #link
1280px
설치
이 명령 한 줄이면 필요한 파일과 패키지가 함께 설치돼요.
터미널터미널터미널bash
npx shadcn@latest add https://dsui.vercel.app/r/chip.json파일
components/arc/chip/chip.module.csscomponents/arc/chip/chip.tsx
함께 오는 항목
chip.module.csschip.module.csschip.module.csscss
.list { display: flex; flex-wrap: wrap; margin: 0; padding: 0; gap: var(--space-2); list-style: none; }
.chip { display: inline-flex; min-height: var(--control-height-sm); align-items: center; gap: var(--space-1); border: 1px solid var(--border); border-radius: var(--radius-pill); padding: 0 14px; background: var(--surface); color: var(--text-secondary); font-size: var(--text-sm); font-weight: 500; line-height: 20px; text-decoration: none; white-space: nowrap; }
.sm { min-height: 26px; padding: 0 10px; font-size: var(--text-xs); }
/* The number is context, not the label, so it stays a step quieter than the word it follows. */
.count { color: var(--text-muted); font-weight: 400; font-variant-numeric: tabular-nums; }
.link { -webkit-tap-highlight-color: transparent; transition: color var(--duration-fast) var(--ease-standard), background var(--duration-fast) var(--ease-standard), border-color var(--duration-fast) var(--ease-standard), transform var(--duration-fast) var(--ease-standard); }
@media (hover: hover) and (pointer: fine) {
.link:hover { border-color: var(--border-strong); color: var(--foreground); }
}
.link:active { background: var(--surface-muted); border-color: transparent; color: var(--foreground); transform: scale(.97); }
/* The current filter is told apart by a filled face and a stronger edge, not by color alone. */
.link[aria-current="true"] { border-color: transparent; background: var(--accent-subtle); color: var(--foreground); }
.link[aria-current="true"] .count { color: var(--accent); }
@media (hover: hover) and (pointer: fine) {
.link[aria-current="true"]:hover { border-color: transparent; background: var(--accent-subtle); }
}
@media (prefers-reduced-motion: reduce) { .link { transition: none; } .link:active { transform: none; } }
chip.tsxchip.tsxchip.tsxtsx
"use client";
import type { HTMLAttributes, ReactNode } from "react";
import { ArcLink } from "../lib/arc-provider";
import { playUiSound } from "../sound/play";
import styles from "./chip.module.css";
export type ChipSize = "sm" | "md";
export interface ChipProps {
/** Makes the chip a link. Without it the chip is a plain label. */
href?: string;
/** Marks the filter the reader is on. Only meaningful with `href`. */
current?: boolean;
size?: ChipSize;
/** A quiet number after the label, such as how many items a tag holds. */
count?: number | string;
/** The language of the label, when it differs from the page. */
lang?: string;
className?: string;
children: ReactNode;
}
/**
* A pill for a tag or a filter. With `href` it goes somewhere; without one it is a label and gets no hover or press,
* because nothing happens when you touch it. For chips that are *chosen* rather than followed — a filter that changes
* the list in place — use ChipGroup, which keeps selection and arrow-key movement.
*/
export function Chip({ href, current = false, size = "md", count, lang, className, children }: ChipProps) {
const classes = [styles.chip, styles[size], className].filter(Boolean).join(" ");
const content = (
<>
{children}
{count === undefined ? null : <span className={styles.count}>{count}</span>}
</>
);
if (!href) {
return <span className={classes} lang={lang}>{content}</span>;
}
return (
<ArcLink
href={href}
lang={lang}
className={[classes, styles.link].join(" ")}
aria-current={current ? "true" : undefined}
onClick={() => playUiSound("tick")}
>
{content}
</ArcLink>
);
}
export interface ChipListProps extends HTMLAttributes<HTMLUListElement> {
/** The accessible name of the row, e.g. "태그". */
label?: string;
children: ReactNode;
}
/** A wrapping row of chips as a real list, so a screen reader announces how many there are. Wrap each Chip in an `<li>`. */
export function ChipList({ className, label, children, ...props }: ChipListProps) {
return (
<ul {...props} aria-label={label} className={[styles.list, className].filter(Boolean).join(" ")}>
{children}
</ul>
);
}
Chip.displayName = "Chip";
ChipList.displayName = "ChipList";
export default Chip;