errors
Connectivity Error
A dismissible card surfacing a network connection failure with a description and retry/details actions.
Connection lostCheck your internet connection, VPN or proxy and try again.
"use client";
import * as React from "react";
import { AnimatePresence, motion } from "motion/react";
import { WifiOff, X } from "lucide-react";
import { cn } from "@/lib/utils";
export interface ConnectivityErrorProps {
title?: string;
description?: string;
detailsLabel?: string;
retryLabel?: string;
onViewDetails?: () => void;
onRetry?: () => void;
onClose?: () => void;
className?: string;
}
export function ConnectivityError({
title = "Connection lost",
description = "Check your internet connection, VPN or proxy and try again.",
detailsLabel = "View details",
retryLabel = "Try again",
onViewDetails,
onRetry,
onClose,
className,
}: ConnectivityErrorProps) {
const [visible, setVisible] = React.useState(true);
function handleClose() {
setVisible(false);
onClose?.();
}
return (
<AnimatePresence>
{visible && (
<motion.div
initial={{ opacity: 0, y: -6 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -6 }}
transition={{ duration: 0.2, ease: "easeOut" }}
role="alert"
aria-live="assertive"
className={cn(
"flex w-full flex-wrap items-center gap-x-4 gap-y-2 overflow-hidden rounded-lg border border-border bg-background px-4 py-2.5 text-foreground",
className
)}
>
{/* Message */}
<div className="flex min-w-0 flex-1 items-center gap-2">
<WifiOff className="size-3.5 shrink-0 text-foreground" aria-hidden />
<span className="truncate text-xs font-semibold text-foreground">{title}</span>
<span className="truncate text-xs text-muted-foreground">{description}</span>
</div>
{/* Actions */}
<div className="flex shrink-0 items-center gap-2">
{onViewDetails && (
<button
type="button"
onClick={onViewDetails}
className="rounded-md border border-border bg-background px-2.5 py-1 text-[11px] font-medium text-foreground transition-colors hover:bg-accent"
>
{detailsLabel}
</button>
)}
{onRetry && (
<button
type="button"
onClick={onRetry}
className="rounded-md border border-border bg-background px-2.5 py-1 text-[11px] font-medium text-foreground transition-colors hover:bg-accent"
>
{retryLabel}
</button>
)}
{onClose && (
<button
type="button"
onClick={handleClose}
aria-label="Close"
className="rounded-md p-1 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
>
<X className="size-3.5" aria-hidden />
</button>
)}
</div>
</motion.div>
)}
</AnimatePresence>
);
}
A UX spec for this pattern — written for agents implementing or reusing it, not the code.
# Connectivity Error
## Summary
A dismissible banner that surfaces a network connectivity failure with a short description and two recovery actions — "View details" and "Try again". Spans the full width of its container as a single row, appears inline in the layout (not as a toast or modal), and exits with a fade when dismissed or retried.
## When to use
- When the AI product loses its connection to the backend mid-session or on page load, and the user needs to know before submitting another prompt.
- When the error may be caused by VPN, proxy, or local network configuration — not just a transient blip — and the user might need to investigate before retrying.
- When both a passive "View details" path and an immediate "Try again" action are meaningful.
## When not to use
- Brief transient failures that auto-recover within seconds — surface a spinner instead and only show this if recovery fails after a threshold.
- Hard authentication or quota failures — use a more specific error pattern (Rate Limit, account error) rather than a generic connectivity card.
- When there is no meaningful "View details" target — omit that action rather than linking to a dead end.
## Anatomy
- **Container**: Single row, full width, small border radius, no shadow — reads as a slim banner rather than a card.
- **Icon**: `WifiOff` (or similar offline icon) at 14px, next to the title. Decorative — `aria-hidden`.
- **Title**: Short, factual label ("Connection lost"). Small, semibold, text-foreground.
- **Description**: One short sentence, inline after the title on the same row. Small, muted text, truncates if the row runs out of space.
- **View details button**: Small secondary bordered button in the trailing action group. Omitted if `onViewDetails` is not provided.
- **Try again button**: Small secondary bordered button in the trailing action group. Omitted if `onRetry` is not provided.
- **Close button**: Icon-only × button at the far right of the action group, hidden if `onClose` is not provided.
## Behavior
- Mounts with a short upward fade (opacity 0→1, y −6→0, 200 ms ease-out).
- Exits with the reverse animation when the close button is clicked.
- Pressing "Try again" should trigger the parent's retry logic; the demo resets the key so the card reappears after it has been dismissed.
- Internal `visible` state gates the `AnimatePresence` exit — callers control re-showing by re-mounting (key reset) or by not providing `onClose`.
- The card does not auto-dismiss or countdown — the user must act.
## Content guidelines
- Title: plain noun phrase describing the state, not an error code. "Connection lost" not "ERR_NETWORK_CHANGED" or "Request failed (503)".
- Description: one sentence, action-oriented. Lead with what to check, not what went wrong. "Check your internet connection, VPN or proxy and try again."
- "Try again" is always the primary recovery; "View details" is optional and should link to a diagnostic panel or log, not an alert.
- Keep both button labels under 20 characters — they share a single row.
## Accessibility
- The container has `role="alert"` and `aria-live="assertive"` so screen readers announce it immediately on mount.
- The close button has `aria-label="Close"`.
- The wifi-off icon is `aria-hidden` — the title carries the semantic meaning.
## Related patterns
- Rate Limit — for quota-exceeded states, not connectivity failures.
- Partial Response — for responses cut short by the server, not by a network drop.
- Console Error Card — for surfacing developer-facing browser errors, not end-user connectivity issues.