npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

mw-design-library

v2.0.1

Published

MW Design System - Shared component library for all MW products

Downloads

237

Readme

mw-design-library

🎨 MW Design System - Shared component library and design tokens for all MW products.

Installation

pnpm add mw-design-library
# or
npm install mw-design-library
# or
yarn add mw-design-library

Usage

Import Components

import { Avatar, Badge, Breadcrumb, Button, Checkbox, Radio, Dropdown, Flex, Input, Textarea, Pagination, ProgressBar, ProgressStepper, Scrollbar } from 'mw-design-library';

// Avatar examples
<Avatar src="/user.jpg" alt="John Doe" />
<Avatar name="John Doe" />
<Avatar src="/user.jpg" status="online" statusPosition="bottom" />

// Badge examples
<Badge>New</Badge>
<Badge color="success" style="solid">Active</Badge>
<Badge iconLeft={<Icon />}>Label</Badge>
<Badge size="lg" borderRadius="full">Pill Badge</Badge>

// Breadcrumb examples
<Breadcrumb 
  items={[
    { label: 'Home', href: '/' },
    { label: 'Products', href: '/products' },
    { label: 'Current Page' }
  ]} 
/>

// Button examples
<Button>Click me</Button>
<Button variant="secondary" size="lg">Large Button</Button>
<Button iconLeft={<Icon />}>With Icon</Button>
<Button loading>Loading...</Button>

// Checkbox examples
<Checkbox label="Accept terms" />
<Checkbox size="sm" label="Small checkbox" />
<Checkbox indeterminate label="Select all" />

// Radio examples
<Radio name="option" value="1" label="Option 1" />
<Radio name="option" value="2" label="Option 2" />
<Radio size="sm" variant="outlined" name="size" value="sm" label="Small" />

// Dropdown examples
<Dropdown
  label="Select an option"
  options={[
    { value: '1', label: 'Option 1' },
    { value: '2', label: 'Option 2' }
  ]}
  value={selectedValue}
  onChange={(value) => setSelectedValue(value)}
/>

// Flex examples
<Flex>
  <div>Item 1</div>
  <div>Item 2</div>
</Flex>
<Flex direction="column" gap="m">
  <div>Item 1</div>
  <div>Item 2</div>
</Flex>
<Flex justify="center" align="center" fullHeight>
  <div>Centered content</div>
</Flex>
<Flex justify="between" gap="l">
  <Button>Left</Button>
  <Button>Right</Button>
</Flex>

// Input examples
<Input label="Email" placeholder="Enter your email" />
<Input label="Password" type="password" required />
<Input size="sm" iconLeft={<Icon />} helperText="Helper text" />

// Textarea examples
<Textarea label="Description" placeholder="Enter description" />
<Textarea label="Comments" characterCount="0/500" maxLength={500} />
<Textarea label="Feedback" error="This field is required" />

// Pagination examples
<Pagination
  currentPage={1}
  totalPages={10}
  onPageChange={(page) => setPage(page)}
/>
<Pagination
  currentPage={currentPage}
  totalPages={totalPages}
  totalItems={100}
  itemsPerPage={10}
  itemsPerPageOptions={[10, 20, 30, 40]}
  onPageChange={(page) => setPage(page)}
  onItemsPerPageChange={(size) => setItemsPerPage(size)}
/>

// Progress Bar examples
<ProgressBar value={50} />
<ProgressBar value={75} color="success" />
<ProgressBar value={25} color="error" size="sm" />

// Progress Stepper examples
<ProgressStepper
  steps={[
    { title: 'Step 1', description: 'Description 1', state: 'completed' },
    { title: 'Step 2', description: 'Description 2', state: 'active' },
    { title: 'Step 3', description: 'Description 3', state: 'default' }
  ]}
/>

// Scrollbar examples
<Scrollbar maxHeight="240px">
  <div>Long scrollable content here...</div>
</Scrollbar>
<div className="mw-scrollbar-global" style={{ maxHeight: '240px', overflow: 'auto' }}>
  <div>Content with global scrollbar class</div>
</div>

Import Design Tokens (TypeScript/JavaScript)

import { colors, getColorWithOpacity } from 'mw-design-library';

// Use colors directly
const primaryColor = colors.primary[600]; // '#237bd4'

// Get color with opacity
const primaryWithOpacity = getColorWithOpacity('primary', 600, 0.5);
// Returns: 'rgba(35, 123, 212, 0.5)'

Import CSS Variables

import 'mw-design-library/styles';

Then use in your CSS:

.my-button {
  background-color: var(--mw-primary-600);
  color: var(--mw-neutral-100);
  padding: var(--mw-spacing-md);
  border-radius: var(--mw-radius-md);
}

Available Color Palettes

  • Primary: colors.primary[100-1000]
  • Secondary: colors.secondary[100-1000]
  • Neutral: colors.neutral[100-1000]
  • Success: colors.success[100-1000]
  • Error: colors.error[100-1000]
  • Warning: colors.warning[100-1000]
  • Grey: colors.grey[100-1000]
  • Info: colors.info[100-1000]

Import Typography Tokens

import { typography, getTypographyStyleObject } from 'mw-design-library';

// Use typography styles directly
const h1Style = typography.heading.h1;
// { fontFamily: 'Poppins, sans-serif', fontWeight: 600, fontSize: '40px', ... }

// Get as CSS object for React inline styles
const style = getTypographyStyleObject('heading', 'h1');

Typography Styles Available

Display Styles (Bold):

  • typography.display.large - 40px / 56px
  • typography.display.medium - 32px / 48px
  • typography.display.small - 24px / 40px

Heading Styles (SemiBold):

  • typography.heading.h1 - 40px / 56px
  • typography.heading.h2 - 32px / 48px
  • typography.heading.h3 - 24px / 40px
  • typography.heading.h4 - 20px / 32px
  • typography.heading.h5 - 18px / 24px

Body Styles (SemiBold):

  • typography.body.large - 18px / 24px
  • typography.body.regular - 16px / 20px
  • typography.body.medium - 14px / 18px
  • typography.body.small - 12px / 16px

