Skip to content
design
Instruments

Alert frame

A frame in the alert color around content, which blinks while an alarm is unacknowledged.

On this page
2.1
Depth
m
Echosounder
Depth 2.1 m, below the 3.0 m limit. Slow down and steer for deeper water.
00:42
58
From anchor
m
GPS, swing circle 45 m
92
Coolant
°C
Yanmar 4JH80
Engine coolant 92 °C and rising. Reduce to 1800 rpm.
03:15
0.3
CPA, MV Nordic Star
NM
AIS
18
Fuel tank
%
Tank sender, about 40 L left
6
Bilge pump cycles
/h
Bilge switch
Bilge pump ran 6 times in the last hour. The pump is dry now; check the stern gland.
21:00
11.9
House bank
V
Victron BMV
House bank at 11.9 V. Start the engine or plug in shore power before nightfall.
10:00
‒
Wind speed
kn
Masthead sensor silent for 4 min
Eight alerts on one watch screen: every alert type, every frame layout and every lifecycle mode.
Code
import {
  AlertFrame,
  AlertFrameTextSize,
  AlertType,
  ObcAlertFrameMode,
  ObcAlertFrameThickness,
  ObcAlertFrameType,
  Readout,
  ReadoutSize,
} from "@bosunkit/instruments";
import { Anchor, BatteryLow, Droplets, Thermometer, Waves } from "lucide-react";
import { useEffect, useState, type CSSProperties, type Dispatch, type ReactElement, type SetStateAction } from "react";

export const description: string =
  "Eight alerts on one watch screen, one per alert type, across every frame layout, both thicknesses, both text " +
  "sizes and all three lifecycle modes, with flap icons, long labels that wrap and then shorten at a word, and timers " +
  "that count up live.";

const GRID: CSSProperties = {
  display: "grid",
  gridTemplateColumns: "repeat(auto-fit, minmax(240px, 1fr))",
  columnGap: 48,
  rowGap: 8,
  alignItems: "start",
  width: "100%",
  maxWidth: 640,
  padding: "0 40px 0 8px",
  boxSizing: "border-box",
};

/**
 * Every frame's cell keeps room above it for a top flap on two lines and below it for a bottom flap on two lines of the
 * large text, so the frames of a row share their top edge whichever flap they carry, and every row is spaced alike.
 */
const CELL: CSSProperties = { paddingTop: 40, paddingBottom: 56 };

const CONTENT: CSSProperties = { padding: "10px 14px", display: "flex", flexDirection: "column", gap: 4 };

const ICON_SIZE: number = 20;

/** Minutes and seconds since the alert was raised, as the flap's timer shows them. */
function elapsed(seconds: number): string {
  const minutes: number = Math.floor(seconds / 60);
  const rest: number = seconds % 60;
  return `${String(minutes).padStart(2, "0")}:${String(rest).padStart(2, "0")}`;
}

