Skip to content

Mezzanine-UI/mezzanine

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3,295 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Mezzanine UI

A comprehensive React and Angular component library with a complete design system, built for modern web applications.

📦 Version

Current releases:

{
  "@mezzanine-ui/core": "1.0.3",
  "@mezzanine-ui/react": "1.0.3",
  "@mezzanine-ui/ng": "1.0.0-rc.2",
  "@mezzanine-ui/system": "1.0.2",
  "@mezzanine-ui/icons": "1.0.2"
}

@mezzanine-ui/ng is published under the rc dist-tag while the Angular port stabilises. Install with @rc (see below).

📚 Documentation

  • Storybook — unified hub that composes the React and Angular Storybooks side by side
    • https://storybook.mezzanine-ui.org/react/ — React canonical URL
    • https://storybook.mezzanine-ui.org/angular/ — Angular canonical URL
  • Migration Guide — Upgrading from previous versions

🌐 Browser Support

Browser Minimum Version
Google Chrome 64 (2018)
Edge 79 (2020)
Safari 13.1 (2020)
Firefox 69 (2019)

⚡ Framework Support

Next.js (App Router & Pages Router)

Mezzanine UI fully supports Next.js including the App Router. All React components include the 'use client' directive, making them compatible with React Server Components architecture.

// app/layout.tsx - Works seamlessly with Next.js App Router
import { CalendarConfigProviderDayjs, CalendarLocale } from '@mezzanine-ui/react/dayjs';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        <CalendarConfigProviderDayjs locale={CalendarLocale.ZH_TW}>{children}</CalendarConfigProviderDayjs>
      </body>
    </html>
  );
}

All hooks and utilities are also SSR-safe and can be used in Next.js projects without additional configuration.


📦 Installation

React

yarn add @mezzanine-ui/core @mezzanine-ui/react @mezzanine-ui/system @mezzanine-ui/icons

Angular (release candidate)

Requires Angular 21+.

yarn add @mezzanine-ui/core @mezzanine-ui/system @mezzanine-ui/icons
yarn add @mezzanine-ui/ng@rc
yarn add @angular/animations @angular/cdk @angular/common @angular/core

If you plan to use date-related components (DatePicker, Calendar, TimePicker, etc.), install one of the supported date libraries:

# Choose one:
yarn add dayjs      # Recommended - lightweight
yarn add moment     # Legacy support
yarn add luxon      # Alternative

🚀 Quick Start

1. Setup Styles

Create a main.scss file in your project:

@use '@mezzanine-ui/system' as mzn-system;
@use '@mezzanine-ui/core' as mzn-core;

// Apply design system variables
:root {
  @include mzn-system.colors(light);
  @include mzn-system.common-variables(default);
}

// Optional: Dark mode support
[data-theme='dark'] {
  @include mzn-system.colors(dark);
}

// Optional: Compact mode support
[data-density='compact'] {
  @include mzn-system.common-variables(compact);
}

// Import component styles
@include mzn-core.styles();

2. Import Styles

Import the stylesheet at your app's entry point:

import './main.scss';

function App() {
  return <div>Your App</div>;
}

3. Use Components

import { Button, Typography } from '@mezzanine-ui/react';
import { PlusIcon } from '@mezzanine-ui/icons';

function App() {
  return (
    <div>
      <Typography variant="h1">Welcome to Mezzanine UI</Typography>
      <Button icon={PlusIcon} iconType="leading" size="main" variant="base-primary">
        Click Me
      </Button>
    </div>
  );
}

4. Setup CalendarConfigProvider (Required for Date Components)

If your application uses date-related components (DatePicker, DateRangePicker, Calendar, TimePicker, etc.), you must wrap your app with a CalendarConfigProvider. This provider configures the date library and locale settings.

Choose one of the following date libraries based on your project needs:

Using Day.js (Recommended - Lightweight)

yarn add dayjs
import { CalendarConfigProviderDayjs, CalendarLocale } from '@mezzanine-ui/react/dayjs';

function App({ children }) {
  return <CalendarConfigProviderDayjs locale={CalendarLocale.ZH_TW}>{children}</CalendarConfigProviderDayjs>;
}

Using Moment.js