Typography CSS Variables

All typography styles are available as CSS custom properties:

.my-heading {
  font-family: var(--mw-heading-h1-font-family);
  font-weight: var(--mw-heading-h1-font-weight);
  font-size: var(--mw-heading-h1-font-size);
  line-height: var(--mw-heading-h1-line-height);
  letter-spacing: var(--mw-heading-h1-letter-spacing);
}

Import Border Tokens

import { borders, borderRadius, getBorderWidth } from 'mw-design-library';

// Use border widths directly
const borderWidth = borders.m; // '2px'

// Get border width
const width = getBorderWidth('xl'); // '6px'

// Use border radius
const radius = borderRadius.m; // '16px'

Border Styles Available

Border Widths (Stroke):

  • borders.s - 1px
  • borders.m - 2px
  • borders.l - 4px
  • borders.xl - 6px
  • borders['2xl'] - 8px
  • borders['3xl'] - 12px
  • borders['4xl'] - 16px
  • borders['5xl'] - 20px

Border Radius:

  • borderRadius.none - 0px
  • borderRadius['2xs'] - 4px
  • borderRadius.xs - 8px
  • borderRadius.s - 12px
  • borderRadius.m - 16px
  • borderRadius.l - 20px
  • borderRadius.xl - 24px
  • borderRadius['2xl'] - 32px
  • borderRadius['3xl'] - 40px
  • borderRadius['4xl'] - 48px
  • borderRadius['5xl'] - 56px
  • borderRadius.full - 999px

Border CSS Variables

All border styles are available as CSS custom properties:

.my-element {
  border-width: var(--mw-border-m);
  border-radius: var(--mw-border-radius-m);
  border-style: solid;
  border-color: var(--mw-primary-600);
}

Import Spacing Tokens

import { spacing, getSpacing } from 'mw-design-library';

// Use spacing values directly
const padding = spacing.m; // '16px'

// Get spacing value
const margin = getSpacing('xl'); // '24px'

Spacing Values Available

  • spacing['3xs'] - 2px
  • spacing['2xs'] - 4px
  • spacing.xs - 8px
  • spacing.s - 12px
  • spacing.m - 16px
  • spacing.l - 20px
  • spacing.xl - 24px
  • spacing['2xl'] - 32px
  • spacing['3xl'] - 40px
  • spacing['4xl'] - 48px
  • spacing['5xl'] - 56px
  • spacing['6xl'] - 64px

Spacing CSS Variables

All spacing values are available as CSS custom properties:

.my-container {
  padding: var(--mw-spacing-m);
  margin: var(--mw-spacing-xl);
  gap: var(--mw-spacing-s);
}

Import Elevation/Shadow Tokens

import { elevation, getElevation, getElevationStyle } from 'mw-design-library';

// Use elevation shadows directly
const shadow = elevation.e1.boxShadow;

// Get elevation shadow
const boxShadow = getElevation('e2');

// Get as CSS object for React inline styles
const style = getElevationStyle('e3');

Elevation Levels Available

  • elevation.e0 - Base elevation (subtle shadow)
  • elevation.e1 - Low elevation (minimal shadow with border)
  • elevation.e2 - Medium elevation (moderate shadow with border)
  • elevation.e3 - High elevation (strong shadow with border)

Elevation CSS Variables

All elevation shadows are available as CSS custom properties:

.my-card {
  box-shadow: var(--mw-elevation-e2);
}

.floating-element {
  box-shadow: var(--mw-elevation-e3);
}

Avatar Component Props

The Avatar component is highly customizable:

Basic Props:

  • src - Image source URL
  • alt - Alt text for the image
  • name - Full name (used to generate initials)
  • initials - Custom initials to display
  • icon - React node to display as icon
  • size - Size: 'xs' (24px), 'sm' (32px), 'md' (40px), 'lg' (44px), 'xl' (56px)
  • status - Status indicator: 'online', 'offline', 'away', 'busy'
  • statusPosition - Status position: 'top' or 'bottom' (default: 'bottom')
  • backgroundColor - Custom background color for initials
  • textColor - Custom text color for initials
  • bordered - Show border around avatar
  • onClick - Click handler (makes avatar clickable)

Examples:

// Image with status
<Avatar 
  src="/user.jpg" 
  alt="John Doe"
  status="online"
  size="md"
/>

// Initials with custom colors
<Avatar 
  name="Jane Smith"
  backgroundColor="#4a84bf"
  textColor="#fff"
  size="lg"
/>

// Icon avatar
<Avatar 
  icon={<UserIcon />}
  size="sm"
/>

// Clickable avatar
<Avatar 
  src="/user.jpg"
  onClick={() => console.log('Avatar clicked')}
/>

Badge Component Props

The Badge component is highly customizable:

Basic Props:

  • children - Badge content (text)
  • size - Size: 'sm' (20px), 'md' (24px), 'lg' (28px)
  • color - Color variant: 'primary', 'neutral', 'error', 'success', 'warning'
  • style - Style variant: 'solid', 'outline', 'subtle', 'outline-subtle'
  • borderRadius - Border radius: 'corner' (4px) or 'full' (pill shape)
  • iconLeft - Icon to display before text
  • iconRight - Icon to display after text

Examples:

// Basic badge
<Badge>New</Badge>

// Color and style variants
<Badge color="success" style="solid">Active</Badge>
<Badge color="error" style="outline">Error</Badge>
<Badge color="warning" style="subtle">Warning</Badge>

// With icons
<Badge iconLeft={<CheckIcon />}>Verified</Badge>
<Badge iconRight={<CloseIcon />}>Dismissible</Badge>

// Different sizes
<Badge size="sm">Small</Badge>
<Badge size="md">Medium</Badge>
<Badge size="lg">Large</Badge>

// Pill shape
<Badge borderRadius="full">Pill Badge</Badge>

Breadcrumb Component Props

The Breadcrumb component provides navigation context:

Basic Props:

  • items - Array of breadcrumb items (required)
  • divider - Custom divider character or component (default: '/')
  • showIcons - Whether to show icons on items (default: true)

BreadcrumbItem Props:

  • label - Text label for the item (required)
  • href - URL for the item (optional)
  • icon - Icon component to display before label (optional)
  • onClick - Click handler (alternative to href)

Examples:

// Basic breadcrumb
<Breadcrumb 
  items={[
    { label: 'Home', href: '/' },
    { label: 'Products', href: '/products' },
    { label: 'Current Page' }
  ]} 
/>

// With icons
<Breadcrumb 
  items={[
    { label: 'Home', icon: <HomeIcon />, href: '/' },
    { label: 'Dashboard', icon: <DashboardIcon />, href: '/dashboard' },
    { label: 'Settings' }
  ]} 
/>

// Custom divider
<Breadcrumb 
  items={items}
  divider=">"
/>

// With onClick handlers
<Breadcrumb 
  items={[
    { label: 'Home', onClick: () => navigate('/') },
    { label: 'Current Page' }
  ]} 
/>

Button Component Props

The Button component is highly customizable with multiple variants and states:

Basic Props:

  • children - Button content (text)
  • variant - Button style: 'primary', 'secondary', 'outline', 'link'
  • size - Size: 'sm' (32px), 'md' (34px), 'lg' (40px)
  • color - Color variant: 'primary', 'neutral', 'success', 'error', 'warning'
  • iconLeft - Icon to display before text
  • iconRight - Icon to display after text
  • loading - Show loading spinner
  • fullWidth - Make button full width
  • disabled - Disable the button
  • onClick - Click handler

Examples:

// Basic buttons
<Button>Primary Button</Button>
<Button variant="secondary">Secondary Button</Button>
<Button variant="outline">Outline Button</Button>
<Button variant="link">Link Button</Button>

// Different sizes
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>

// Color variants
<Button color="success">Success</Button>
<Button color="error">Error</Button>
<Button color="warning">Warning</Button>
<Button color="neutral">Neutral</Button>

// With icons
<Button iconLeft={<PlusIcon />}>Add Item</Button>
<Button iconRight={<ArrowIcon />}>Continue</Button>
<Button iconLeft={<SaveIcon />} iconRight={<CheckIcon />}>Save</Button>

// Loading state
<Button loading>Processing...</Button>

// Full width
<Button fullWidth>Full Width Button</Button>

// Disabled state
<Button disabled>Disabled Button</Button>

// Combined props
<Button 
  variant="outline" 
  color="success" 
  size="lg" 
  iconLeft={<CheckIcon />}
  fullWidth
>
  Complete Order
</Button>

Checkbox Component Props

The Checkbox component supports multiple sizes, states, and an indeterminate mode:

Basic Props:

  • label - Label text displayed next to the checkbox
  • size - Size: 'sm' (16px), 'lg' (20px)
  • checked - Whether the checkbox is checked (controlled)
  • indeterminate - Whether the checkbox is in indeterminate state
  • disabled - Disable the checkbox
  • onChange - Change handler
  • defaultChecked - Initial checked state (uncontrolled)

Examples:

// Basic checkbox
<Checkbox label="Accept terms and conditions" />

// Controlled checkbox
<Checkbox 
  label="Subscribe to newsletter" 
  checked={isSubscribed}
  onChange={(e) => setIsSubscribed(e.target.checked)}
/>

// Uncontrolled checkbox
<Checkbox 
  label="Remember me"
  defaultChecked={true}
/>

// Different sizes
<Checkbox size="sm" label="Small checkbox" />
<Checkbox size="lg" label="Large checkbox" />

// Indeterminate state (useful for "select all" scenarios)
<Checkbox 
  label="Select all items"
  indeterminate={true}
  checked={false}
  onChange={(e) => {
    // Handle select all logic
  }}
/>

// Disabled state
<Checkbox disabled label="Disabled checkbox" />
<Checkbox disabled checked label="Disabled checked" />

// Without label (just the checkbox)
<Checkbox 
  checked={isChecked}
  onChange={(e) => setIsChecked(e.target.checked)}
  aria-label="Custom checkbox"
/>

// In a form
<form>
  <Checkbox label="Option 1" name="option1" value="1" />
  <Checkbox label="Option 2" name="option2" value="2" />
  <Checkbox label="Option 3" name="option3" value="3" />
</form>

States:

  • Default: Unchecked with neutral border
  • Hover: Darker border on hover
  • Focus: Focus outline with darker border
  • Checked: Primary background with white checkmark
  • Indeterminate: Primary background with white minus icon
  • Disabled: Lighter colors, reduced opacity

Radio Component Props

The Radio component enables users to select a single option from a list. All radio buttons in a group must share the same name prop.

Basic Props:

  • label - Label text displayed next to the radio button
  • size - Size: 'sm' (16px), 'lg' (20px)
  • variant - Visual variant when checked: 'default' (unchecked), 'outlined' (primary border + dot), 'filled' (primary background + white dot)
  • checked - Whether the radio button is checked (controlled)
  • disabled - Disable the radio button
  • onChange - Change handler
  • name - Name attribute for radio group (required)
  • value - Value of the radio button
  • defaultChecked - Initial checked state (uncontrolled)

Examples:

// Basic radio group
<Radio name="option" value="1" label="Option 1" />
<Radio name="option" value="2" label="Option 2" />
<Radio name="option" value="3" label="Option 3" />

// Controlled radio group
const [selectedSize, setSelectedSize] = useState('medium');

<Radio 
  name="size"
  value="small"
  label="Small"
  checked={selectedSize === 'small'}
  onChange={(e) => setSelectedSize(e.target.value)}
/>
<Radio 
  name="size"
  value="medium"
  label="Medium"
  checked={selectedSize === 'medium'}
  onChange={(e) => setSelectedSize(e.target.value)}
/>
<Radio 
  name="size"
  value="large"
  label="Large"
  checked={selectedSize === 'large'}
  onChange={(e) => setSelectedSize(e.target.value)}
/>

// Different sizes
<Radio size="sm" name="size" value="sm" label="Small" />
<Radio size="lg" name="size" value="lg" label="Large" />

// Different variants
<Radio variant="outlined" name="type" value="outlined" label="Outlined style" />
<Radio variant="filled" name="type" value="filled" label="Filled style" />

// Disabled state
<Radio disabled name="option" value="disabled" label="Disabled option" />
<Radio disabled checked name="option" value="disabled-checked" label="Disabled checked" />

// Without label (just the radio button)
<Radio 
  name="option"
  value="no-label"
  checked={isChecked}
  onChange={(e) => setIsChecked(e.target.checked)}
  aria-label="Custom radio button"
/>

// In a form
<form>
  <Radio name="payment" value="credit" label="Credit Card" />
  <Radio name="payment" value="debit" label="Debit Card" />
  <Radio name="payment" value="paypal" label="PayPal" />
</form>

States:

  • Default: Unchecked with neutral border
  • Hover: Darker border on hover
  • Focus: Focus outline with darker border
  • Checked (Outlined): Primary border with primary dot inside
  • Checked (Filled): Primary background with white dot inside
  • Disabled: Lighter colors, reduced opacity

Important Notes:

  • Radio buttons are used for single selection from multiple options
  • All radio buttons in a group must share the same name prop
  • Only one radio button in a group can be selected at a time
  • Use value prop to identify which option was selected

Dropdown Component Props

The Dropdown component enables users to select one or multiple options from a list. It supports search, categories, and various display modes.

Basic Props:

  • label - Label text displayed above the dropdown
  • required - Whether the field is required
  • optionalText - Optional indicator text (e.g., "(Optional)")
  • helperText - Helper text displayed below the dropdown
  • characterCount - Character count text (e.g., "0/200")
  • placeholder - Placeholder text
  • options - Array of options to display
  • value - Selected value(s): string for single select, string[] for multi-select
  • onChange - Change handler
  • mode - Selection mode: 'single' or 'multi'
  • optionType - Option display type: 'simple', 'radio', 'checkbox'
  • searchable - Whether search is enabled
  • searchPlaceholder - Search placeholder text
  • showCreateOption - Whether to show "Create new" option
  • createOptionLabel - Create option label text
  • onCreateOption - Create option handler
  • disabled - Disable the dropdown
  • open - Whether the dropdown is open (controlled)
  • onOpenChange - Open/close handler
  • maxHeight - Maximum height of the dropdown menu

DropdownOption Interface:

interface DropdownOption {
  value: string;
  label: string;
  description?: string;
  disabled?: boolean;
  category?: string;
  icon?: React.ReactNode;
}

Examples:

// Basic single select
<Dropdown
  label="Select an option"
  options={[
    { value: '1', label: 'Option 1' },
    { value: '2', label: 'Option 2' },
    { value: '3', label: 'Option 3' }
  ]}
  value={selectedValue}
  onChange={(value) => setSelectedValue(value)}
/>

// Multi-select with checkboxes
<Dropdown
  label="Select multiple options"
  mode="multi"
  optionType="checkbox"
  options={[
    { value: '1', label: 'Option 1', description: 'First option' },
    { value: '2', label: 'Option 2', description: 'Second option' },
    { value: '3', label: 'Option 3', description: 'Third option' }
  ]}
  value={selectedValues}
  onChange={(values) => setSelectedValues(values)}
/>

// With search
<Dropdown
  label="Search options"
  searchable
  searchPlaceholder="Search here.."
  options={options}
  value={selectedValue}
  onChange={(value) => setSelectedValue(value)}
/>

// With categories
<Dropdown
  label="Select category"
  options={[
    { value: '1', label: 'Option 1', category: 'Category A' },
    { value: '2', label: 'Option 2', category: 'Category A' },
    { value: '3', label: 'Option 3', category: 'Category B' }
  ]}
  value={selectedValue}
  onChange={(value) => setSelectedValue(value)}
/>

// With radio buttons (single select)
<Dropdown
  label="Select with radio"
  optionType="radio"
  options={options}
  value={selectedValue}
  onChange={(value) => setSelectedValue(value)}
/>

// With create option
<Dropdown
  label="Select or create"
  showCreateOption
  createOptionLabel="Create new option"
  onCreateOption={() => console.log('Create clicked')}
  options={options}
  value={selectedValue}
  onChange={(value) => setSelectedValue(value)}
/>

// With helper text and character count
<Dropdown
  label="Select option"
  helperText="Please select an option from the list"
  characterCount="0/200"
  options={options}
  value={selectedValue}
  onChange={(value) => setSelectedValue(value)}
/>

// Required field
<Dropdown
  label="Required field"
  required
  options={options}
  value={selectedValue}
  onChange={(value) => setSelectedValue(value)}
/>

// Disabled
<Dropdown
  label="Disabled dropdown"
  disabled
  options={options}
  value={selectedValue}
  onChange={(value) => setSelectedValue(value)}
/>

Features:

  • Single Select: Select one option from the list
  • Multi-Select: Select multiple options with checkboxes
  • Search: Filter options by typing
  • Categories: Group options by category
  • Option Descriptions: Show additional text below option labels
  • Icons: Support for icons in options
  • Create Option: Add a "Create new" action at the bottom
  • Keyboard Navigation: Full keyboard support
  • Click Outside: Closes when clicking outside
  • Accessibility: Proper ARIA attributes

Input Component Props

The Input component is a versatile text input field that supports multiple sizes, states, icons, and helper text.

Basic Props:

  • label - Label text displayed above the input
  • required - Whether the field is required
  • optionalText - Optional indicator text (e.g., "(Optional)")
  • helperText - Helper text displayed below the input
  • helperTextType - Type of helper text: 'default', 'error', 'success', 'info'
  • characterCount - Character count text (e.g., "0/200")
  • iconLeft - Icon displayed on the left side
  • iconRight - Icon displayed on the right side
  • size - Size: 'xs' (24px), 'sm' (36px), 'md' (40px), 'lg' (44px)
  • state - State: 'default', 'error', 'success'
  • error - Error message (alternative to helperText)
  • showPasswordStrength - Show password strength indicator
  • passwordStrength - Password strength level (0-3)
  • passwordRequirements - Array of password requirements with met status
  • disabled - Disable the input
  • All standard HTML input attributes (type, placeholder, value, onChange, etc.)

Examples:

// Basic input
<Input
  label="Email"
  placeholder="Enter your email"
  value={email}
  onChange={(e) => setEmail(e.target.value)}
/>

// Different sizes
<Input size="xs" label="XSmall" placeholder="XSmall input" />
<Input size="sm" label="Small" placeholder="Small input" />
<Input size="md" label="Medium" placeholder="Medium input" />
<Input size="lg" label="Large" placeholder="Large input" />

// With icons
<Input
  label="Search"
  iconLeft={<SearchIcon />}
  placeholder="Search..."
/>
<Input
  label="Email"
  iconLeft={<EmailIcon />}
  iconRight={<CheckIcon />}
  placeholder="[email protected]"
/>

// Error state
<Input
  label="Email"
  error="Invalid email address"
  value={email}
  onChange={(e) => setEmail(e.target.value)}
/>

// Success state
<Input
  label="Email"
  state="success"
  helperText="Email is valid"
  value={email}
  onChange={(e) => setEmail(e.target.value)}
/>

// Helper text types
<Input
  label="Input with info"
  helperText="This is helpful information"
  helperTextType="info"
/>
<Input
  label="Input with success"
  helperText="Success message"
  helperTextType="success"
/>
<Input
  label="Input with error"
  helperText="Error message"
  helperTextType="error"
/>

// With character count
<Input
  label="Description"
  placeholder="Enter description"
  helperText="Please provide a description"
  characterCount="0/200"
  maxLength={200}
/>

// Password with strength indicator
<Input
  type="password"
  label="Password"
  required
  showPasswordStrength
  passwordStrength={passwordStrength}
  passwordRequirements={[
    { text: "At least 1 uppercase", met: hasUppercase },
    { text: "At least 1 number", met: hasNumber },
    { text: "At least 8 characters", met: length >= 8 }
  ]}
  value={password}
  onChange={(e) => setPassword(e.target.value)}
/>

// Required field
<Input
  label="Required field"
  required
  placeholder="This field is required"
/>

// Optional field
<Input
  label="Optional field"
  optionalText="(Optional)"
  placeholder="This field is optional"
/>

// Disabled
<Input
  label="Disabled input"
  disabled
  value="Cannot edit"
/>

// Different input types
<Input type="email" label="Email" placeholder="[email protected]" />
<Input type="number" label="Age" placeholder="Enter age" />
<Input type="tel" label="Phone" placeholder="+1 (555) 000-0000" />
<Input type="url" label="Website" placeholder="https://example.com" />

States:

  • Default: Neutral border, white background
  • Hover: Darker border on hover
  • Focused: Primary border with focus outline
  • Error: Error border (red)
  • Success: Success border (green)
  • Disabled: Lighter colors, reduced opacity

Password Strength:

  • Strength levels: 0 (none), 1 (weak), 2 (medium), 3 (strong)
  • Visual indicator with colored bars
  • Requirements list with checkmarks

Textarea Component Props

The Textarea component is a multi-line text input field that supports multiple states, helper text, and character count.

Basic Props:

  • label - Label text displayed above the textarea
  • required - Whether the field is required
  • optionalText - Optional indicator text (e.g., "(Optional)")
  • helperText - Helper text displayed below the textarea
  • helperTextType - Type of helper text: 'default', 'error', 'success', 'info'
  • characterCount - Character count text (e.g., "0/200")
  • state - State: 'default', 'error', 'success'
  • error - Error message (alternative to helperText)
  • resizable - Whether the textarea is resizable (default: true)
  • minRows - Minimum number of rows (default: 3)
  • maxRows - Maximum number of rows
  • rows - Number of rows (overrides minRows)
  • disabled - Disable the textarea
  • All standard HTML textarea attributes (placeholder, value, onChange, maxLength, etc.)

Examples:

// Basic textarea
<Textarea
  label="Description"
  placeholder="Enter description"
  value={description}
  onChange={(e) => setDescription(e.target.value)}
/>

// With character count
<Textarea
  label="Comments"
  placeholder="Enter your comments"
  helperText="Please provide your feedback"
  characterCount="0/500"
  maxLength={500}
  value={comments}
  onChange={(e) => setComments(e.target.value)}
/>

// Error state
<Textarea
  label="Description"
  error="Description is required"
  value={description}
  onChange={(e) => setDescription(e.target.value)}
/>

// Success state
<Textarea
  label="Description"
  state="success"
  helperText="Description is valid"
  value={description}
  onChange={(e) => setDescription(e.target.value)}
/>

// Helper text types
<Textarea
  label="Feedback"
  helperText="This is helpful information"
  helperTextType="info"
/>
<Textarea
  label="Feedback"
  helperText="Success message"
  helperTextType="success"
/>
<Textarea
  label="Feedback"
  helperText="Error message"
  helperTextType="error"
/>

// Non-resizable
<Textarea
  label="Fixed size"
  resizable={false}
  rows={4}
/>

// Custom rows
<Textarea
  label="Long description"
  minRows={5}
  maxRows={10}
  placeholder="Enter a longer description"
/>

// Required field
<Textarea
  label="Required field"
  required
  placeholder="This field is required"
/>

// Optional field
<Textarea
  label="Optional field"
  optionalText="(Optional)"
  placeholder="This field is optional"
/>

// Disabled
<Textarea
  label="Disabled textarea"
  disabled
  value="Cannot edit"
/>

States:

  • Default: Neutral border, white background
  • Hover: Primary border on hover
  • Focused: Primary border with focus outline
  • Error: Error border (red)
  • Success: Success border (green)
  • Disabled: Lighter colors, reduced opacity

Resizing:

  • By default, textarea is resizable (both directions)
  • Can be disabled with resizable={false}
  • Resizer icon appears in bottom-right corner when resizable

Pagination Component Props

The Pagination component provides navigation controls for paginated data with support for page navigation, page size selection, and different display styles.

Basic Props:

  • currentPage - Current page number (1-indexed, required)
  • totalPages - Total number of pages (required)
  • totalItems - Total number of items (optional, for range display)
  • itemsPerPage - Number of items per page (optional)
  • itemsPerPageOptions - Available items per page options (default: [10, 20, 30, 40])
  • onPageChange - Callback when page changes (required)
  • onItemsPerPageChange - Callback when items per page changes (optional)
  • size - Size: 'sm' (32px), 'md' (38px), 'lg' (44px) (default: 'md')
  • style - Style variant: 'buttons', 'buttons-data', 'simple-data' (default: 'buttons-data')
  • showFirstLast - Show first/last page buttons (default: true)
  • siblingCount - Number of page buttons on each side of current page (default: 1)
  • disabled - Disable pagination (default: false)

Examples:

// Basic pagination (buttons only)
<Pagination
  currentPage={1}
  totalPages={10}
  onPageChange={(page) => setPage(page)}
  style="buttons"
/>

// With page info
<Pagination
  currentPage={currentPage}
  totalPages={totalPages}
  onPageChange={(page) => setPage(page)}
  style="buttons-data"
/>

// With page size selector and range display
<Pagination
  currentPage={currentPage}
  totalPages={totalPages}
  totalItems={100}
  itemsPerPage={10}
  itemsPerPageOptions={[10, 20, 30, 40]}
  onPageChange={(page) => setPage(page)}
  onItemsPerPageChange={(size) => setItemsPerPage(size)}
  style="buttons-data"
/>

// Simple data style (shows range and page size selector)
<Pagination
  currentPage={currentPage}
  totalPages={totalPages}
  totalItems={100}
  itemsPerPage={10}
  itemsPerPageOptions={[10, 20, 30, 40]}
  onPageChange={(page) => setPage(page)}
  onItemsPerPageChange={(size) => setItemsPerPage(size)}
  style="simple-data"
/>

// Different sizes
<Pagination
  currentPage={currentPage}
  totalPages={totalPages}
  size="sm"
  onPageChange={(page) => setPage(page)}
/>
<Pagination
  currentPage={currentPage}
  totalPages={totalPages}
  size="md"
  onPageChange={(page) => setPage(page)}
/>
<Pagination
  currentPage={currentPage}
  totalPages={totalPages}
  size="lg"
  onPageChange={(page) => setPage(page)}
/>

// Without first/last buttons
<Pagination
  currentPage={currentPage}
  totalPages={totalPages}
  showFirstLast={false}
  onPageChange={(page) => setPage(page)}
/>

// Custom sibling count (more page numbers visible)
<Pagination
  currentPage={currentPage}
  totalPages={20}
  siblingCount={2}
  onPageChange={(page) => setPage(page)}
/>

// Disabled
<Pagination
  currentPage={currentPage}
  totalPages={totalPages}
  disabled
  onPageChange={(page) => setPage(page)}
/>

Styles:

  • buttons: Shows only navigation buttons (first, previous, page numbers, next, last)
  • buttons-data: Shows navigation buttons + "Page X of Y" info
  • simple-data: Shows page size selector + "X to Y of Z" range + navigation buttons

Features:

  • Smart ellipsis: Automatically shows ellipsis when there are many pages
  • Active page highlighting: Current page is visually distinct
  • Disabled states: First/previous disabled on first page, next/last disabled on last page
  • Keyboard accessible: All buttons have proper ARIA labels
  • Page size selector: Integrated dropdown for changing items per page
  • Range display: Shows "X to Y of Z" when totalItems is provided

Progress Bar Component Props

The Progress Bar component displays progress as a visual bar with percentage label.

Basic Props:

  • value - Progress value (0-100, required)
  • max - Maximum value (default: 100)
  • size - Size: 'sm' (track: 8px, fill: 6px), 'md' (track: 8px, fill: 8px) (default: 'md')
  • color - Color variant: 'primary', 'success', 'error', 'warning', 'neutral' (default: 'primary')
  • showLabel - Whether to show the percentage label (default: true)
  • formatLabel - Custom label format function
  • disabled - Disable the progress bar (default: false)

Examples:

// Basic progress bar
<ProgressBar value={50} />

// Different colors
<ProgressBar value={75} color="primary" />
<ProgressBar value={60} color="success" />
<ProgressBar value={30} color="error" />
<ProgressBar value={45} color="warning" />
<ProgressBar value={80} color="neutral" />

// Different sizes
<ProgressBar value={50} size="sm" />
<ProgressBar value={50} size="md" />

// Without label
<ProgressBar value={50} showLabel={false} />

// Custom label format
<ProgressBar
  value={50}
  formatLabel={(value) => `${value}% complete`}
/>

// Custom max value
<ProgressBar
  value={25}
  max={50}
  formatLabel={(value, max) => `${value}/${max}`}
/>

// Disabled
<ProgressBar value={50} disabled />

// Different progress values
<ProgressBar value={0} />
<ProgressBar value={25} />
<ProgressBar value={50} />
<ProgressBar value={75} />
<ProgressBar value={100} />

