Defense & Aerospace

Alert Condition

A DEFCON-style readiness indicator with five stacked level bars, a restrained breathing glow that only appears at escalated conditions, and reduced-motion-safe color-driven state.

Install

npx shadcn@latest add @paragon/alert-condition

alert-condition.tsx

"use client";

import * as React from "react";
import { useReducedMotion } from "motion/react";
import { cn } from "@/lib/utils";

export type ConditionLevel = 5 | 4 | 3 | 2 | 1;

interface LevelSpec {
  name: string;
  color: string;
  posture: string;
}

const LEVELS: Record<ConditionLevel, LevelSpec> = {
  5: { name: "COND 5", color: "var(--color-success)", posture: "Normal readiness" },
  4: { name: "COND 4", color: "oklch(0.72 0.15 130)", posture: "Increased watch" },
  3: { name: "COND 3", color: "var(--color-warning)", posture: "Elevated — forces ready" },
  2: { name: "COND 2", color: "oklch(0.66 0.2 45)", posture: "Further escalation" },
  1: { name: "COND 1", color: "var(--color-destructive)", posture: "Maximum readiness" },
};

export interface AlertConditionProps extends React.ComponentProps<"div"> {
  /** Current condition, 5 (normal) → 1 (maximum). */
  level?: ConditionLevel;
  /** Short mission / sector label. */
  sector?: string;
  static?: boolean;
}

/**
 * A DEFCON-style readiness indicator. Five stacked bars fill up to the active
 * level; only the active bar carries a restrained breathing glow, and only
 * when the condition is 2 or 1 (escalated). The glow is a looping opacity
 * pulse — no movement — and is removed entirely under reduced motion, leaving
 * the color to carry the state. Level changes color-transition, never restart.
 */
export function AlertCondition({
  level = 4,
  sector = "SECTOR-7 / DEMO",
  static: isStatic = false,
  className,
  ...props
}: AlertConditionProps) {
  const reduced = useReducedMotion();
  const spec = LEVELS[level];
  const escalated = level <= 2;
  const pulse = escalated && !isStatic && !reduced;

  return (
    <div
      className={cn(
        "w-full max-w-xs rounded-xl bg-card p-4 text-card-foreground shadow-border",
        className,
      )}
      {...props}
    >
      <style href="paragon-alert-condition" precedence="paragon">{`
        @keyframes pg-cond-pulse { 0%, 100% { opacity: 0.35; } 50% { opacity: 0.9; } }
        .pg-cond-glow { animation: pg-cond-pulse 1.8s var(--ease-in-out) infinite; }
        @media (prefers-reduced-motion: reduce) { .pg-cond-glow { animation: none; opacity: 0.6; } }
      `}</style>

      <div className="flex items-center justify-between">
        <span className="text-[10px] uppercase tracking-wider text-muted-foreground">
          Alert Condition
        </span>
        <span className="font-mono text-[10px] text-muted-foreground">
          {sector}
        </span>
      </div>

      <div className="mt-3 flex items-stretch gap-3">
        <div className="flex flex-1 flex-col-reverse gap-1.5">
          {([5, 4, 3, 2, 1] as ConditionLevel[]).map((l) => {
            const active = l >= level; // bars from top(1) down to current fill
            const isCurrent = l === level;
            const c = LEVELS[l].color;
            return (
              <div key={l} className="relative flex items-center gap-2">
                <span
                  className={cn(
                    "w-8 shrink-0 text-right font-mono text-[10px] tabular-nums transition-colors duration-(--duration-base)",
                    isCurrent ? "text-foreground" : "text-muted-foreground/60",
                  )}
                >
                  {l}
                </span>
                <div className="relative h-4 flex-1 overflow-hidden rounded-sm bg-secondary">
                  <div
                    className="absolute inset-0 transition-[opacity,background-color] duration-(--duration-base) ease-(--ease-out)"
                    style={{
                      backgroundColor: c,
                      opacity: active ? (isCurrent ? 1 : 0.55) : 0.08,
                    }}
                  />
                  {isCurrent && pulse && (
                    <div
                      aria-hidden
                      className="pg-cond-glow pointer-events-none absolute inset-0"
                      style={{ backgroundColor: c }}
                    />
                  )}
                </div>
              </div>
            );
          })}
        </div>
      </div>

      <div className="mt-3 flex items-center gap-2 border-t border-border pt-3">
        <span
          className="size-2.5 shrink-0 rounded-full transition-colors duration-(--duration-base)"
          style={{ backgroundColor: spec.color }}
        />
        <div className="min-w-0">
          <p
            className="font-mono text-sm font-semibold transition-colors duration-(--duration-base)"
            style={{ color: spec.color }}
          >
            {spec.name}
          </p>
          <p className="truncate text-xs text-muted-foreground">{spec.posture}</p>
        </div>
      </div>
    </div>
  );
}