yarn add moment
import { CalendarConfigProviderMoment, CalendarLocale } from '@mezzanine-ui/react/moment';

function App({ children }) {
  return <CalendarConfigProviderMoment locale={CalendarLocale.ZH_TW}>{children}</CalendarConfigProviderMoment>;
}

Using Luxon

yarn add luxon
import { CalendarConfigProviderLuxon, CalendarLocale } from '@mezzanine-ui/react/luxon';

function App({ children }) {
  return <CalendarConfigProviderLuxon locale={CalendarLocale.ZH_TW}>{children}</CalendarConfigProviderLuxon>;
}

⚠️ Important: Import the provider from the specific entry point (e.g., @mezzanine-ui/react/dayjs) to avoid bundling unused date libraries.


📅 CalendarConfigProvider Configuration

Available Props

Prop Type Default Description
locale CalendarLocaleValue CalendarLocale.EN_US Controls calendar display: first day of week, month/weekday names
defaultDateFormat string 'YYYY-MM-DD' Default format string for date values
defaultTimeFormat string 'HH:mm:ss' Default format string for time values

Supported Locales

Common locale values (see CalendarLocale enum for full list):

Locale Value First Day of Week
CalendarLocale.EN_US 'en-US' Sunday
CalendarLocale.EN_GB 'en-GB' Monday
CalendarLocale.ZH_TW 'zh-TW' Sunday
CalendarLocale.ZH_CN 'zh-CN' Monday
CalendarLocale.JA_JP 'ja-JP' Sunday
CalendarLocale.KO_KR 'ko-KR' Sunday
CalendarLocale.DE_DE 'de-DE' Monday
CalendarLocale.FR_FR 'fr-FR' Monday

Usage with Next.js App Router

For Next.js App Router, place the provider in your root layout:

// app/layout.tsx
import { CalendarConfigProviderDayjs, CalendarLocale } from '@mezzanine-ui/react/dayjs';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        <CalendarConfigProviderDayjs locale={CalendarLocale.ZH_TW}>{children}</CalendarConfigProviderDayjs>
      </body>
    </html>
  );
}

Custom Date/Time Formats

import { CalendarConfigProviderDayjs, CalendarLocale } from '@mezzanine-ui/react/dayjs';

function App({ children }) {
  return (
    <CalendarConfigProviderDayjs defaultDateFormat="YYYY/MM/DD" defaultTimeFormat="HH:mm" locale={CalendarLocale.ZH_TW}>
      {children}
    </CalendarConfigProviderDayjs>
  );
}

Date Library Comparison

Library Bundle Size Tree-Shakeable ISO Week Support Note
Day.js ~2KB Yes Yes Recommended for most projects
Moment ~70KB No Yes Legacy support
Luxon ~20KB Yes Yes (ISO only) Always uses Monday as first day

🅰️ Angular Quick Start

The Angular package mirrors the React API prop-for-prop. Components are distributed as secondary entry points — import each component from its own path so that Angular's tree-shaker only pulls in what you use.

1. Share the same SCSS setup

@mezzanine-ui/ng consumes the same @mezzanine-ui/core + @mezzanine-ui/system tokens as React. Reuse the main.scss shown in the React Quick Start verbatim and load it from your Angular app's styles array (angular.json) or global stylesheet.

2. Import components from secondary entry points

// app.component.ts
import { Component } from '@angular/core';
import { MznButton } from '@mezzanine-ui/ng/button';
import { MznTypography } from '@mezzanine-ui/ng/typography';
import { MznIcon } from '@mezzanine-ui/ng/icon';
import { PlusIcon } from '@mezzanine-ui/icons';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [MznButton, MznTypography, MznIcon],
  template: `
    <mzn-typography variant="h1">Welcome to Mezzanine UI</mzn-typography>
    <mzn-button variant="base-primary" size="main" [icon]="plusIcon" iconType="leading"> Click Me </mzn-button>
  `,
})
export class AppComponent {
  plusIcon = PlusIcon;
}

⚠️ Do not barrel-import from @mezzanine-ui/ng. Always use the per-component path (e.g. @mezzanine-ui/ng/button) so ng-packagr's secondary entry points can be code-split correctly.

