Skip to content

mieweb/ui

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1,099 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@mieweb/ui

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

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

Optional Add-ons

Heavy or specialized dependencies are kept in separate entry points so they don't bloat the core bundle. Install the peer dependencies for the add-ons you need:

Entry point Install Import path
AG Grid npm install ag-grid-community ag-grid-react @mieweb/ui/ag-grid
DataVis npm install datavis-ace @mieweb/ui/datavis

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:

  1. 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',
        },
      },
    },
  },
};
  1. 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

  1. Clone the repository (including submodules):
git clone --recurse-submodules https://github.com/mieweb/ui.git
cd ui

--recurse-submodules is strongly recommended for first clone so packages/esheet, packages/datavis, and packages/ychart are populated immediately. Without these submodules, eSheet, DataVis, and YChart stories will not work.

If you already cloned without submodules, run:

git submodule update --init --recursive
  1. Install dependencies:
npm install

Use one package manager consistently per clone. The commands below use npm.

  1. Build eSheet packages (required before first Storybook run):
npm run build:esheet

This installs eSheet's own dependencies and compiles all @esheet/* packages.

If packages/esheet is updated later (submodule update, branch switch, or pull), force a fresh rebuild:

npm run rebuild:esheet
  1. Start Storybook:
npm run storybook

This starts the Storybook development server at http://localhost:6006 with all components, including eSheet, DataVis, and YChart.

Library Development (watch mode)

To rebuild the library on file changes (for consumers that link this repo locally):

npm run dev

This watches for source changes and rebuilds automatically. It does not start Storybook.

Available Scripts

Script Description
npm run dev Watch & rebuild the library (for local consumers, not Storybook)
npm run build:esheet Build eSheet submodule packages for first-time setup (skips when already built)
npm run rebuild:esheet Force a full eSheet rebuild after packages/esheet changes
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 DateTime from luxon for 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 Date math 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

npm run 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:

npm run build-storybook

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:

npm install @mieweb/ui

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:

  1. In the @mieweb/ui directory:
cd /path/to/mieweb-ui
npm run build
npm link
  1. In your consuming project:
cd /path/to/your-project
npm link @mieweb/ui
  1. 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 state
  • Input - Text input with label, error, and helper text
  • Card - Container component with header, content, and footer
  • Text - Typography component with variants
  • Badge - Status indicators and labels
  • Alert - Feedback messages

Specialized Inputs

  • PhoneInput - US phone number formatting
  • DateInput - Date input with validation modes (DOB, expiration, etc.)

Overlays

  • Tooltip - Accessible tooltip with multiple placements
  • Dropdown - Dropdown menu with items, separators, and labels

Utilities

  • VisuallyHidden - Screen reader only content
  • ThemeProvider - Theme context provider

Hooks

  • useTheme() - Theme state management
  • useClickOutside() - Detect clicks outside an element
  • useEscapeKey() - Handle escape key press
  • useFocusTrap() - Trap focus within a container
  • usePrefersReducedMotion() - 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:

  1. Go to the repository on GitHub
  2. Navigate to ActionsCreate Stable Release
  3. Click Run workflow
  4. 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)
  5. Click Run workflow

The workflow will:

  1. Bump the version in package.json
  2. Commit and push the change
  3. Create a git tag (e.g., v0.2.0)
  4. 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.0

The release workflow will automatically:

  • Run tests and build
  • Publish to npm with the appropriate tag (latest for stable, next for prereleases like v1.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

  1. Fork and clone the repository
  2. Install dependencies: npm install
  3. Create a branch: git checkout -b feature/your-feature
  4. Start Storybook: npm run storybook
  5. Make your changes
  6. Run checks:
    npm run typecheck  # TypeScript
    npm run lint       # ESLint
    npm run format     # Prettier
    npm run test       # Tests
  7. Commit your changes following Conventional Commits
  8. Push and create a Pull Request

Adding a New Component

  1. Create a new directory in src/components/YourComponent/
  2. Add the component file: YourComponent.tsx
  3. Add the index export: index.ts
  4. Add Storybook stories: YourComponent.stories.tsx
  5. Export from src/components/index.ts
  6. Add to the README components list

Adding a New Brand

  1. Create src/brands/your-brand.ts with the BrandConfig
  2. Create src/brands/your-brand.css with CSS variables
  3. Export from src/brands/index.ts
  4. 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.

Packages

 
 
 

Contributors