export default function Showcase(): ReactElement {
  const [tick, setTick]: [number, Dispatch<SetStateAction<number>>] = useState<number>(0);

  useEffect((): (() => void) => {
    const interval: number = window.setInterval((): void => {
      setTick((previous: number): number => previous + 1);
    }, 1000);
    return (): void => {
      window.clearInterval(interval);
    };
  }, []);

  return (
    <div style={GRID}>
      <div style={CELL}>
        <AlertFrame
          type={ObcAlertFrameType.TopFlip}
          status={AlertType.Alarm}
          mode={ObcAlertFrameMode.unackedActive}
          thickness={ObcAlertFrameThickness.Large}
          showIcon
          icon={<Waves size={ICON_SIZE} />}
          label={<span>Depth 2.1 m, below the 3.0 m limit. Slow down and steer for deeper water.</span>}
          timer={elapsed(42 + tick)}
          wrapContent
          fullWidth
        >
          <div style={CONTENT}>
            <Readout
              label="Depth"
              value={2.1}
              fractionDigits={1}
              unit="m"
              src="Echosounder"
              size={ReadoutSize.medium}
            />
          </div>
        </AlertFrame>
      </div>

      <div style={CELL}>
        <AlertFrame
          type={ObcAlertFrameType.LargeSideFlip}
          status={AlertType.LevelCritical}
          mode={ObcAlertFrameMode.unackedActive}
          showIcon
          icon={<Anchor size={ICON_SIZE} />}
          wrapContent
          fullWidth
        >
          <div style={CONTENT}>
            <Readout label="From anchor" value={58} unit="m" src="GPS, swing circle 45 m" size={ReadoutSize.medium} />
          </div>
        </AlertFrame>
      </div>

      <div style={CELL}>
        <AlertFrame
          type={ObcAlertFrameType.BottomFlip}
          status={AlertType.Warning}
          mode={ObcAlertFrameMode.ackedActive}
          textSize={AlertFrameTextSize.Large}
          showIcon
          icon={<Thermometer size={ICON_SIZE} />}
          label={<span>Engine coolant 92 °C and rising. Reduce to 1800 rpm.</span>}
          timer={elapsed(195 + tick)}
          wrapContent
          fullWidth
        >
          <div style={CONTENT}>
            <Readout label="Coolant" value={92} unit="°C" src="Yanmar 4JH80" size={ReadoutSize.medium} />
          </div>
        </AlertFrame>
      </div>

      <div style={CELL}>
        <AlertFrame
          type={ObcAlertFrameType.SmallSideFlip}
          status={AlertType.LevelHigh}
          mode={ObcAlertFrameMode.ackedActive}
          thickness={ObcAlertFrameThickness.Large}
          wrapContent
          fullWidth
        >
          <div style={CONTENT}>
            <Readout label="CPA, MV Nordic Star" value={0.3} fractionDigits={1} unit="NM" src="AIS" />
          </div>
        </AlertFrame>
      </div>

      <div style={CELL}>
        <AlertFrame
          type={ObcAlertFrameType.SmallSideFlip}
          status={AlertType.Caution}
          mode={ObcAlertFrameMode.unackedActive}
          wrapContent
          fullWidth
        >
          <div style={CONTENT}>
            <Readout label="Fuel tank" value={18} unit="%" src="Tank sender, about 40 L left" />
          </div>
        </AlertFrame>
      </div>

      <div style={CELL}>
        <AlertFrame
          type={ObcAlertFrameType.TopFlip}
          status={AlertType.LevelMedium}
          mode={ObcAlertFrameMode.unackedRectified}
          showIcon
          icon={<Droplets size={ICON_SIZE} />}
          label={<span>Bilge pump ran 6 times in the last hour. The pump is dry now; check the stern gland.</span>}
          timer={elapsed(1260 + tick)}
          wrapContent
          fullWidth
        >
          <div style={CONTENT}>
            <Readout label="Bilge pump cycles" value={6} unit="/h" src="Bilge switch" />
          </div>
        </AlertFrame>
      </div>

      <div style={CELL}>
        <AlertFrame
          type={ObcAlertFrameType.BottomFlip}
          status={AlertType.LevelLow}
          mode={ObcAlertFrameMode.unackedActive}
          showIcon
          icon={<BatteryLow size={ICON_SIZE} />}
          label={<span>House bank at 11.9 V. Start the engine or plug in shore power before nightfall.</span>}
          timer={elapsed(600 + tick)}
          wrapContent
          fullWidth
        >
          <div style={CONTENT}>
            <Readout label="House bank" value={11.9} fractionDigits={1} unit="V" src="Victron BMV" />
          </div>
        </AlertFrame>
      </div>

      <div style={CELL}>
        <AlertFrame
          type={ObcAlertFrameType.Regular}
          status={AlertType.LevelDiagnostic}
          mode={ObcAlertFrameMode.ackedActive}
          sharpEdgeTopLeft
          sharpEdgeBottomRight
          wrapContent
          fullWidth
        >
          <div style={CONTENT}>
            <Readout label="Wind speed" value={null} unit="kn" src="Masthead sensor silent for 4 min" />
          </div>
        </AlertFrame>
      </div>
    </div>
  );
}