Colors:

  • Primary: Blue (#2176cc)
  • Success: Green (#2d7d32)
  • Error: Red (#c52828)
  • Warning: Orange (#f9a825)
  • Neutral: Gray (#717171)

Sizes:

  • Small: Track 8px, Fill 6px
  • Medium: Track 8px, Fill 8px

Features:

  • Smooth animation: Progress changes animate smoothly
  • Accessible: Proper ARIA attributes for screen readers
  • Customizable: Custom label format and styling
  • Value clamping: Automatically clamps value between 0 and max
  • Disabled state: Visual feedback when disabled

Progress Stepper Component Props

The Progress Stepper component displays a sequence of steps in a process with different states and visual indicators.

Basic Props:

  • steps - Array of step objects (required)
  • variant - Visual variant: 'hierarchy', 'circle', 'circle-image', 'image', 'corner-radius', 'number-circle', 'image-corner-radius' (default: 'hierarchy')
  • alignment - Text alignment: 'hierarchy', 'center', 'left' (default: 'hierarchy')

Step Object Props:

  • title - Step title (required)
  • description - Step description/subtitle (optional)
  • state - Step state: 'default', 'active', 'completed', 'disabled' (default: 'default')
  • number - Step number (for number-circle variant, defaults to index + 1)
  • icon - Step icon (for corner-radius variant)
  • image - Step image URL or element (for image variants)
  • onClick - Click handler for the step
  • clickable - Whether the step is clickable (default: true if onClick provided)

Examples:

// Basic hierarchy stepper (text only with top border)
<ProgressStepper
  steps={[
    { title: 'Step 1', description: 'Description 1', state: 'completed' },
    { title: 'Step 2', description: 'Description 2', state: 'active' },
    { title: 'Step 3', description: 'Description 3', state: 'default' }
  ]}
/>

// Circle variant with numbers
<ProgressStepper
  variant="circle"
  steps={[
    { title: 'Step 1', number: 1, state: 'completed' },
    { title: 'Step 2', number: 2, state: 'active' },
    { title: 'Step 3', number: 3, state: 'default' }
  ]}
/>

// Circle with images
<ProgressStepper
  variant="circle-image"
  steps={[
    { title: 'Step 1', image: '/image1.jpg', state: 'completed' },
    { title: 'Step 2', image: '/image2.jpg', state: 'active' }
  ]}
/>

// Image variant
<ProgressStepper
  variant="image"
  steps={[
    { title: 'Step 1', image: '/image1.jpg', state: 'completed' },
    { title: 'Step 2', image: '/image2.jpg', state: 'active' }
  ]}
/>

// Corner radius variant
<ProgressStepper
  variant="corner-radius"
  steps={[
    { title: 'Step 1', icon: <Icon />, state: 'completed' },
    { title: 'Step 2', icon: <Icon />, state: 'active' }
  ]}
/>

// Number circle variant
<ProgressStepper
  variant="number-circle"
  steps={[
    { title: 'Step 1', number: 1, state: 'completed' },
    { title: 'Step 2', number: 2, state: 'active' },
    { title: 'Step 3', number: 3, state: 'default' }
  ]}
/>

// Image corner radius variant
<ProgressStepper
  variant="image-corner-radius"
  steps={[
    { title: 'Step 1', image: '/image1.jpg', state: 'completed' },
    { title: 'Step 2', image: '/image2.jpg', state: 'active' }
  ]}
/>

// Center aligned
<ProgressStepper
  variant="circle"
  alignment="center"
  steps={[
    { title: 'Step 1', number: 1, state: 'completed' },
    { title: 'Step 2', number: 2, state: 'active' }
  ]}
/>

// Left aligned
<ProgressStepper
  variant="circle"
  alignment="left"
  steps={[
    { title: 'Step 1', number: 1, state: 'completed' },
    { title: 'Step 2', number: 2, state: 'active' }
  ]}
/>

// Clickable steps
<ProgressStepper
  variant="circle"
  steps={[
    { title: 'Step 1', number: 1, state: 'completed', onClick: () => goToStep(1) },
    { title: 'Step 2', number: 2, state: 'active', onClick: () => goToStep(2) },
    { title: 'Step 3', number: 3, state: 'default', onClick: () => goToStep(3) }
  ]}
/>

// Disabled step
<ProgressStepper
  variant="circle"
  steps={[
    { title: 'Step 1', number: 1, state: 'completed' },
    { title: 'Step 2', number: 2, state: 'disabled' },
    { title: 'Step 3', number: 3, state: 'default' }
  ]}
/>

States:

  • Default: Gray border, gray text
  • Active: Primary blue border and text
  • Completed: Success green border and text, checkmark icon
  • Disabled: Lighter colors, reduced opacity

Variants:

  • hierarchy: Text only with top border indicator
  • circle: Circular indicator with number or checkmark
  • circle-image: Circular indicator with image
  • image: Image indicator only
  • corner-radius: Rounded square indicator with icon/number
  • number-circle: Rounded square with number
  • image-corner-radius: Rounded square with image

Alignments:

  • hierarchy: Indicator and text side by side (horizontal)
  • center: Indicator above text, both centered
  • left: Indicator above text, both left-aligned

Features:

  • Visual state indicators: Top border color changes based on state
  • Multiple indicator styles: Circle, rounded square, image, or text-only
  • Clickable steps: Optional click handlers for navigation
  • Auto-numbering: Automatically numbers steps if not provided
  • Responsive: Adapts to different screen sizes

Scrollbar Component Props

The Scrollbar component provides custom-styled scrollbars for scrollable containers. It wraps content and applies scrollbar styling using CSS.

Basic Props:

  • children - Content to wrap with scrollbar styling (required)
  • orientation - Scrollbar orientation: 'vertical', 'horizontal', 'both' (default: 'vertical')
  • maxHeight - Maximum height for vertical scrolling
  • maxWidth - Maximum width for horizontal scrolling
  • alwaysVisible - Whether to always show scrollbar (default: false)
  • className - Custom className

Examples:

// Basic vertical scrollbar
<Scrollbar maxHeight="240px">
  <div>
    <p>Long content here...</p>
    <p>More content...</p>
    <p>Even more content...</p>
  </div>
</Scrollbar>

// Horizontal scrollbar
<Scrollbar orientation="horizontal" maxWidth="400px">
  <div style={{ width: '800px', display: 'flex', gap: '16px' }}>
    <div>Item 1</div>
    <div>Item 2</div>
    <div>Item 3</div>
    <div>Item 4</div>
  </div>
</Scrollbar>

// Both orientations
<Scrollbar orientation="both" maxHeight="240px" maxWidth="400px">
  <div style={{ width: '800px', height: '500px' }}>
    Large content here...
  </div>
</Scrollbar>

// Always visible scrollbar
<Scrollbar alwaysVisible maxHeight="240px">
  <div>Content here...</div>
</Scrollbar>

// Using global CSS class (alternative approach)
<div className="mw-scrollbar-global" style={{ maxHeight: '240px', overflow: 'auto' }}>
  <div>Content with global scrollbar styling</div>
</div>

Styling:

  • Width: 16px scrollbar width
  • Thumb Color: Neutral gray (#f3f3f3)
  • Border Radius: 8px rounded corners
  • Hover: Darker gray on hover
  • Active: Even darker gray when dragging
  • Padding: 4px padding around scrollbar track

Browser Support:

  • Webkit browsers (Chrome, Safari, Edge): Full support with ::-webkit-scrollbar pseudo-elements
  • Firefox: Uses scrollbar-width and scrollbar-color properties
  • Other browsers: Falls back to default browser scrollbar

Features:

  • Cross-browser support: Works in Chrome, Safari, Edge, and Firefox
  • Customizable: Uses design tokens for colors and spacing
  • Lightweight: Pure CSS implementation
  • Global utility: Can also use mw-scrollbar-global class directly

Flex Component Props

The Flex component is a utility component for creating flexible layouts with consistent spacing using design system tokens.

Basic Props:

  • children - Flex container content (required)
  • direction - Flex direction: 'row', 'column', 'row-reverse', 'column-reverse' (default: 'row')
  • align - Align items (cross-axis): 'start', 'end', 'center', 'stretch', 'baseline' (default: 'stretch')
  • justify - Justify content (main-axis): 'start', 'end', 'center', 'between', 'around', 'evenly' (default: 'start')
  • gap - Gap between flex items: spacing token key ('3xs', '2xs', 'xs', 's', 'm', 'l', 'xl', '2xl', '3xl', '4xl', '5xl', '6xl') or custom CSS value (e.g., '20px')
  • wrap - Flex wrap: 'nowrap', 'wrap', 'wrap-reverse' (default: 'nowrap')
  • fullWidth - Whether container should take full width (default: false)
  • fullHeight - Whether container should take full height (default: false)
  • className - Custom className for additional styling
  • All standard HTML div attributes are supported

Examples:

// Basic horizontal flex
<Flex>
  <div>Item 1</div>
  <div>Item 2</div>
  <div>Item 3</div>
</Flex>

// Vertical flex with gap
<Flex direction="column" gap="m">
  <div>Item 1</div>
  <div>Item 2</div>
  <div>Item 3</div>
</Flex>

// Centered content
<Flex justify="center" align="center" fullHeight>
  <div>Centered content</div>
</Flex>

// Space between items
<Flex justify="between" gap="l">
  <Button>Left</Button>
  <Button>Right</Button>
</Flex>

// Space around items
<Flex justify="around" gap="xl">
  <Badge>Badge 1</Badge>
  <Badge>Badge 2</Badge>
  <Badge>Badge 3</Badge>
</Flex>

// Wrapping flex with custom gap
<Flex wrap="wrap" gap="2xl">
  {items.map(item => (
    <Card key={item.id}>{item.content}</Card>
  ))}
</Flex>

// Column layout with spacing
<Flex direction="column" gap="s" fullWidth>
  <Input label="Email" />
  <Input label="Password" type="password" />
  <Button fullWidth>Submit</Button>
</Flex>

// Custom gap value
<Flex gap="30px">
  <div>Item 1</div>
  <div>Item 2</div>
</Flex>

// Reverse direction
<Flex direction="row-reverse" gap="m">
  <div>Item 1</div>
  <div>Item 2</div>
  <div>Item 3</div>
</Flex>

Direction Options:

  • 'row' - Horizontal layout (left to right)
  • 'column' - Vertical layout (top to bottom)
  • 'row-reverse' - Horizontal layout (right to left)
  • 'column-reverse' - Vertical layout (bottom to top)

Align Options:

  • 'start' - Align items to start of cross-axis
  • 'end' - Align items to end of cross-axis
  • 'center' - Center items on cross-axis
  • 'stretch' - Stretch items to fill cross-axis
  • 'baseline' - Align items to their baseline

Justify Options:

  • 'start' - Align content to start of main-axis
  • 'end' - Align content to end of main-axis
  • 'center' - Center content on main-axis
  • 'between' - Space between items (space-between)
  • 'around' - Space around items (space-around)
  • 'evenly' - Equal space around items (space-evenly)

Gap Values:

  • Use spacing tokens: '3xs' (2px), '2xs' (4px), 'xs' (8px), 's' (12px), 'm' (16px), 'l' (20px), 'xl' (24px), '2xl' (32px), '3xl' (40px), '4xl' (48px), '5xl' (56px), '6xl' (64px)
  • Or use custom CSS values: '20px', '1rem', '2em', etc.

Features:

  • Design system integration: Uses spacing tokens for consistent gaps
  • Flexible: Supports all common flexbox patterns
  • Type-safe: Full TypeScript support
  • Customizable: Accepts custom gap values and all HTML div attributes
  • Lightweight: Pure CSS flexbox implementation

CSS Custom Properties

All colors are available as CSS custom properties:

  • --mw-primary-{100-1000}
  • --mw-secondary-{100-1000}
  • --mw-neutral-{100-1000}
  • --mw-success-{100-1000}
  • --mw-error-{100-1000}
  • --mw-warning-{100-1000}
  • --mw-grey-{100-1000}
  • --mw-info-{100-1000}

Development

# Install dependencies
pnpm install

# Build the library
pnpm build

# Watch mode for development
pnpm dev

# Type check
pnpm type-check

Publishing

# Build first
pnpm build

# Publish to npm (public)
npm publish --access public

# For future updates, bump version first
npm version patch  # or minor, major
npm publish

Update Workflow

  1. Make changes in mw-design-system
  2. Test locally
  3. Bump version: npm version patch (or minor/major)
  4. Build: pnpm build
  5. Publish: npm publish --access public
  6. Update in products: pnpm update mw-design-library

License

MIT