Skip to content

New: AI agent integration via Model Context Protocol (MCP).Learn more

UI component library

The storefront’s UI lives in the @evolve-storefront/ui package (frontend/packages/ui/) and is organized into two tiers: a small set of base primitives and a larger set of commerce-specific components built on top of them.

  • Directorypackages/ui/src/
    • Directorybase/ ~16 primitives (Button, Input, Select, …)
    • Directorycomponents/ ~30 commerce components (ProductCard, Navigation, …)
    • Directoryhelpers/ Utility functions (cn, typography)
    • global.css Design tokens + Tailwind theme (at package root)

These are low-level building blocks with no commerce knowledge. They wrap Radix UI and React Aria (react-aria-components) for accessibility and use CVA for variant management.

alert-dialog button checkbox container icon input label radio radio-group range-slider ribbon select skeleton switch text-area typography

Higher-level components that compose base primitives with business logic. Examples include product cards, cart line items, checkout summaries, and navigation.

Product display: product-card product-carousel product-gallery product-grid product-header product-list product-specifications product-stock-info reviews-stars

Checkout & cart: checkout-summary quantity-selector address-card delivery-methods line-item summary order-status-indicator shopping-list

Content & data: catalog-card catalog-carousel content-teaser data-table table markdown-container usp-list

Navigation & layout: accordion breadcrumbs card footer hero navigation

Modals & feedback: dialog toaster tooltip

Variant selectors: variant-selectors (button, color, and image selector sub-components)

Components declare their visual variants with Class Variance Authority:

import { cva } from "class-variance-authority";
export const buttonVariants = cva(
"inline-flex cursor-pointer items-center justify-center …",
{
variants: {
variant: {
primary: "bg-orange-500 font-bold text-black …",
secondary: "bg-black font-bold text-white …",
tertiary: "text-gray-600 hover:bg-gray-50 …",
link: "text-black hover:text-gray-500 …",
outline: "bg-white font-semibold ring-2 ring-black …",
},
size: {
lg: "gap-lg px-4 py-[10px] text-sm",
xl: "gap-sm px-4 py-2.5 text-sm md:px-6 md:py-3 md:text-base",
xxl: "gap-sm px-4 py-2.5 text-sm md:px-6 md:py-3 md:text-base",
},
},
defaultVariants: { variant: "primary", size: "xxl" },
}
);

Base components use Radix UI and React Aria Components primitives for keyboard navigation, focus management, and ARIA attributes. The asChild pattern (via Slot.Root) lets you swap the rendered element while keeping behaviour:

const Component = asChild ? Slot.Root : "button";

All components merge class names with cn(), which combines clsx (conditional classes) and tailwind-merge (deduplication of Tailwind utilities):

import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]): string {
return twMerge(clsx(inputs));
}

Design tokens are defined in two layers in global.css. A base layer sets raw values as CSS custom properties, and the @theme directive maps them into Tailwind:

/* Base layer - raw design token values */
@layer base {
:root {
--background: hsl(0 0% 100%);
--foreground: hsl(240 10% 3.9%);
--primary: hsl(240 5.9% 10%);
--destructive: hsl(0 72.22% 50.59%);
--radius: 0.5rem;
}
}
/* Tailwind theme - maps tokens to utility classes */
@theme {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-primary: var(--primary);
--color-destructive: var(--destructive);
--radius-lg: var(--radius);
}

Dark mode is enabled through a custom Tailwind variant:

@custom-variant dark (&:is(.dark *));

This means dark-mode styles activate when an ancestor has the dark class, rather than relying on prefers-color-scheme.

The library uses Lucide React for icons. A custom Icon component renders icons as CSS masks rather than inline SVGs, reducing DOM size:

<span
style={{ "--mask-image": `var(--icon-${icon})` }}
className="inline-block size-6 bg-current [mask-image:var(--mask-image)]"
aria-hidden="true"
/>

All components have Storybook stories alongside their source files. A hosted instance is available at ui.evolve.labdigital.nl. The Storybook setup includes:

  • Accessibility checks via @storybook/addon-a11y
  • Responsive viewport presets (minimal through extra-large)
  • Auto-generated documentation from component props

The @evolve-storefront/gtm-ecommerce package provides Google Tag Manager Enhanced Ecommerce events. Ten standard ecommerce events are implemented:

Event Trigger
view_item Product detail page view
view_item_list Product listing page view
select_item Click on a product card
add_to_cart Add product to cart
remove_from_cart Remove product from cart
view_cart Cart page view
begin_checkout Start checkout
add_shipping_info Select shipping method
add_payment_info Select payment method
purchase Order confirmation

Each event sends a standard GA4 ecommerce payload with item-level data (SKU, name, brand, category, price, quantity). The core function sendGTMEcommerceEvent() pushes events to the dataLayer.