3. API parity with React

Angular inputs/outputs mirror React props 1:1, including both flat top-level props (e.g. disabledMonthSwitch, placeholderLeft) and bundle props (e.g. calendarProps, popperProps). When both are provided, flat props win. See .claude/skills/architecting-angular-components/SKILL.md for the full contract.


🎨 Design System Customization

Understanding Primitives vs Semantic

Mezzanine UI v2 uses a two-layer design token system:

  • Primitives: Raw values (e.g., #3b82f6, 16px)
  • Semantic: Contextual tokens that reference primitives (e.g., text-brand, padding-base)

💡 Best Practice: Always use semantic tokens in your application for automatic theme switching support.

Customize Color Palette

Override palette colors by passing a custom configuration:

@use '@mezzanine-ui/system' as mzn-system;

$custom-palette: (
  background: (
    base: (
      light: #000,
      dark: #fff,
    ),
    menu: (
      light: #fff,
      dark: #9a9a9a,
    ),
    // ...
  ), // ...
);

:root {
  @include mzn-system.colors(light, $custom-palette);
}

[data-theme='dark'] {
  @include mzn-system.colors(dark, $custom-palette);
}

Customize Typography

Override typography settings:

@use '@mezzanine-ui/system' as mzn-system;
@use '@mezzanine-ui/system/typography' as typography;

$custom-variables: (
  typography: (
    h3: (
      font-size: 18px,
      font-weight: 700,
      line-height: 26px,
      letter-spacing: 0,
    ),
    // ...
  ),
);

:root {
  @include mzn-system.common-variables(default, $custom-variables);
}

Customize Spacing

Override spacing values:

@use '@mezzanine-ui/system' as mzn-system;

$custom-variables: (
  spacing: (
    size: (
      element: (
        hairline: (
          default: 2px,
          compact: 2px,
        ),
      ),
    ),
  ),
);

:root {
  @include mzn-system.common-variables(default, $custom-variables);
}

[data-density='compact'] {
  @include mzn-system.common-variables(compact, $custom-variables);
}

🎯 Using Design Tokens in Your Components

In SCSS

@use '@mezzanine-ui/system/palette' as palette;
@use '@mezzanine-ui/system/spacing' as spacing;
@use '@mezzanine-ui/system/radius' as radius;
@use '@mezzanine-ui/system/typography' as typography;

.my-component {
  // Colors - use semantic variables
  color: palette.semantic-variable(text, brand);
  background-color: palette.semantic-variable(background, base);
  border-color: palette.semantic-variable(border, neutral);

  // Spacing - use semantic variables
  padding: spacing.semantic-variable(padding, horizontal, base);
  gap: spacing.semantic-variable(gap, tight);

  // Border radius
  border-radius: radius.variable(base);

  // Typography - apply full semantic typography
  @include typography.semantic-variable(body);
}

Available System Modules

Module Purpose Example
palette Colors (text, background, etc.) palette.semantic-variable(text, brand)
spacing Padding, margin, gap spacing.semantic-variable(padding, base)
radius Border radius radius.variable(base)
typography Font settings @include typography.semantic-variable(button)
effect Shadows, focus rings effect.variable(focus, primary)
size Element sizes size.semantic-variable(element, main)

🔧 Using React Components

Component Examples

Button

import { Button } from '@mezzanine-ui/react';
import { PlusIcon } from '@mezzanine-ui/icons';

<Button variant="base-primary" size="main">
  Primary Button
</Button>

<Button variant="base-secondary" size="sub" disabled>
  Disabled Button
</Button>

<Button icon={PlusIcon} iconType="leading" size="minor" variant="outlined-primary">
  With Icon
</Button>

Typography

import { Typography } from '@mezzanine-ui/react';

<Typography variant="h1">Heading 1</Typography>
<Typography variant="body">Body text</Typography>
<Typography variant="caption" color="text-neutral">
  Caption text
</Typography>

DatePicker

Requires CalendarConfigProvider wrapper (see Setup CalendarConfigProvider)

import { useState } from 'react';
import { DatePicker } from '@mezzanine-ui/react';

function Example() {
  const [date, setDate] = useState<string>();

  return (
    <DatePicker
      onChange={setDate}
      placeholder="Select a date"
      value={date}
    />
  );
}

DateRangePicker

import { useState } from 'react';
import { DateRangePicker } from '@mezzanine-ui/react';

function Example() {
  const [range, setRange] = useState<[string, string]>();

  return (
    <DateRangePicker
      onChange={setRange}
      value={range}
    />
  );
}

Layout

Layout is the top-level page shell for building full-application frames. It manages a responsive structure with a sticky navigation sidebar and optional resizable side panels. The sub-components (Layout.Main, Layout.LeftPanel, Layout.RightPanel) can be placed in any order — the layout always renders them in the correct visual sequence.

Sub-component Purpose
<Navigation> Sticky left navigation sidebar
<Layout.Main> Main scrollable content area (required)
<Layout.LeftPanel> Resizable left panel (optional)
<Layout.RightPanel> Resizable right panel (optional)

Basic layout with navigation:

import { useState } from 'react';
import { Layout, Navigation, NavigationFooter, NavigationHeader, NavigationOption } from '@mezzanine-ui/react';
import { FileIcon, HomeIcon } from '@mezzanine-ui/icons';

function App() {
  const [activatedPath, setActivatedPath] = useState(['Home']);

  return (
    <Layout>
      <Navigation activatedPath={activatedPath} onOptionClick={(path) => path && setActivatedPath(path)}>
        <NavigationHeader title="My App" />
        <NavigationOption icon={HomeIcon} title="Home" />
        <NavigationOption icon={FileIcon} title="Reports">
          <NavigationOption title="Traffic" />
          <NavigationOption title="Conversion" />
        </NavigationOption>
        <NavigationFooter />
      </Navigation>
      <Layout.Main>
        <h1>Page Content</h1>
      </Layout.Main>
    </Layout>
  );
}

With a toggleable right panel:

import { useState } from 'react';
import { Layout, Navigation, NavigationFooter, NavigationHeader } from '@mezzanine-ui/react';

function App() {
  const [rightOpen, setRightOpen] = useState(false);

  return (
    <Layout>
      <Navigation>
        <NavigationHeader title="My App" />
        <NavigationFooter />
      </Navigation>
      <Layout.Main>
        <button onClick={() => setRightOpen(true)}>Open Details</button>
      </Layout.Main>
      <Layout.RightPanel defaultWidth={320} open={rightOpen}>
        <div>
          <h2>Details</h2>
          <button onClick={() => setRightOpen(false)}>Close</button>
        </div>
      </Layout.RightPanel>
    </Layout>
  );
}

With dual panels (left + right):

import { useState } from 'react';
import { Layout, Navigation, NavigationFooter, NavigationHeader } from '@mezzanine-ui/react';

function App() {
  const [leftOpen, setLeftOpen] = useState(true);
  const [rightOpen, setRightOpen] = useState(false);

  return (
    <Layout>
      <Navigation>
        <NavigationHeader title="My App" />
        <NavigationFooter />
      </Navigation>
      <Layout.LeftPanel defaultWidth={240} open={leftOpen}>
        <div>Sidebar filters, navigation trees, etc.</div>
      </Layout.LeftPanel>
      <Layout.Main>
        <h1>Main Content</h1>
        {!rightOpen && <button onClick={() => setRightOpen(true)}>Open Right</button>}
      </Layout.Main>
      <Layout.RightPanel defaultWidth={320} open={rightOpen}>
        <div>Detail view, preview, contextual actions, etc.</div>
      </Layout.RightPanel>
    </Layout>
  );
}

Layout.LeftPanel / Layout.RightPanel props:

Prop Type Default Description
open boolean false Controls panel visibility
defaultWidth number 320 Initial width in px (minimum 240)
onWidthChange (width: number) => void Callback when the panel is resized
scrollbarProps ScrollbarProps Props forwarded to the inner scrollbar

Panels are resizable by dragging the divider. Focus the divider and use / arrow keys to resize with keyboard.


🪝 React Hooks

Mezzanine provides several utility hooks for common UI patterns:

Hook Description
useClickAway Detect clicks outside an element (useful for dropdowns, modals)
useComposeRefs Compose multiple refs into one
useDocumentEscapeKeyDown Listen for ESC key press on document
useDocumentTabKeyDown Listen for Tab key press on document
useDocumentEvents Generic document event listener with cleanup
useIsomorphicLayoutEffect SSR-safe useLayoutEffect (uses useEffect on server)
useLastCallback Stable callback reference that always calls the latest version
useLastValue Ref that always holds the latest value
usePreviousValue Access the previous render's value
useScrollLock Lock body scroll (for modals/overlays) with scrollbar gap compensation
useTopStack Manage stacking order for overlays
useWindowWidth Track window width with resize listener

Example: useClickAway

import { useRef } from 'react';
import { useClickAway } from '@mezzanine-ui/react';

function Dropdown({ onClose }) {
  const dropdownRef = useRef();

  useClickAway(() => (event) => onClose(event), dropdownRef, [onClose]);

  return <div ref={dropdownRef}>Dropdown content</div>;
}

Example: useScrollLock

import { useScrollLock } from '@mezzanine-ui/react';

function Modal({ isOpen }) {
  // Automatically locks scroll when modal is open
  useScrollLock({ enabled: isOpen });

  return isOpen ? <div className="modal">Modal content</div> : null;
}

🛠️ Utility Functions

Mezzanine also exports commonly used utility functions:

Utility Description
formatNumberWithCommas Format numbers with locale-aware thousands separators
parseNumberWithCommas Parse comma-formatted string back to number
getCSSVariableValue Get computed CSS variable value from :root
getNumericCSSVariablePixelValue Get CSS variable as numeric pixel value
arrayMove Move array item from one index to another (immutable)
cx Classname utility (re-exported from clsx)
composeRefs Compose multiple refs (non-hook version)
getScrollbarWidth Get current scrollbar width in pixels

Example: Number Formatting

import { formatNumberWithCommas, parseNumberWithCommas } from '@mezzanine-ui/react';

// Format number for display
formatNumberWithCommas(1234567); // '1,234,567'
formatNumberWithCommas(1234567, 'de-DE'); // '1.234.567'

// Parse back to number
parseNumberWithCommas('1,234,567'); // 1234567

Example: CSS Variables

import { getCSSVariableValue } from '@mezzanine-ui/react';

// Read design token values at runtime
const brandColor = getCSSVariableValue('--mzn-color-text-brand');

🌙 Theme Support

Light/Dark Mode

Mezzanine UI v2 has built-in support for light and dark modes:

// In your SCSS
:root {
  @include mzn-system.colors(light);
}

[data-theme='dark'] {
  @include mzn-system.colors(dark);
}

Toggle theme in your React app:

function ThemeToggle() {
  const [theme, setTheme] = useState('light');

  useEffect(() => {
    document.documentElement.setAttribute('data-theme', theme);
  }, [theme]);

  return <Button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>Toggle Theme</Button>;
}

Default/Compact Mode

Switch between comfortable and compact spacing:

:root {
  @include mzn-system.common-variables(default);
}

[data-density='compact'] {
  @include mzn-system.common-variables(compact);
}

Toggle density in your React app:

function DensityToggle() {
  const [density, setDensity] = useState('default');

  useEffect(() => {
    document.documentElement.setAttribute('data-density', density);
  }, [density]);

  return <Button onClick={() => setDensity(density === 'default' ? 'compact' : 'default')}>Toggle Density</Button>;
}

📖 Icon Usage

Use icons from the @mezzanine-ui/icons package:

import Icon from '@mezzanine-ui/react/Icon';
import { CheckIcon, ChevronDownIcon, PlusIcon } from '@mezzanine-ui/icons';

function Example() {
  return (
    <div>
      <Icon icon={ChevronDownIcon} />
      <Icon icon={PlusIcon} />
      <Icon icon={CheckIcon} />
    </div>
  );
}

🤝 Contributing

We welcome contributions! Please see our Development Guidelines for:

  • Setting up the development environment
  • Understanding the project architecture
  • Following coding conventions
  • Writing tests and documentation

📝 License

MIT License - see LICENSE for details.


🔗 Links

About

An enterprise-class UI design language and React/Angular UI library

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages