Mikkel Manniche
Dansk English Deutsch

Template · React · HTML

Health overview

A health dashboard in Tiles: three vital figures, a patient card, a score dial and calorie bars with macros.

Preview

React

Install the template with shadcn. It lands in your project as ordinary components that you edit yourself.

npx shadcn@latest add https://mikkelmanniche.dk/lab/r/health-overview.json
npx shadcn@latest add @manniche/health-overview
health-overview.tsx
// A health overview: three vital figures, a patient card, a score dial and calorie bars with macros.
import { cn } from '@/lib/utils'
import { BarChart, type BarPoint } from '@/registry/manniche/bar-chart/bar-chart'
import { BigNumber } from '@/registry/manniche/chart-kit/chart-kit'
import { DataTile, TileFact } from '@/registry/manniche/data-tile/data-tile'
import { Dial, type DialZone } from '@/registry/manniche/dial/dial'
import { Donut, type DonutDatum } from '@/registry/manniche/donut/donut'

export type HealthOverviewData = {
  vitals: { id: string; title: string; value: number; unit: string }[]
  patient: { name: string; details: { label: string; value: string }[] }
  score: { title: string; value: number; zones: DialZone[]; caption: string }
  calories: { title: string; points: BarPoint[]; macros: DonutDatum[]; macrosTitle: string }
}

export type HealthOverviewLabels = { note?: string }

export type HealthOverviewProps = { data: HealthOverviewData; labels?: HealthOverviewLabels; className?: string }

export function HealthOverview({ data, labels, className }: HealthOverviewProps) {
  const note = labels?.note ?? 'Example data.'
  return (
    <div className={cn('@container w-full rounded-2xl bg-background p-3 text-foreground', className)}>
      <div className="grid grid-cols-1 gap-3 @lg:grid-cols-3 @3xl:grid-cols-6">
        {data.vitals.map((v) => (
          <div key={v.id} className="@3xl:col-span-2">
            <DataTile className="h-full" title={v.title} density="compact">
              <TileFact aside={<span className="text-sm text-muted-foreground">{v.unit}</span>}>
                <BigNumber value={v.value} size="lg" />
              </TileFact>
            </DataTile>
          </div>
        ))}
        <div className="@lg:col-span-3 @3xl:col-span-3">
          <DataTile className="h-full" title={data.patient.name} inverted>
            <dl className="grid grid-cols-2 gap-3 text-sm">
              {data.patient.details.map((d) => (
                <div key={d.label}>
                  <dt className="opacity-70">{d.label}</dt>
                  <dd className="font-medium">{d.value}</dd>
                </div>
              ))}
            </dl>
          </DataTile>
        </div>
        <div className="@lg:col-span-3 @3xl:col-span-3">
          <DataTile className="h-full" title={data.score.title} footer={note}>
            <Dial value={data.score.value} min={0} max={100} zones={data.score.zones} label={data.score.title} caption={data.score.caption} />
          </DataTile>
        </div>
        <div className="@lg:col-span-3 @3xl:col-span-4">
          <DataTile className="h-full" title={data.calories.title} footer={note}>
            <BarChart data={data.calories.points} label={data.calories.title} format={{ suffix: ' kcal' }} />
          </DataTile>
        </div>
        <div className="@lg:col-span-3 @3xl:col-span-2">
          <DataTile className="h-full" title={data.calories.macrosTitle} footer={note}>
            <Donut data={data.calories.macros} label={data.calories.macrosTitle} format={{ suffix: ' g' }} />
          </DataTile>
        </div>
      </div>
    </div>
  )
}
health-overview-demo.tsx
import { HealthOverview, type HealthOverviewData } from '@/components/health-overview'

// Example data only. The person and all figures are invented; this is not medical information.
const data: HealthOverviewData = {
  vitals: [
    { id: 'hr', title: 'Heart rate', value: 64, unit: 'bpm' },
    { id: 'sl', title: 'Sleep', value: 7, unit: 'hours' },
    { id: 'st', title: 'Steps', value: 8200, unit: 'today' },
  ],
  patient: {
    name: 'Sample Patient',
    details: [
      { label: 'Age', value: '41' },
      { label: 'Height', value: '178 cm' },
      { label: 'Last visit', value: '2 Oct 2026' },
      { label: 'Plan', value: 'Example plan' },
    ],
  },
  score: {
    title: 'Wellness score',
    value: 74,
    caption: 'example score',
    zones: [
      { label: 'Low', to: 40 },
      { label: 'Fair', to: 70 },
      { label: 'Good', to: 100 },
    ],
  },
  calories: {
    title: 'Calories, last 7 days',
    points: ['Wed', 'Thu', 'Fri', 'Sat', 'Sun', 'Mon', 'Tue'].map((label, i) => ({ label, value: [2100, 1950, 2250, 2000, 2300, 2600, 1900][i] })),
    macrosTitle: 'Macros today',
    macros: [
      { label: 'Carbs', value: 240 },
      { label: 'Protein', value: 110 },
      { label: 'Fat', value: 70 },
    ],
  },
}

export default function HealthOverviewDemo() {
  return <HealthOverview data={data} />
}

HTML

The whole page as one file with all CSS built in and no JavaScript. Toggles, tabs and filters work with HTML and CSS alone.

Open in a new tab

The colour in your own app

If you already have shadcn set up, these variables give the same colour. Light in :root, dark in .dark.

:root {
  --primary: oklch(0.52 0.2 262);
  --primary-foreground: oklch(0.99 0 0);
  --accent: oklch(0.95 0.034 262);
  --accent-foreground: oklch(0.36 0.140 262);
  --ring: oklch(0.52 0.2 262);
}

.dark {
  --primary: oklch(0.74 0.150 262);
  --primary-foreground: oklch(0.18 0.024 262);
  --accent: oklch(0.29 0.050 262);
  --accent-foreground: oklch(0.9 0.050 262);
  --ring: oklch(0.74 0.150 262);
}

Install

npx shadcn@latest add https://mikkelmanniche.dk/lab/r/health-overview.json

Dependencies

    Files

    • health-overview.tsx
    • health-overview.html

    Manniche Lab · open source

    Free tools for AI agents and web development.