Get started
Install from the private feed, load the tokens and the icon font once, then import components. The library ships raw source, so there is no build step to wait on.
Authenticate to the package feed
The packages are published to a private Azure Artifacts feed, so npm needs to know where to find the @healthmetrics scope and how to authenticate.
@healthmetrics:registry=https://pkgs.dev.azure.com/<organisation>/_packaging/<feed>/npm/registry/# Windows — writes a token to your user ~/.npmrc
npx vsts-npm-auth -config .npmrc
# macOS / Linux — create a feed PAT in Azure DevOps, then paste the
# base64 block from "Connect to feed" into your ~/.npmrcKeep credentials out of the repo
The committed .npmrc holds only the registry mapping. Tokens belong in your user-level
~/.npmrc, which is never committed. In CI, use the npmAuthenticate@0 pipeline task rather
than a checked-in token.
Install
Add the packages your app needs.
npm install @healthmetrics/ui @healthmetrics/tailwind-config
# @healthmetrics/icons and @healthmetrics/tokens come in as dependencies;
# install them directly only if you want them without the components.Configure your bundler
Because the library ships .tsx source, your app compiles it. In Next.js, transpile the packages. Vite needs no equivalent step.
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
transpilePackages: ["@healthmetrics/ui", "@healthmetrics/icons", "@healthmetrics/tokens"],
};
export default nextConfig;Why ship source?
Compiling in your app is what keeps each component's "use client" boundary exact — bundling is
where those directives get stripped or misplaced. It also means the prop types you see in your
editor are the implementation, never a stale .d.ts. Use "moduleResolution": "bundler" in your
tsconfig.
Load the tokens and point Tailwind at the source
Import Tailwind and the HMS token layer at your app root, and tell Tailwind to scan the library source so its utility classes are not purged.
@import "tailwindcss";
@import "@healthmetrics/tailwind-config"; /* tokens + animation utilities */
/* Tailwind v4 does not scan node_modules — adjust these paths to match your app */
@source "../node_modules/@healthmetrics/ui/src/**/*.{ts,tsx}";
@source "../node_modules/@healthmetrics/icons/src/**/*.{ts,tsx}";Why the @source lines?
Tailwind v4 skips node_modules. Without these, every utility class used inside the library is
purged and the components render completely unstyled.
Load the Material Symbols font
Icons are font ligatures, not SVGs. This step is the one most often missed.
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
<link
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@20..48,400,0,0&display=swap"
rel="stylesheet"
/>Without this, icons render as literal text
<Icon name="exportToExcel" /> renders a ligature. If the font has not loaded, the browser draws
the words export_to_excel where the icon should be. Nothing throws, so it reads like a styling
bug. Under a strict CSP, self-host the variable font instead and keep the CSS family name
material-symbols-rounded.
Set the theme before paint
The token layer keys off a dark class on <html>. Apply it in a blocking script so there is no light/dark flash.
<script
dangerouslySetInnerHTML={{
__html: `(function(){try{var t=localStorage.getItem('hms-theme');
if(!t){t=matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light';}
if(t==='dark')document.documentElement.classList.add('dark');}catch(e){}})();`,
}}
/>const { theme, toggle, mounted } = useTheme();
// The server cannot read localStorage, so `theme` is "light" until mounted.
// Gate theme-dependent output on `mounted` to avoid a hydration mismatch —
// or, better, drive it in CSS with Tailwind's `dark:` variant.
<button onClick={toggle}>{mounted && theme === "dark" ? "Light" : "Dark"}</button>;Use components
Import and compose. Icons are typed against the Material Symbols set.
import { Button, Icon } from "@healthmetrics/ui";
export default function Page() {
return (
<Button>
<Icon name="exportToExcel" size={18} />
Export
</Button>
);
}Import from subpaths in shipped routes
The root barrel re-exports all 65 components, so importing from it pulls recharts, cmdk,
@tanstack/react-table and react-day-picker into your module graph. Every component also has its
own subpath — import { Button } from "@healthmetrics/ui/button" resolves to a single file. Use
the barrel while prototyping, subpaths in production routes.
Contributing to the design system
From a clone of the monorepo:
npm install
npm run dev # this docs site
npm run storybook # component workbench (port 6006)
npm run lint # ESLint
npm run typecheck # TypeScript across every package
npm test # unit + interaction tests
npm run gate:a11y # every story, both themes, WCAG 2.2 AA
npm run verify:packaging # tarball contents, publint, type resolutionNext: explore the components or the brand foundations.
Overview
Brand-aligned, accessible React components for HealthMetrics products — built on shadcn conventions and Tailwind v4 tokens, shipped as source.
Foundations
The design guidelines behind the system — the principles, standards and best practices for building consistent, accessible HealthMetrics interfaces.