A themeable, accessible React component library built with Tailwind CSS 4.
Features
- π¨ Fully Themeable - Customize colors, fonts, border radius, and more using CSS variables
- π’ Multi-Brand Support - Pre-configured themes for BlueHive, Enterprise Health, WebChart, Waggleline, and MIE
- βΏ Accessible - Built with WCAG guidelines in mind, including proper ARIA attributes and keyboard navigation
- π³ Tree-Shakeable - Import only the components you need
- π Dark Mode - Built-in dark mode support with system preference detection
- π¦ Dual Format - ESM and CommonJS support
- π― TypeScript - Full TypeScript support with comprehensive type definitions
- π Storybook - Interactive documentation and component playground
Table of Contents
- Installation
- Quick Start
- Development
- Date & Time Standard
- Storybook
- Using in Other Projects
- Brand System
- Theming
- Components
- Hooks
- Utilities
- Releases
- Contributing
Installation
npm install @mieweb/ui # or yarn add @mieweb/ui # or pnpm add @mieweb/ui
Peer Dependencies
This library requires React 18+ and React DOM 18+:
npm install react react-dom
Quick Start
Option 1: With Tailwind CSS (Recommended)
If your project uses Tailwind CSS 4, you can use the library's Tailwind preset for the best experience:
- Add the preset to your
tailwind.config.js:
// tailwind.config.js module.exports = { presets: [require('@mieweb/ui/tailwind-preset')], content: [ // ... your content './node_modules/@mieweb/ui/dist/**/*.js', ], // Override theme values to match your brand theme: { extend: { colors: { primary: { 500: '#your-brand-color', }, }, }, }, };
- Import and use components:
import { Button, Card, Input } from '@mieweb/ui'; function App() { return ( <Card> <Input label="Email" type="email" /> <Button>Submit</Button> </Card> ); }
Option 2: Pre-compiled CSS
If you're not using Tailwind CSS, you can import the pre-compiled stylesheet:
import '@mieweb/ui/styles.css'; import { Button } from '@mieweb/ui';
Development
Getting Started
- Clone the repository:
git clone https://github.com/mieweb/ui.git
cd ui- Install dependencies:
- Start development mode:
This will watch for changes and rebuild the library automatically.
Available Scripts
| Script | Description |
|---|---|
npm run dev |
Start development mode with watch |
npm run build |
Build the library for production |
npm run storybook |
Start Storybook development server |
npm run build-storybook |
Build Storybook for static hosting |
npm run typecheck |
Run TypeScript type checking |
npm run lint |
Run ESLint |
npm run lint:fix |
Run ESLint with auto-fix |
npm run format |
Check code formatting with Prettier |
npm run format:fix |
Fix code formatting with Prettier |
npm run test |
Run tests |
npm run test:watch |
Run tests in watch mode |
Date & Time Standard
For UI/UX date and time behavior, this project uses Luxon as the preferred library.
Guidelines
- Use
DateTimefromluxonfor all new date/time parsing, formatting, and comparisons. - Keep timezone explicit when logic depends on business rules (for example: office hours, βopen nowβ, appointment windows).
- Use IANA timezone identifiers (for example:
America/New_York) instead of abbreviations. - Prefer storing/transmitting ISO-8601 values and convert for display at the component edge.
- Avoid adding new date logic with raw
Datemath unless there is a clear performance or compatibility reason.
Examples
import { DateTime } from 'luxon'; const localDisplay = DateTime.fromISO(timestamp).toFormat('LLL d, yyyy h:mm a'); const inProviderZone = DateTime.fromISO(timestamp, { zone: 'America/New_York', }); const isOpen = DateTime.now().setZone('America/New_York') < inProviderZone.plus({ hours: 1 });
Storybook
Storybook provides interactive documentation and a component playground where you can explore all components with different props and themes.
Running Storybook
This starts the Storybook development server at http://localhost:6006.
Features in Storybook
- Component Explorer: Browse all components with live examples
- Props Documentation: See all available props for each component
- Theme Switcher: Toggle between light and dark modes
- Brand Switcher: Preview components with different brand themes (BlueHive, Enterprise Health, WebChart, Waggleline, MIE)
- Accessibility Panel: Check accessibility compliance for each component
- Controls: Interactively modify component props
Building Storybook
To build a static version of Storybook for deployment:
The output will be in the storybook-static directory.
Using in Other Projects
Method 1: NPM Package (Recommended)
Once published, install the package in your project:
Then import components:
import { Button, Card, Input, ThemeProvider } from '@mieweb/ui'; import '@mieweb/ui/styles.css'; // or use a brand CSS file function App() { return ( <ThemeProvider> <Card> <Card.Header> <Card.Title>Welcome</Card.Title> </Card.Header> <Card.Content> <Input label="Email" type="email" placeholder="you@example.com" /> <Button className="mt-4">Submit</Button> </Card.Content> </Card> </ThemeProvider> ); }
Method 2: Local Development (npm link)
For local development across projects:
- In the @mieweb/ui directory:
cd /path/to/mieweb-ui
npm run build
npm link- In your consuming project:
cd /path/to/your-project
npm link @mieweb/ui- Import and use components:
import { Button } from '@mieweb/ui'; import '@mieweb/ui/dist/styles.css';
Method 3: Direct Path Import
For monorepo setups or when you want to reference the source directly:
// In your consuming project's package.json { "dependencies": { "@mieweb/ui": "file:../mieweb-ui" } }
Then run npm install and import as usual.
Using with Different Frameworks
Next.js
// app/layout.tsx or pages/_app.tsx import '@mieweb/ui/brands/bluehive.css'; import { ThemeProvider } from '@mieweb/ui'; export default function RootLayout({ children }) { return ( <html lang="en"> <body> <ThemeProvider>{children}</ThemeProvider> </body> </html> ); }
Vite
// main.tsx import React from 'react'; import ReactDOM from 'react-dom/client'; import '@mieweb/ui/brands/enterprise-health.css'; import { ThemeProvider } from '@mieweb/ui'; import App from './App'; ReactDOM.createRoot(document.getElementById('root')!).render( <React.StrictMode> <ThemeProvider> <App /> </ThemeProvider> </React.StrictMode> );
Meteor
// client/main.tsx import { Meteor } from 'meteor/meteor'; import React from 'react'; import { createRoot } from 'react-dom/client'; import '@mieweb/ui/brands/bluehive.css'; import { ThemeProvider } from '@mieweb/ui'; import App from '/imports/ui/App'; Meteor.startup(() => { const container = document.getElementById('react-target'); const root = createRoot(container!); root.render( <ThemeProvider> <App /> </ThemeProvider> ); });
Brand System
The library includes pre-configured themes for multiple brands. Each brand has its own design system with unique colors, typography, border radius, and shadows.
Available Brands
| Brand | Primary Color | Font | Description |
|---|---|---|---|
| BlueHive | #27AAE1 (Blue) |
Nunito | DOT Physical scheduling and healthcare compliance |
| Enterprise Health | #6E2B68 (Burgundy) |
Jost | Employee health and occupational medicine |
| WebChart | #F5841F (Orange) |
Inter | Future-ready electronic health record system |
| Waggleline | #17AEED (Blue) |
Inter | Experience visualization and orchestration |
| MIE | #27AE60 (Green) |
Inter | Healthcare software and services |
Using a Brand Theme
Method 1: Import the brand CSS file
// Import the brand's CSS file import '@mieweb/ui/brands/enterprise-health.css'; import { Button, Card } from '@mieweb/ui';
Method 2: Use the ThemeProvider with brand
import { ThemeProvider } from '@mieweb/ui'; import { enterpriseHealthBrand } from '@mieweb/ui/brands'; function App() { return ( <ThemeProvider brand={enterpriseHealthBrand}> <YourApp /> </ThemeProvider> ); }
Method 3: Tailwind CSS preset
// tailwind.config.js const { enterpriseHealthBrand } = require('@mieweb/ui/brands'); const { createBrandPreset } = require('@mieweb/ui/brands/types'); module.exports = { presets: [createBrandPreset(enterpriseHealthBrand)], // ... };
Brand Design Tokens
Each brand defines the following design tokens:
Enterprise Health
Extracted from enterprisehealth.com:
/* Primary: Burgundy/Purple */ --mieweb-primary-600: #6e2b68; /* Secondary: Deep Teal Blue (for gradients) */ --mieweb-secondary: #00497a; /* Accent: Gold/Yellow (logo) */ --mieweb-accent: #f8b700; /* Brand Gradient */ --mieweb-gradient: linear-gradient(111.02deg, #00497a, #6e2b68); /* Typography */ --mieweb-font-sans: 'Jost', ui-sans-serif, system-ui, sans-serif; /* Border Radius (larger, more rounded) */ --mieweb-radius-sm: 0.375rem; /* 6px - badges */ --mieweb-radius-md: 0.625rem; /* 10px - buttons */ --mieweb-radius-lg: 0.75rem; /* 12px - inputs */ --mieweb-radius-2xl: 1.5rem; /* 24px - cards */ /* Shadows (subtle, layered) */ --mieweb-shadow-card: 0 16px 32px 0 rgba(34, 35, 38, 0.05), 0 8px 16px 0 rgba(34, 35, 38, 0.05);
BlueHive
/* Primary: Blue */ --mieweb-primary-500: #27aae1; /* Typography */ --mieweb-font-sans: 'Nunito', ui-sans-serif, system-ui, sans-serif;
Creating a Custom Brand
You can create your own brand configuration:
import type { BrandConfig } from '@mieweb/ui/brands/types'; export const myBrand: BrandConfig = { name: 'my-brand', displayName: 'My Brand', description: 'Custom brand for my application', colors: { primary: { 50: '#f0f9ff', // ... full color scale 50-950 600: '#0284c7', // Main brand color // ... }, light: { background: '#ffffff', foreground: '#171717', // ... semantic colors }, dark: { background: '#171717', foreground: '#fafafa', // ... semantic colors }, }, typography: { fontFamily: { sans: ['Your Font', 'ui-sans-serif', 'system-ui', 'sans-serif'], mono: ['ui-monospace', 'SFMono-Regular', 'Menlo', 'monospace'], }, }, borderRadius: { none: '0', sm: '0.25rem', md: '0.5rem', lg: '0.75rem', xl: '1rem', '2xl': '1.5rem', full: '9999px', }, boxShadow: { card: '0 1px 3px 0 rgb(0 0 0 / 0.1)', dropdown: '0 4px 6px -1px rgb(0 0 0 / 0.1)', modal: '0 10px 15px -3px rgb(0 0 0 / 0.1)', }, };
Theming
The library uses CSS custom properties for theming. Override these variables to customize the appearance:
:root { /* Primary color scale */ --mieweb-primary-500: #27aae1; /* Semantic colors */ --mieweb-background: hsl(0 0% 100%); --mieweb-foreground: hsl(222.2 84% 4.9%); /* Border radius */ --mieweb-radius-md: 0.5rem; /* Font */ --mieweb-font-sans: 'Your Font', sans-serif; }
Dark Mode
The library supports dark mode via the .dark class or data-theme="dark" attribute on a parent element:
import { ThemeProvider, useThemeContext, Button } from '@mieweb/ui'; function ThemeToggle() { const { resolvedTheme, setTheme } = useThemeContext(); return ( <Button onClick={() => setTheme(resolvedTheme === 'dark' ? 'light' : 'dark')} > Toggle Theme </Button> ); } function App() { return ( <ThemeProvider> <ThemeToggle /> </ThemeProvider> ); }
Components
Primitives
Button- Multi-variant button with loading stateInput- Text input with label, error, and helper textCard- Container component with header, content, and footerText- Typography component with variantsBadge- Status indicators and labelsAlert- Feedback messages
Specialized Inputs
PhoneInput- US phone number formattingDateInput- Date input with validation modes (DOB, expiration, etc.)
Overlays
Tooltip- Accessible tooltip with multiple placementsDropdown- Dropdown menu with items, separators, and labels
Utilities
VisuallyHidden- Screen reader only contentThemeProvider- Theme context provider
Hooks
useTheme()- Theme state managementuseClickOutside()- Detect clicks outside an elementuseEscapeKey()- Handle escape key pressuseFocusTrap()- Trap focus within a containerusePrefersReducedMotion()- Detect reduced motion preference
Utilities
Class Names
import { cn } from '@mieweb/ui/utils'; // Merge classes with Tailwind conflict resolution cn('px-4 py-2', isActive && 'bg-primary-500', className);
Phone Utilities
import { formatPhoneNumber, isValidPhoneNumber } from '@mieweb/ui/utils'; formatPhoneNumber('5551234567'); // "(555) 123-4567" isValidPhoneNumber('5551234567'); // true
Date Utilities
import { formatDateValue, calculateAge, isValidDate } from '@mieweb/ui/utils'; formatDateValue('01152024'); // "01/15/2024" calculateAge('01/15/1990'); // 34 isValidDate('01/15/2024'); // true
Tree-Shaking
Import components directly for optimal bundle size:
// Import only what you need import { Button } from '@mieweb/ui/components/Button'; import { useTheme } from '@mieweb/ui/hooks'; import { cn } from '@mieweb/ui/utils';
TypeScript
All components are fully typed. Import types as needed:
import type { ButtonProps, InputProps, Theme } from '@mieweb/ui';
Releases
This package uses automated releases via GitHub Actions. There are two release channels:
Release Channels
| Channel | npm Tag | Install Command | Description |
|---|---|---|---|
| Stable | latest |
npm install @mieweb/ui |
Production-ready releases |
| Prerelease | next |
npm install @mieweb/ui@next |
Latest from main branch |
Prerelease (Automatic)
Every push to the main branch automatically publishes a prerelease version to npm:
- Version format:
x.y.z-dev.{run_number}(e.g.,0.1.0-dev.45) - npm tag:
next - Install:
npm install @mieweb/ui@next
This allows consumers to test the latest changes before a stable release.
Stable Release (Manual)
To create a stable release:
- Go to the repository on GitHub
- Navigate to Actions β Create Stable Release
- Click Run workflow
- Select the version bump type:
patch- Bug fixes (0.1.0 β 0.1.1)minor- New features (0.1.0 β 0.2.0)major- Breaking changes (0.1.0 β 1.0.0)
- Click Run workflow
The workflow will:
- Bump the version in
package.json - Commit and push the change
- Create a git tag (e.g.,
v0.2.0) - Trigger the release workflow which publishes to npm and creates a GitHub Release
Manual Tag Release
You can also create a release by pushing a version tag directly:
# Create and push a tag
git tag v1.0.0
git push origin v1.0.0The release workflow will automatically:
- Run tests and build
- Publish to npm with the appropriate tag (
latestfor stable,nextfor prereleases likev1.0.0-beta.1) - Create a GitHub Release with auto-generated release notes
Version Guidelines
We follow Semantic Versioning:
- MAJOR version for incompatible API changes
- MINOR version for backwards-compatible functionality additions
- PATCH version for backwards-compatible bug fixes
Contributing
We welcome contributions! Here's how to get started:
Development Workflow
- Fork and clone the repository
- Install dependencies:
npm install - Create a branch:
git checkout -b feature/your-feature - Start Storybook:
npm run storybook - Make your changes
- Run checks:
npm run typecheck # TypeScript npm run lint # ESLint npm run format # Prettier npm run test # Tests
- Commit your changes following Conventional Commits
- Push and create a Pull Request
Adding a New Component
- Create a new directory in
src/components/YourComponent/ - Add the component file:
YourComponent.tsx - Add the index export:
index.ts - Add Storybook stories:
YourComponent.stories.tsx - Export from
src/components/index.ts - Add to the README components list
Adding a New Brand
- Create
src/brands/your-brand.tswith theBrandConfig - Create
src/brands/your-brand.csswith CSS variables - Export from
src/brands/index.ts - Add to the README brands table
License
Copyright Β© 2026 Medical Informatics Engineering, Inc. All rights reserved.
This software is source available with the following terms:
- β Free for open source projects - Use, modify, and distribute freely in open source projects with attribution
- β Free for non-commercial use - Personal projects, education, research
- πΌ Commercial license required - For proprietary products or commercial use, contact licensing@mieweb.com
See the LICENSE file for full details.