mw-design-library
v2.0.1
Published
MW Design System - Shared component library for all MW products
Downloads
237
Maintainers
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-libraryUsage
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 / 56pxtypography.display.medium- 32px / 48pxtypography.display.small- 24px / 40px
Heading Styles (SemiBold):
typography.heading.h1- 40px / 56pxtypography.heading.h2- 32px / 48pxtypography.heading.h3- 24px / 40pxtypography.heading.h4- 20px / 32pxtypography.heading.h5- 18px / 24px
Body Styles (SemiBold):
typography.body.large- 18px / 24pxtypography.body.regular- 16px / 20pxtypography.body.medium- 14px / 18pxtypography.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- 1pxborders.m- 2pxborders.l- 4pxborders.xl- 6pxborders['2xl']- 8pxborders['3xl']- 12pxborders['4xl']- 16pxborders['5xl']- 20px
Border Radius:
borderRadius.none- 0pxborderRadius['2xs']- 4pxborderRadius.xs- 8pxborderRadius.s- 12pxborderRadius.m- 16pxborderRadius.l- 20pxborderRadius.xl- 24pxborderRadius['2xl']- 32pxborderRadius['3xl']- 40pxborderRadius['4xl']- 48pxborderRadius['5xl']- 56pxborderRadius.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']- 2pxspacing['2xs']- 4pxspacing.xs- 8pxspacing.s- 12pxspacing.m- 16pxspacing.l- 20pxspacing.xl- 24pxspacing['2xl']- 32pxspacing['3xl']- 40pxspacing['4xl']- 48pxspacing['5xl']- 56pxspacing['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 URLalt- Alt text for the imagename- Full name (used to generate initials)initials- Custom initials to displayicon- React node to display as iconsize- 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 initialstextColor- Custom text color for initialsbordered- Show border around avataronClick- 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 texticonRight- 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 texticonRight- Icon to display after textloading- Show loading spinnerfullWidth- Make button full widthdisabled- Disable the buttononClick- 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 checkboxsize- Size:'sm'(16px),'lg'(20px)checked- Whether the checkbox is checked (controlled)indeterminate- Whether the checkbox is in indeterminate statedisabled- Disable the checkboxonChange- Change handlerdefaultChecked- 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 buttonsize- 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 buttononChange- Change handlername- Name attribute for radio group (required)value- Value of the radio buttondefaultChecked- 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
nameprop - Only one radio button in a group can be selected at a time
- Use
valueprop 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 dropdownrequired- Whether the field is requiredoptionalText- Optional indicator text (e.g., "(Optional)")helperText- Helper text displayed below the dropdowncharacterCount- Character count text (e.g., "0/200")placeholder- Placeholder textoptions- Array of options to displayvalue- Selected value(s):stringfor single select,string[]for multi-selectonChange- Change handlermode- Selection mode:'single'or'multi'optionType- Option display type:'simple','radio','checkbox'searchable- Whether search is enabledsearchPlaceholder- Search placeholder textshowCreateOption- Whether to show "Create new" optioncreateOptionLabel- Create option label textonCreateOption- Create option handlerdisabled- Disable the dropdownopen- Whether the dropdown is open (controlled)onOpenChange- Open/close handlermaxHeight- 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 inputrequired- Whether the field is requiredoptionalText- Optional indicator text (e.g., "(Optional)")helperText- Helper text displayed below the inputhelperTextType- Type of helper text:'default','error','success','info'characterCount- Character count text (e.g., "0/200")iconLeft- Icon displayed on the left sideiconRight- Icon displayed on the right sidesize- Size:'xs'(24px),'sm'(36px),'md'(40px),'lg'(44px)state- State:'default','error','success'error- Error message (alternative to helperText)showPasswordStrength- Show password strength indicatorpasswordStrength- Password strength level (0-3)passwordRequirements- Array of password requirements with met statusdisabled- 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 textarearequired- Whether the field is requiredoptionalText- Optional indicator text (e.g., "(Optional)")helperText- Helper text displayed below the textareahelperTextType- 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 rowsrows- 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 functiondisabled- 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 stepclickable- 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 scrollingmaxWidth- Maximum width for horizontal scrollingalwaysVisible- 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-scrollbarpseudo-elements - Firefox: Uses
scrollbar-widthandscrollbar-colorproperties - 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-globalclass 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-checkPublishing
# 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 publishUpdate Workflow
- Make changes in
mw-design-system - Test locally
- Bump version:
npm version patch(orminor/major) - Build:
pnpm build - Publish:
npm publish --access public - Update in products:
pnpm update mw-design-library
License
MIT
