SignalStrength
Data DisplayCompact stepped levels with a readable value and accessible meter.
Responsive viewport — resize the window to explore. Direction and density apply only to this preview. Theme applies to the docs too.
"use client";
import { SignalStrength } from "@/components/ui/signal-strength";
export default function SignalStrengthExample() {
return (
<div className="w-full max-w-md space-y-5">
<div className="divide-y rounded-xl border bg-card">
{[
{ label: "Studio Wi-Fi", value: 5 },
{ label: "Mobile hotspot", value: 3 },
{ label: "Meeting room", value: 1 },
{ label: "Guest network", value: 0 },
].map((item) => (
<div key={item.label} className="p-4">
<SignalStrength {...item} />
</div>
))}
</div>
<div className="rounded-lg bg-muted/30 p-4">
<SignalStrength label="Priority" value={2} max={3} />
</div>
<p className="text-xs text-muted-foreground">
Every level has a readable numeric label. These are sample values, not
device measurements.
</p>
</div>
);
}Usage
Use for a supplied strength, priority, or capacity level. The component does not read device signal or network state. Value rounds to the nearest integer and clamps between zero and max. The bar count clamps between one and ten, with non-finite inputs handled safely. Use the visible label to explain what is being measured and keep the same scale across comparable rows.
import { SignalStrength } from "@/components/ui/signal-strength";Shipped as editable source in src/components/ui/signal-strength.tsx in both apps. Copy the implementation below when moving it to another project. Copy the complete example from Source code for all imports and state. Add "use client" when using hooks or event handlers. See installation and theming for project setup.
Component source
The complete implementation is included in both apps. It uses the React, Radix, Lucide, and shared-core dependencies already installed in Apex. Keep your project’s semantic theme tokens when reusing it.
src/components/ui/signal-strength.tsx
import { cn } from "@dashboardpack/core/lib/utils";
export interface SignalStrengthProps {
value: number;
max?: number;
label: string;
className?: string;
}
export function normalizeSignal(value: number, max = 5) {
const count = Math.max(
1,
Math.min(10, Number.isFinite(max) ? Math.floor(max) : 5),
);
return {
count,
level: Math.max(
0,
Math.min(count, Number.isFinite(value) ? Math.round(value) : 0),
),
};
}
export function SignalStrength({
value,
max = 5,
label,
className,
}: SignalStrengthProps) {
const { count, level } = normalizeSignal(value, max);
return (
<span className={cn("inline-flex items-center gap-3", className)}>
<span
role="meter"
aria-label={label}
aria-valuemin={0}
aria-valuemax={count}
aria-valuenow={level}
aria-valuetext={`${level} of ${count}`}
className="inline-flex h-6 items-end gap-1"
>
{Array.from({ length: count }, (_, index) => (
<span
key={index}
aria-hidden
style={{ height: `${((index + 1) / count) * 100}%` }}
className={cn(
"w-1.5 rounded-sm",
index < level ? "bg-primary" : "bg-muted-foreground/20",
)}
/>
))}
</span>
<span className="text-xs text-muted-foreground">
{label}: {level}/{count}
</span>
</span>
);
}API reference
Common props and defaults. Slot-specific props are named below; native elements also accept their standard HTML attributes. The component TypeScript definitions are the complete type reference.
| Prop | Type / Default | Description |
|---|---|---|
| label | stringDefault: required | Visible metric name and accessible meter label. |
| value | numberDefault: required | Rounded level, bounded to the meter range. |
| max | numberDefault: 5 | Number of bars, clamped to 1–10. |
| className | stringDefault: — | Extend the container with semantic theme utilities. |