The alert frame puts an outline in the color of an alert around the content it belongs to. mode follows the alert lifecycle: steady while it is acknowledged and active, blinking in step with the other alerts while an alarm or an emergency is unacknowledged, and dashed once the cause has cleared. A warning and a caution stay steady. A flap with the category icon, a label and a timer can sit on one side, along the top or along the bottom. It ports obc-alert-frame; the overview lists the other instruments.

Examples

Unacknowledged alarm

42
Drop distance
m
GPS
An anchor drag alarm nobody has acknowledged, with the frame outline around its readout blinking in step with the other alerts on screen.
Code
import { AlertFrame, AlertType, ObcAlertFrameMode, Readout } from "@bosunkit/instruments";
import type { ReactElement } from "react";

export const description: string =
  "An anchor drag alarm nobody has acknowledged, with the frame outline around its readout blinking in step with " +
  "the other alerts on screen.";

export default function UnacknowledgedAlarm(): ReactElement {
  return (
    <AlertFrame
      status={AlertType.Alarm}
      mode={ObcAlertFrameMode.unackedActive}
      wrapContent
      showAlertCategoryIcon={false}
    >
      <div style={{ padding: "6px 12px" }}>
        <Readout label="Drop distance" value={42} unit="m" src="GPS" />
      </div>
    </AlertFrame>
  );
}

Frame the affected content

Wrap the reading or equipment region the alert actually concerns. A frame around an entire report or screen can imply that every value shares the alert. Keep the cause, priority, source and state readable inside or next to the framed region.

Supply mode from the alert lifecycle. A dashed rectified outline does not mean that an outstanding acknowledgment has completed. The optional timer needs a stated meaning, such as time since occurrence; it is not automatically a response deadline. Keep acknowledgment and audio control in their dedicated actions.

Props

PropTypeDefaultDescription
typeObcAlertFrameTypeObcAlertFrameType.SmallSideFlipFrame layout: regular is the outline alone; the other layouts add a flap with the category icon on a small or large side (small-side-flip, large-side-flip), along the bottom or along the top. Default small-side-flip.
thicknessObcAlertFrameThicknessObcAlertFrameThickness.SmallBorder thickness: small (default) or large for more emphasis.
statusAlertTypeAlertType.AlarmThe alert type, which selects the frame color and the category icon. Default alarm.
modeObcAlertFrameModeObcAlertFrameMode.ackedActiveWhere the alert is in its lifecycle: acked-active (steady, default), unacked-active (blinks with the alert blink cadence) or unacked-rectified (dashed outline).
textSizeAlertFrameTextSizeAlertFrameTextSize.RegularSize of the flap's label and timer text: regular (default) or large.
showIconbooleanfalseShows icon in the flap of the large-side, bottom and top layouts. Default false.
showAlertCategoryIconbooleantrueShows the category icon for status in the flap. Default true.
wrapContentbooleanfalseWhen true, the frame wraps and sizes itself to its slotted content instead of overlaying a fixed region. Default: false (absolute, inset 0).
fullWidthbooleanfalseWhen true, the frame stretches to the full width of its container.
sharpEdgeTopLeftbooleanfalseIf true, the top-left corner will be sharp (not rounded).
sharpEdgeTopRightbooleanfalseIf true, the top-right corner will be sharp (not rounded).
sharpEdgeBottomLeftbooleanfalseIf true, the bottom-left corner will be sharp (not rounded).
sharpEdgeBottomRightbooleanfalseIf true, the bottom-right corner will be sharp (not rounded).
iconReactNode–Custom icon displayed in the flap (large-side-flip, bottom-flip, top-flip).
labelReactNode–Label text shown in the bottom/top flap.
timerReactNode–Timer or time label in the bottom/top flap.
childrenReactNode–Main content inside the alert frame.
classNamestring–Class names added to the frame's root element.