All components
Breadcrumb
Hierarchical navigation trails with plain and underline link styles in three sizes.
12345678910111213141516171819202122232425import { Breadcrumb } from "#components/ui/base-ui/breadcrumb.tsx";
export function Example() { return ( // variant/size resolve once on Root; every part inherits them via context <Breadcrumb.Root aria-label="Breadcrumb" variant="plain" size="md"> <Breadcrumb.List> <Breadcrumb.Item> {/* composition: render swaps the anchor for your router's link */} <Breadcrumb.Link href="/">Home</Breadcrumb.Link> </Breadcrumb.Item> <Breadcrumb.Separator aria-hidden="true">/</Breadcrumb.Separator> <Breadcrumb.Item> <Breadcrumb.Link href="/projects">Projects</Breadcrumb.Link> </Breadcrumb.Item> <Breadcrumb.Separator aria-hidden="true">/</Breadcrumb.Separator> <Breadcrumb.Item> <Breadcrumb.CurrentLink aria-current="page"> Settings </Breadcrumb.CurrentLink> </Breadcrumb.Item> </Breadcrumb.List> </Breadcrumb.Root> );}Variants
plain
underline
Sizes
sm
md
lg
Install
1npx stylexblocks add breadcrumbSource
components/ui/base-ui/breadcrumb.tsx
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209import * as stylex from "@stylexjs/stylex";import { createSlotRecipeContext } from "#lib/base-ui/create-slot-recipe-context.tsx";import { defineSlotRecipe } from "#lib/recipe-props.ts";import { breadcrumbSeparatorIconVars, breadcrumbEllipsisIconVars,} from '@stylexblocks/ui/slot-recipes/breadcrumb.stylex.ts'
import { colorPalette } from '@stylexblocks/ui/color-palette.stylex.ts'import { fontSizes } from '@stylexblocks/ui/tokens/font-sizes.stylex.ts'import { lineHeights } from '@stylexblocks/ui/tokens/line-heights.stylex.ts'import { semanticColors } from '@stylexblocks/ui/semantic-tokens/colors.stylex.ts'import { semanticRadii } from '@stylexblocks/ui/semantic-tokens/radii.stylex.ts'import { spacing } from '@stylexblocks/ui/tokens/spacing.stylex.ts'
const breadcrumbList = stylex.create({ base: { display: 'flex', alignItems: 'center', wordBreak: 'break-word', color: semanticColors.fgMuted, },})
const breadcrumbListSizes = stylex.create({ sm: { gap: spacing._1, fontSize: fontSizes.xs, lineHeight: lineHeights.xs, }, md: { gap: spacing._1_5, fontSize: fontSizes.sm, lineHeight: lineHeights.sm, }, lg: { gap: spacing._2, fontSize: fontSizes.md, lineHeight: lineHeights.md, },})
const breadcrumbLink = stylex.create({ base: { outline: 'none', textDecoration: 'none', borderRadius: semanticRadii.control, outlineWidth: { default: null, ':focus': '1px', }, outlineOffset: { default: null, ':focus': '2px', }, outlineStyle: { default: null, ':focus': 'solid', }, outlineColor: { default: null, ':focus': colorPalette.focusRing, }, display: 'inline-flex', alignItems: 'center', gap: spacing._2, },})
const breadcrumbLinkVariants = stylex.create({ underline: { color: colorPalette.fg, textDecoration: 'underline', textUnderlineOffset: '0.2em', textDecorationColor: { default: `color-mix(in oklch, ${colorPalette.fg} 20%, transparent)`, ':hover': colorPalette.fg, }, }, plain: { color: { default: semanticColors.fgMuted, ':hover': semanticColors.fg, }, },})
const breadcrumbItem = stylex.create({ base: { display: 'inline-flex', alignItems: 'center', },})
const breadcrumbSeparator = stylex.create({ base: { color: semanticColors.fgMuted, opacity: 0.8, [breadcrumbSeparatorIconVars.width]: '1em', [breadcrumbSeparatorIconVars.height]: '1em', },})
const breadcrumbSeparatorIcon = stylex.create({ base: { width: breadcrumbSeparatorIconVars.width, height: breadcrumbSeparatorIconVars.height, },})
const breadcrumbEllipsis = stylex.create({ base: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', [breadcrumbEllipsisIconVars.width]: '1em', [breadcrumbEllipsisIconVars.height]: '1em', },})
const breadcrumbEllipsisIcon = stylex.create({ base: { width: breadcrumbEllipsisIconVars.width, height: breadcrumbEllipsisIconVars.height, },})
const breadcrumbCurrentLink = stylex.create({ base: {},})
const breadcrumbCurrentLinkVariants = stylex.create({ underline: { color: colorPalette.fg, }, plain: { color: semanticColors.fg, },})
export type BreadcrumbVariant = keyof typeof breadcrumbLinkVariants
export type BreadcrumbSize = keyof typeof breadcrumbListSizes
// The generated recipe styles content, not chrome: the list keeps the// browser's list semantics, so the component resets them here.const breadcrumbStatic = stylex.create({ list: { listStyle: "none", margin: 0, padding: 0 },});
// defineSlotRecipe validates the shape at compile time: default values must// exist in the axis maps, and slot keys must be `styles` or a declared axisconst breadcrumbSlotRecipe = defineSlotRecipe({ slots: { list: { styles: breadcrumbList, sizes: breadcrumbListSizes }, item: { styles: breadcrumbItem }, link: { styles: breadcrumbLink, variants: breadcrumbLinkVariants }, currentLink: { styles: breadcrumbCurrentLink, variants: breadcrumbCurrentLinkVariants, }, separator: { styles: breadcrumbSeparator }, separatorIcon: { styles: breadcrumbSeparatorIcon }, ellipsis: { styles: breadcrumbEllipsis }, ellipsisIcon: { styles: breadcrumbEllipsisIcon }, }, defaultVariants: { variant: "plain", size: "md" },});
/** * Breadcrumb — recipe and component colocated in one file. `<Breadcrumb.Root * variant size>` resolves the recipe once and every part inherits it through * context; each part composes via `render`. Give Root an `aria-label` * ("Breadcrumb") and mark the current page with `aria-current="page"` on * `CurrentLink`; Separator renders `aria-hidden` decoration. */const { withProvider, withContext } = createSlotRecipeContext(breadcrumbSlotRecipe);
export const Breadcrumb = { // Root is the nav landmark: unstyled itself, it only provides the variants Root: withProvider("nav", () => [], "BreadcrumbRoot"), List: withContext( "ol", (v) => [ breadcrumbList.base, breadcrumbListSizes[v.size], breadcrumbStatic.list, ], "BreadcrumbList", ), Item: withContext("li", "item", "BreadcrumbItem"), Link: withContext("a", "link", "BreadcrumbLink"), CurrentLink: withContext("span", "currentLink", "BreadcrumbCurrentLink"), Separator: withContext( "li", () => [breadcrumbItem.base, breadcrumbSeparator.base], "BreadcrumbSeparator", ), SeparatorIcon: withContext("svg", "separatorIcon", "BreadcrumbSeparatorIcon"), Ellipsis: withContext( "li", () => [breadcrumbItem.base, breadcrumbEllipsis.base], "BreadcrumbEllipsis", ), EllipsisIcon: withContext("svg", "ellipsisIcon", "BreadcrumbEllipsisIcon"),};