Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tangy-groups-listen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'commerce-toolkit': minor
---

Added Breadcrumbs component
12 changes: 6 additions & 6 deletions .cursor/commands/tailwind-downgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ Below is an example:

- `<div class="bg-(--brand-color)"></div>` → `<div class="bg-[--brand-color]"></div>`

Sometimes you may come across examples that use a fallback value for CSS variables. When that's the case, you'll need to change "()" with "[]" and also wrap ANY CSS variable fallback with the hsl color function (this applies to all color variables like `--background`, `--foreground`, `--contrast-*`, etc.).
Sometimes you may come across examples that use a fallback value for CSS variables. When that's the case, you'll need to change "()" with "[]".

Below are examples:

- `bg-(--dropdown-menu-background,var(--background))` → `bg-[var(--dropdown-menu-background,hsl(var(--background)))]`
- `border-(--dropdown-menu-border,var(--contrast-100))` → `border-[var(--dropdown-menu-border,hsl(var(--contrast-100)))]`
- `text-(--custom-text,var(--foreground))` → `text-[var(--custom-text,hsl(var(--foreground)))]`
- `bg-(--dropdown-menu-background,var(--background))` → `bg-[var(--dropdown-menu-background,var(--background))]`
- `border-(--dropdown-menu-border,var(--contrast-100))` → `border-[var(--dropdown-menu-border,var(--contrast-100))]`
- `text-(--custom-text,var(--foreground))` → `text-[var(--custom-text,var(--foreground))]`

## Highlight and Shadow variants

Expand All @@ -50,5 +50,5 @@ The following CSS variables are included in the Tailwind 4 version:

However, in Tailwind 3 we do not have these CSS variables defined. Therefore, when you come across a class name that uses one of these CSS variables it needs to be converted for Tailwind 3 like this:

- `bg-(--alert-success-background,var(--success-highlight))` → `bg-[var(--alert-success-background,color-mix(in_oklab,_hsl(var(--success)),_white_75%))]`
- `text-(--form-status-light-text-success,var(--success-shadow))` → `text-[var(--form-status-light-text-success,color-mix(in_oklab,hsl(var(--success)),black_75%))]`
- `bg-(--alert-success-background,var(--success-highlight))` → `bg-[var(--alert-success-background,color-mix(in_oklab,var(--success),white_75%))]`
- `text-(--form-status-light-text-success,var(--success-shadow))` → `text-[var(--form-status-light-text-success,color-mix(in_oklab,var(--success),black_75%))]`
10 changes: 10 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,16 @@
"default": "./dist/banner.cjs"
}
},
"./breadcrumbs": {
"import": {
"types": "./dist/components/breadcrumbs/primitives.d.ts",
"default": "./dist/breadcrumbs.js"
},
"require": {
"types": "./dist/components/breadcrumbs/primitives.d.ts",
"default": "./dist/breadcrumbs.cjs"
}
},
"./button-radio-group": {
"import": {
"types": "./dist/components/button-radio-group/primitives.d.ts",
Expand Down
147 changes: 0 additions & 147 deletions src/components/accordion/archive/archive-accordion.tsx

This file was deleted.

69 changes: 69 additions & 0 deletions src/components/breadcrumbs/breadcrumbs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import type { ReactNode } from 'react';

import { AnimatedUnderline } from '@/components/animated-underline';
import * as BreadcrumbsPrimitive from '@/components/breadcrumbs';
import { cn } from '@/lib';

interface Breadcrumb {
label: string;
href: string;
asChild?: boolean;
children?: ReactNode;
}

export interface BreadcrumbsProps {
breadcrumbs: Breadcrumb[];
className?: string;
ariaLabel?: string;
icon?: {
asChild?: boolean;
children?: ReactNode;
};
}

/**
* This component supports various CSS variables for theming. Here's a comprehensive list, along
* with their default values:
*
* ```css
* :root {
* --breadcrumbs-font-family: var(--font-family-body);
* --breadcrumbs-primary-text: var(--foreground);
* --breadcrumbs-secondary-text: var(--contrast-500);
* --breadcrumbs-icon: var(--contrast-500);
* }
* ```
*/
export function Breadcrumbs({
breadcrumbs,
className,
ariaLabel = 'Breadcrumb',
icon,
}: BreadcrumbsProps) {
return (
<BreadcrumbsPrimitive.Root aria-label={ariaLabel} className={cn(className)}>
<BreadcrumbsPrimitive.List>
{breadcrumbs.map(({ label, href, asChild, children }, index) => {
if (index < breadcrumbs.length - 1) {
return (
<BreadcrumbsPrimitive.Item key={href}>
<BreadcrumbsPrimitive.Link asChild={asChild === true} href={href}>
{asChild === true ? children : <AnimatedUnderline>{label}</AnimatedUnderline>}
</BreadcrumbsPrimitive.Link>
<BreadcrumbsPrimitive.Icon asChild={icon?.asChild === true}>
{icon?.children}
</BreadcrumbsPrimitive.Icon>
</BreadcrumbsPrimitive.Item>
);
}

return (
<BreadcrumbsPrimitive.Item key={href}>
<BreadcrumbsPrimitive.Current>{label}</BreadcrumbsPrimitive.Current>
</BreadcrumbsPrimitive.Item>
);
})}
</BreadcrumbsPrimitive.List>
</BreadcrumbsPrimitive.Root>
);
}
2 changes: 2 additions & 0 deletions src/components/breadcrumbs/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { Breadcrumbs, type BreadcrumbsProps } from '@/components/breadcrumbs/breadcrumbs';
export * from '@/components/breadcrumbs/primitives';
24 changes: 24 additions & 0 deletions src/components/breadcrumbs/primitives.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export {
BreadcrumbsRoot as Root,
type BreadcrumbsRootProps as RootProps,
} from '@/components/breadcrumbs/primitives/breadcrumbs-root';
export {
BreadcrumbsList as List,
type BreadcrumbsListProps as ListProps,
} from '@/components/breadcrumbs/primitives/breadcrumbs-list';
export {
BreadcrumbsItem as Item,
type BreadcrumbsItemProps as ItemProps,
} from '@/components/breadcrumbs/primitives/breadcrumbs-item';
export {
BreadcrumbsLink as Link,
type BreadcrumbsLinkProps as LinkProps,
} from '@/components/breadcrumbs/primitives/breadcrumbs-link';
export {
BreadcrumbsIcon as Icon,
type BreadcrumbsIconProps as IconProps,
} from '@/components/breadcrumbs/primitives/breadcrumbs-icon';
export {
BreadcrumbsCurrent as Current,
type BreadcrumbsCurrentProps as CurrentProps,
} from '@/components/breadcrumbs/primitives/breadcrumbs-current';
20 changes: 20 additions & 0 deletions src/components/breadcrumbs/primitives/breadcrumbs-current.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { ComponentProps } from 'react';

import { cn } from '@/lib';

export type BreadcrumbsCurrentProps = ComponentProps<'span'>;

export function BreadcrumbsCurrent({ className, children, ...props }: BreadcrumbsCurrentProps) {
return (
<span
aria-current="page"
aria-disabled="true"
className={cn('text-[var(--breadcrumbs-secondary-text,var(--contrast-500))]', className)}
data-slot="breadcrumbs-current"
role="link"
{...props}
>
{children}
</span>
);
}
37 changes: 37 additions & 0 deletions src/components/breadcrumbs/primitives/breadcrumbs-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use client';

import { Slot } from '@radix-ui/react-slot';
import { ChevronRight } from 'lucide-react';
import type { ReactNode } from 'react';

import { cn } from '@/lib';

export interface BreadcrumbsIconProps {
asChild?: boolean;
className?: string;
children?: ReactNode;
}

export function BreadcrumbsIcon({ asChild = false, className, children }: BreadcrumbsIconProps) {
if (asChild) {
return (
<Slot
aria-hidden="true"
className={cn('size-5 text-[var(--breadcrumbs-icon,var(--contrast-500))]', className)}
data-slot="breadcrumbs-icon"
>
{children}
</Slot>
);
}

return (
<ChevronRight
absoluteStrokeWidth
aria-hidden="true"
className={cn('size-5 text-[var(--breadcrumbs-icon,var(--contrast-500))]', className)}
data-slot="breadcrumbs-icon"
strokeWidth={1.5}
/>
);
}
17 changes: 17 additions & 0 deletions src/components/breadcrumbs/primitives/breadcrumbs-item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { ComponentProps } from 'react';

import { cn } from '@/lib';

export type BreadcrumbsItemProps = ComponentProps<'li'>;

export function BreadcrumbsItem({ className, children, ...props }: BreadcrumbsItemProps) {
return (
<li
className={cn('inline-flex items-center gap-x-1.5', className)}
data-slot="breadcrumbs-item"
{...props}
>
{children}
</li>
);
}
27 changes: 27 additions & 0 deletions src/components/breadcrumbs/primitives/breadcrumbs-link.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use client';

import { Slot } from '@radix-ui/react-slot';
import type { ComponentProps } from 'react';

import { cn } from '@/lib';

export interface BreadcrumbsLinkProps extends ComponentProps<'a'> {
asChild?: boolean;
}

export function BreadcrumbsLink({ asChild = false, className, ...props }: BreadcrumbsLinkProps) {
const Component = asChild ? Slot : 'a';

return (
<Component
className={cn(
'group/underline text-[var(--breadcrumbs-primary-text,var(--foreground))]',
// Focus-visible state
'focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2',
className,
)}
data-slot="breadcrumbs-link"
{...props}
/>
);
}
Loading