Speed arrows
The speed and direction of travel as up to three chevrons, with an optional readout.
On this page
Code
import { ActiveColor, Direction, SpeedArrows } from "@bosunkit/instruments";
import { Sailboat } from "lucide-react";
import { useEffect, useState, type CSSProperties, type Dispatch, type ReactElement, type SetStateAction } from "react";
export const description: string =
"Berthing alongside on a live feed: the speed ahead falls as the vessel stops, the bow thruster pushes her to " +
"starboard onto the pontoon, and a burst astern stops her, with all four directions colored by side and tinted " +
"when idle, beside the speed through water and the drift in the enhanced and regular palettes.";
const PANEL: CSSProperties = {
display: "flex",
flexWrap: "wrap",
alignItems: "center",
justifyContent: "center",
gap: 40,
width: "100%",
maxWidth: 640,
};
const CROSS: CSSProperties = {
display: "grid",
gridTemplateColumns: "repeat(3, auto)",
gridTemplateRows: "repeat(3, auto)",
alignItems: "center",
justifyItems: "center",
gap: 12,
};
const COLUMN: CSSProperties = { display: "flex", flexDirection: "column", gap: 20 };
const LABEL: CSSProperties = { fontSize: 13, color: "var(--text-dim)" };
const VESSEL: CSSProperties = { color: "var(--text-dim)" };
/** How many chevrons a speed lights: one from 0.1 knots, two from 0.5, three from 1.0. */
function chevrons(knots: number): number {
if (knots >= 1) {
return 3;
}
if (knots >= 0.5) {
return 2;
}
return knots >= 0.1 ? 1 : 0;
}
/** Longitudinal and lateral speed in knots, positive ahead and to starboard, over a forty-second approach. */
function motionAt(time: number): { ahead: number; starboard: number } {
const phase: number = time % 40;
const ahead: number = phase < 25 ? 1.4 * (1 - phase / 25) : -0.5 * Math.sin((Math.PI * (phase - 25)) / 15);
const starboard: number = phase > 12 && phase < 34 ? 0.7 * Math.sin((Math.PI * (phase - 12)) / 22) : 0;
return { ahead, starboard };
}
export default function Showcase(): ReactElement {
const [second, setSecond]: [number, Dispatch<SetStateAction<number>>] = useState<number>(0);
useEffect((): (() => void) => {
const interval: number = window.setInterval((): void => {
setSecond((previous: number): number => previous + 1);
}, 1000);
return (): void => {
window.clearInterval(interval);
};
}, []);
const { ahead, starboard }: { ahead: number; starboard: number } = motionAt(second);
const forward: number = Math.max(0, ahead);
const astern: number = Math.max(0, -ahead);
const toStarboard: number = Math.max(0, starboard);
const toPort: number = Math.max(0, -starboard);
return (
<div style={PANEL}>
<div style={CROSS}>
<span />
<SpeedArrows
direction={Direction.forward}
speedKnots={forward}
nActiveArrows={chevrons(forward)}
activeColor={ActiveColor.Direction}
tintedArrows
readout
fractionDigits={1}
maxDigits={1}
/>
<span />
<SpeedArrows
direction={Direction.left}
speedKnots={toPort}
nActiveArrows={chevrons(toPort)}
activeColor={ActiveColor.Direction}
tintedArrows
readout
fractionDigits={1}
maxDigits={1}
/>
<Sailboat size={40} style={VESSEL} aria-hidden />
<SpeedArrows
direction={Direction.right}
speedKnots={toStarboard}
nActiveArrows={chevrons(toStarboard)}
activeColor={ActiveColor.Direction}
tintedArrows
readout
fractionDigits={1}
maxDigits={1}
/>
<span />
<SpeedArrows
direction={Direction.backward}
speedKnots={astern}
nActiveArrows={chevrons(astern)}
activeColor={ActiveColor.Direction}
tintedArrows
readout
fractionDigits={1}
maxDigits={1}
/>
<span />
</div>
<div style={COLUMN}>
<div>
<div style={LABEL}>Speed through water, helm</div>
<SpeedArrows
direction={ahead >= 0 ? Direction.forward : Direction.backward}
speedKnots={Math.abs(ahead)}
nActiveArrows={chevrons(Math.abs(ahead))}
activeColor={ActiveColor.Enhanced}
tintedArrows
readout
fractionDigits={2}
maxDigits={2}
/>
</div>
<div>
<div style={LABEL}>Drift, cockpit repeater</div>
<SpeedArrows
direction={starboard >= 0 ? Direction.right : Direction.left}
speedKnots={Math.abs(starboard)}
nActiveArrows={chevrons(Math.abs(starboard))}
activeColor={ActiveColor.Regular}
/>
</div>
</div>
</div>
);
}
Speed arrows show the speed as up to three stacked chevrons that point along the direction of travel, with an optional
readout above them that prints the speed in knots. activeColor can color the chevrons by the direction of travel,
green for forward and right and red for backward and left. It ports obc-speed-arrows; the
overview lists the other instruments.
Examples
Speed arrows
Code
import { SpeedArrows } from "@bosunkit/instruments";
import type { ReactElement } from "react";
export const description: string = "A speed of 6.4 knots as a readout above the three chevrons.";
export default function SpeedArrowsExample(): ReactElement {
return (
<div style={{ width: 200, height: 200 }}>
<SpeedArrows speedKnots={6.4} readout />
</div>
);
}
Direction, magnitude and thresholds
speedKnots, direction and nActiveArrows are separate inputs. The caller chooses the chevron thresholds;
three arrows do not define a universal speed. Keep those thresholds stable and explain them when the count
informs a decision.
Label whether the motion is longitudinal, transverse, over ground or through water. Direction colors describe travel direction, not alert priority. Keep the numeric readout or an adjacent labeled reading when an exact speed matters, and show an unavailable state outside the arrows when the source is lost.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| speedKnots | number | undefined | 0 | Speed shown in the readout (knots). |
| readout | boolean | undefined | false | Show the numeric speed readout above the arrows. |
| maxDigits | number | undefined | 2 | Maximum digits reserved by the readout. |
| fractionDigits | number | undefined | 0 | Fraction digits shown in the readout. |
| nActiveArrows | number | undefined | 0 | How many of the three chevrons are active (0–3). |
| direction | Direction | undefined | Direction.forward | Travel direction the chevrons point along. |
| activeColor | ActiveColor | undefined | ActiveColor.Regular | Active chevron palette: Regular (neutral), Enhanced (blue), or
Direction — starboard green for forward/right, port red for
backward/left (the IMO port/starboard convention). |
| tintedArrows | boolean | undefined | false | Render inactive chevrons tinted instead of omitting them. |
| className | string | undefined | – | Class names added to the instrument's root element. |
Related
- Speed gauge: the speed on a dial.
- Data integrity: the states a value can be in, and what the crew does.