Important Alerts Card

Scrollable alert feed with tinted icon circles and date pills.

Feed

A scrollable alert feed with date pills.

Important alerts

12

this week
29 Jun - 5 Jul

High Heart rate

Your heart rate rose above 120 BPM while you seemed to be inactive for 10 minutes starting at 8:59 AM, 12 June.

June, 12

Medical ID

Your emergency contact and allergy information was updated in your Medical ID.

June, 9

Lab results ready

Your latest blood panel ordered by Dr. Mattheus Clarkson is back — cholesterol and glucose are within the normal range.

June, 9

Medication reminder

You missed your 9:00 AM dose of Metoprolol. Take it as soon as possible unless your next dose is near.

June, 8

Irregular sleep

Your bedtime shifted by more than 2 hours on 3 of the last 7 nights, which can affect your sleep score.

June, 6

Low blood oxygen

Your blood oxygen dipped to 93% for a short period during sleep on the night of 4 June.

June, 5
function ImportantAlertsDemo() {
  return (
    <ImportantAlertsCard
      alerts={IMPORTANT_ALERTS}
      weekCount={IMPORTANT_ALERTS_WEEK_COUNT}
      weekRangeLabel={IMPORTANT_ALERTS_WEEK_RANGE}
    />
  );
}
function ImportantAlertsDemo() {
  return (
    <ImportantAlertsCard
      alerts={IMPORTANT_ALERTS}
      weekCount={IMPORTANT_ALERTS_WEEK_COUNT}
      weekRangeLabel={IMPORTANT_ALERTS_WEEK_RANGE}
    />
  );
}

Installation

npx shadcn@latest add https://boardcn.dev/r/important-alerts-card.json
npx shadcn@latest add https://boardcn.dev/r/important-alerts-card.json

npm packages

  • @internationalized/date
  • @remixicon/react
  • react-aria-components

BoardCN dependencies

The CLI installs these for you — you do not need to add them yourself.

Source

The 2 files the CLI copies into your project.

components/blocks/medical/important-alerts-card.tsx
"use client";

import { useState } from "react";
import type { ComponentType } from "react";
import { WeekRangePill } from "@/components/blocks/medical/week-range-pill";
import { cx } from "@/utils/cx";

type IconComponent = ComponentType<{ className?: string; "aria-hidden"?: boolean | "true" | "false" }>;

export type ImportantAlert = {
  icon: IconComponent;
  iconBg: string;
  title: string;
  description: string;
  date: string;
};

export type ImportantAlertsCardProps = {
  alerts: readonly ImportantAlert[];
  /** Total alerts logged this week — may exceed `alerts.length` when the feed is a recent subset. */
  weekCount: number;
  weekRangeLabel: string;
  className?: string;
};

export function ImportantAlertsCard({
  alerts,
  weekCount,
  weekRangeLabel,
  className,
}: ImportantAlertsCardProps) {
  const [scrolled, setScrolled] = useState(false);

  return (
    <section
      className={cx(
        // The 1px border matches the card's own bg — invisible as a line,
        // but content clips at the padding box (inside the border), so the
        // white alert rows stop 1px shy of the rounded edge instead of
        // touching it when scrolled to the bottom.
        "flex h-[330px] w-full min-w-0 flex-col gap-4 overflow-hidden rounded-[20px] border border-background-secondary-default bg-background-secondary-default px-2.5 pt-2.5",
        className,
      )}
    >
      <div className="flex w-full items-start justify-between gap-2 px-1.5 pt-1.5">
        <div className="flex min-w-0 flex-1 flex-col gap-0.5">
          <p className="text-body-medium text-text-secondary">Important alerts</p>
          <div className="flex items-baseline gap-1">
            <p className="text-title-1-medium whitespace-nowrap text-text-primary tabular-nums">
              {weekCount}
            </p>
            <span className="text-caption-1-medium whitespace-nowrap text-text-secondary">this week</span>
          </div>
        </div>
        <WeekRangePill label={weekRangeLabel} />
      </div>

      <div className="relative min-h-0 w-full flex-1">
        <div
          className="flex h-full w-full flex-col gap-2.5 overflow-y-auto overscroll-contain pb-2.5"
          onScroll={(e) => setScrolled(e.currentTarget.scrollTop > 0)}
        >
          {alerts.map((alert, index) => (
            <div
              key={`${alert.title}-${index}`}
              className="relative flex w-full shrink-0 flex-col gap-2 rounded-2lg bg-background-inner-default p-2.5"
            >
              <span className={cx("flex size-8 shrink-0 items-center justify-center rounded-full", alert.iconBg)}>
                <alert.icon className="size-[18px] text-white" aria-hidden />
              </span>
              <div className="flex flex-col gap-0.5">
                <p className="text-body-medium whitespace-nowrap text-text-primary">{alert.title}</p>
                <p className="text-body-2-regular text-text-secondary">{alert.description}</p>
              </div>
              <span className="absolute top-2.5 right-2.5 rounded-md bg-background-secondary-default px-1.5 py-px text-body-medium whitespace-nowrap text-text-secondary">
                {alert.date}
              </span>
            </div>
          ))}
        </div>
        {/* Top fade — card-colored gradient that eases in once the feed is
            scrolled, so rows dissolve under the header instead of cutting. */}
        <div
          aria-hidden
          className={cx(
            "pointer-events-none absolute inset-x-0 top-0 h-10 bg-linear-to-b from-background-secondary-default to-transparent",
            "transition-opacity duration-200 ease-out",
            scrolled ? "opacity-100" : "opacity-0",
          )}
        />
      </div>
    </section>
  );
}
"use client";

