Components
Breadcrumb
A breadcrumb trail with separators and an ellipsis. Purely presentational.
Installation
npx kosmesis add breadcrumbpnpm dlx kosmesis add breadcrumbyarn dlx kosmesis add breadcrumbbunx kosmesis add breadcrumbCopy and paste the following code into your project.
import { StatelessComponent } from "@praxisjs/core";import { Component } from "@praxisjs/decorators";import type { Children } from "@praxisjs/shared";import { cn } from "@/lib/utils";export interface BreadcrumbProps { class?: string; "aria-label"?: string; children?: Children;}/** Purely presentational — no Morphos equivalent, same as upstream shadcn/ui. */@Component()export class Breadcrumb extends StatelessComponent<BreadcrumbProps> { render() { const { class: cls, "aria-label": ariaLabel = "breadcrumb", children } = this.props; return ( <nav aria-label={ariaLabel} class={cls}> {children} </nav> ); }}export interface BreadcrumbListProps { class?: string; children?: Children;}@Component()export class BreadcrumbList extends StatelessComponent<BreadcrumbListProps> { render() { const { class: cls, children } = this.props; return ( <ol class={cn( "flex flex-wrap items-center gap-1.5 text-sm break-words text-muted-foreground sm:gap-2.5", cls, )} > {children} </ol> ); }}@Component()export class BreadcrumbItem extends StatelessComponent<BreadcrumbListProps> { render() { const { class: cls, children } = this.props; return <li class={cn("inline-flex items-center gap-1.5", cls)}>{children}</li>; }}export interface BreadcrumbLinkProps { href?: string; onClick?: (event: MouseEvent) => void; class?: string; children?: Children;}@Component()export class BreadcrumbLink extends StatelessComponent<BreadcrumbLinkProps> { render() { const { href, onClick, class: cls, children } = this.props; return ( <a href={href} onClick={onClick} class={cn("transition-colors hover:text-foreground", cls)}> {children} </a> ); }}@Component()export class BreadcrumbPage extends StatelessComponent<BreadcrumbListProps> { render() { const { class: cls, children } = this.props; return ( <span role="link" aria-disabled={"true" as const} aria-current="page" class={cn("font-normal text-foreground", cls)}> {children} </span> ); }}@Component()export class BreadcrumbSeparator extends StatelessComponent<BreadcrumbListProps> { render() { const { class: cls, children } = this.props; return ( <li role="presentation" aria-hidden={"true" as const} class={cn("[&>svg]:size-3.5", cls)}> {children ?? "/"} </li> ); }}@Component()export class BreadcrumbEllipsis extends StatelessComponent<{ class?: string }> { render() { const { class: cls } = this.props; return ( <span role="presentation" aria-hidden={"true" as const} class={cn("flex size-9 items-center justify-center", cls)}> … </span> ); }}Install the following dependencies:
npm install @praxisjs/csspnpm add @praxisjs/cssyarn add @praxisjs/cssbun add @praxisjs/cssCopy and paste the following code into your project.
import { StatelessComponent } from "@praxisjs/core";import { cx, Stylesheet, Styled, tokenVars } from "@praxisjs/css";import { Component } from "@praxisjs/decorators";import type { Children } from "@praxisjs/shared";import { KosmesisTokens } from "@/lib/kosmesis-theme";const t = tokenVars(KosmesisTokens);class BreadcrumbStyles extends Stylesheet { $list = this.css({ display: "flex", flexWrap: "wrap", alignItems: "center", gap: "0.375rem", fontSize: "0.875rem", wordBreak: "break-word", color: t.mutedForeground, }).media("(min-width: 640px)", { gap: "0.625rem" }); $item = this.css({ display: "inline-flex", alignItems: "center", gap: "0.375rem" }); $link = this.css({ transition: "color 120ms ease" }).hover({ color: t.foreground }); $page = this.css({ fontWeight: 400, color: t.foreground }); $separator = this.css({}).on("& > svg", { width: "0.875rem", height: "0.875rem" }); $ellipsis = this.css({ display: "flex", width: "2.25rem", height: "2.25rem", alignItems: "center", justifyContent: "center" });}export interface BreadcrumbProps { class?: string; "aria-label"?: string; children?: Children;}/** Purely presentational — no Morphos equivalent, same as upstream shadcn/ui. */@Component()export class Breadcrumb extends StatelessComponent<BreadcrumbProps> { render() { const { class: cls, "aria-label": ariaLabel = "breadcrumb", children } = this.props; return ( <nav aria-label={ariaLabel} class={cls}> {children} </nav> ); }}export interface BreadcrumbListProps { class?: string; children?: Children;}@Component()export class BreadcrumbList extends StatelessComponent<BreadcrumbListProps> { @Styled(BreadcrumbStyles) $s!: BreadcrumbStyles; render() { const { class: cls, children } = this.props; return <ol class={cx(this.$s.$list, cls)}>{children}</ol>; }}@Component()export class BreadcrumbItem extends StatelessComponent<BreadcrumbListProps> { @Styled(BreadcrumbStyles) $s!: BreadcrumbStyles; render() { const { class: cls, children } = this.props; return <li class={cx(this.$s.$item, cls)}>{children}</li>; }}export interface BreadcrumbLinkProps { href?: string; onClick?: (event: MouseEvent) => void; class?: string; children?: Children;}@Component()export class BreadcrumbLink extends StatelessComponent<BreadcrumbLinkProps> { @Styled(BreadcrumbStyles) $s!: BreadcrumbStyles; render() { const { href, onClick, class: cls, children } = this.props; return ( <a href={href} onClick={onClick} class={cx(this.$s.$link, cls)}> {children} </a> ); }}@Component()export class BreadcrumbPage extends StatelessComponent<BreadcrumbListProps> { @Styled(BreadcrumbStyles) $s!: BreadcrumbStyles; render() { const { class: cls, children } = this.props; return ( <span role="link" aria-disabled={"true" as const} aria-current="page" class={cx(this.$s.$page, cls)}> {children} </span> ); }}@Component()export class BreadcrumbSeparator extends StatelessComponent<BreadcrumbListProps> { @Styled(BreadcrumbStyles) $s!: BreadcrumbStyles; render() { const { class: cls, children } = this.props; return ( <li role="presentation" aria-hidden={"true" as const} class={cx(this.$s.$separator, cls)}> {children ?? "/"} </li> ); }}@Component()export class BreadcrumbEllipsis extends StatelessComponent<{ class?: string }> { @Styled(BreadcrumbStyles) $s!: BreadcrumbStyles; render() { const { class: cls } = this.props; return ( <span role="presentation" aria-hidden={"true" as const} class={cx(this.$s.$ellipsis, cls)}> … </span> ); }}Examples
Usage
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@/components/ui/breadcrumb";
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink href="/">Home</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbLink href="/components">Components</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>Breadcrumb</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>Props
Breadcrumb
| Prop | Type | Default |
|---|---|---|
aria-label | string | "breadcrumb" |
BreadcrumbLink
| Prop | Type | Default |
|---|---|---|
href | string | — |
onClick | (event: MouseEvent) => void | — |
BreadcrumbSeparator
| Prop | Type | Default |
|---|---|---|
children | Children | "/" — overrides the default separator glyph |
BreadcrumbList, BreadcrumbItem, BreadcrumbPage, and BreadcrumbEllipsis take no props
beyond class/children.