import { useState } from "react";
import type { ComponentType } from "react";
import { WeekRangePill } from "@/components/blocks/medical/week-range-pill";
import { cx } from "@/utils/cx";

type IconComponent = ComponentType<{ className?: string; "aria-hidden"?: boolean | "true" | "false" }>;

export type ImportantAlert = {
  icon: IconComponent;
  iconBg: string;
  title: string;
  description: string;
  date: string;
};

export type ImportantAlertsCardProps = {
  alerts: readonly ImportantAlert[];
  /** Total alerts logged this week — may exceed `alerts.length` when the feed is a recent subset. */
  weekCount: number;
  weekRangeLabel: string;
  className?: string;
};

export function ImportantAlertsCard({
  alerts,
  weekCount,
  weekRangeLabel,
  className,
}: ImportantAlertsCardProps) {
  const [scrolled, setScrolled] = useState(false);

  return (
    <section
      className={cx(
        // The 1px border matches the card's own bg — invisible as a line,
        // but content clips at the padding box (inside the border), so the
        // white alert rows stop 1px shy of the rounded edge instead of
        // touching it when scrolled to the bottom.
        "flex h-[330px] w-full min-w-0 flex-col gap-4 overflow-hidden rounded-[20px] border border-background-secondary-default bg-background-secondary-default px-2.5 pt-2.5",
        className,
      )}
    >
      <div className="flex w-full items-start justify-between gap-2 px-1.5 pt-1.5">
        <div className="flex min-w-0 flex-1 flex-col gap-0.5">
          <p className="text-body-medium text-text-secondary">Important alerts</p>
          <div className="flex items-baseline gap-1">
            <p className="text-title-1-medium whitespace-nowrap text-text-primary tabular-nums">
              {weekCount}
            </p>
            <span className="text-caption-1-medium whitespace-nowrap text-text-secondary">this week</span>
          </div>
        </div>
        <WeekRangePill label={weekRangeLabel} />
      </div>

      <div className="relative min-h-0 w-full flex-1">
        <div
          className="flex h-full w-full flex-col gap-2.5 overflow-y-auto overscroll-contain pb-2.5"
          onScroll={(e) => setScrolled(e.currentTarget.scrollTop > 0)}
        >
          {alerts.map((alert, index) => (
            <div
              key={`${alert.title}-${index}`}
              className="relative flex w-full shrink-0 flex-col gap-2 rounded-2lg bg-background-inner-default p-2.5"
            >
              <span className={cx("flex size-8 shrink-0 items-center justify-center rounded-full", alert.iconBg)}>
                <alert.icon className="size-[18px] text-white" aria-hidden />
              </span>
              <div className="flex flex-col gap-0.5">
                <p className="text-body-medium whitespace-nowrap text-text-primary">{alert.title}</p>
                <p className="text-body-2-regular text-text-secondary">{alert.description}</p>
              </div>
              <span className="absolute top-2.5 right-2.5 rounded-md bg-background-secondary-default px-1.5 py-px text-body-medium whitespace-nowrap text-text-secondary">
                {alert.date}
              </span>
            </div>
          ))}
        </div>
        {/* Top fade — card-colored gradient that eases in once the feed is
            scrolled, so rows dissolve under the header instead of cutting. */}
        <div
          aria-hidden
          className={cx(
            "pointer-events-none absolute inset-x-0 top-0 h-10 bg-linear-to-b from-background-secondary-default to-transparent",
            "transition-opacity duration-200 ease-out",
            scrolled ? "opacity-100" : "opacity-0",
          )}
        />
      </div>
    </section>
  );
}

Props

Generated from the component's TypeScript types. Standard DOM and React Aria props are omitted.

ImportantAlertsCard

PropTypeDefaultDescription
alertsrequiredreadonly ImportantAlert[]
weekCountrequirednumberTotal alerts logged this week — may exceed `alerts.length` when the feed is a recent subset.
weekRangeLabelrequiredstring
classNamestring

WeekRangePill

PropTypeDefaultDescription
labelrequiredstring
classNamestringOverride the default 151px width (e.g. the narrower month switcher).
onNext() => void
onPrev() => void