Alerta contextual descartable
Aviso de información, éxito, advertencia o error con descarte, anuncio accesible y opción de volver a mostrarlo.
Puedes seguir editando. Publica cuando todo esté listo.
Código completo
Es exactamente el archivo que se descarga. Cópialo a tu proyecto y úsalo.
"use client";
import { useState, type CSSProperties } from "react";
/** Alerta contextual descartable. React + TypeScript, sin dependencias. Personaliza con variables --gcxl-*. */
export type BaseAlertaProps = {
title?: string;
message?: string;
tone?: "info" | "success" | "warning" | "error";
dismissible?: boolean;
onDismiss?: () => void;
className?: string;
style?: CSSProperties;
};
const CSS = `
.gcxl-root { box-sizing: border-box; min-width: 0; max-width: 100%; color: var(--gcxl-fg, #202421); font: 400 15px/1.5 ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
.gcxl-root *, .gcxl-root *::before, .gcxl-root *::after { box-sizing: border-box; }
.gcxl-root button, .gcxl-root input, .gcxl-root textarea, .gcxl-root select { font: inherit; }
.gcxl-root button, .gcxl-root a { -webkit-tap-highlight-color: transparent; }
.gcxl-root :focus-visible { outline: 2px solid var(--gcxl-accent, #25724b); outline-offset: 4px; }
.gcxl-root ::selection { background: var(--gcxl-accent, #25724b); color: #fff; }
.gcxl-root button:disabled { opacity: .5; cursor: not-allowed; }
.gcxl-root svg { flex-shrink: 0; }
@media (prefers-reduced-motion: reduce) { .gcxl-root *, .gcxl-root *::before, .gcxl-root *::after { animation: none !important; transition-duration: .01ms !important; scroll-behavior: auto !important; } }
.gcxl-root { width:500px; display:flex; gap:14px; align-items:flex-start; padding:20px; border-radius:12px; color:var(--gcxl-fg,#294c68); background:var(--gcxl-bg,#e5eef4); }
.gcxl-root[data-tone="success"] { color:#285938; background:#e6f0e4; }
.gcxl-root[data-tone="warning"] { color:#72521d; background:#f8efda; }
.gcxl-root[data-tone="error"] { color:#863c35; background:#fae9e5; }
.gcxl-root>svg { margin-top:2px; }
.gcxl-root>div { flex:1; min-width:0; }
.gcxl-root strong { font-weight:600; font-size:14px; }
.gcxl-root p { margin:5px 0 0; font-size:13px; overflow-wrap:anywhere; }
.gcxl-root button { display:grid; place-items:center; flex-shrink:0; min-height:44px; min-width:44px; padding:5px 10px; border:0; border-radius:6px; background:transparent; color:inherit; cursor:pointer; margin:-10px -10px -10px 0; }
.gcxl-root button:hover { background:#00000009; }
.gcxl-undo { align-items:center; font-size:13px; }`;
export function BaseAlerta({
title = "Cambios sin publicar",
message = "Puedes seguir editando. Publica cuando todo esté listo.",
tone = "info",
dismissible = true,
onDismiss,
className = "",
style,
}: BaseAlertaProps) {
const [dismissed, setDismissed] = useState(false);
if (dismissed)
return (
<div className={`gcxl-root gcxl-undo ${className}`} style={style}>
<style>{CSS}</style>
<span role="status">Aviso cerrado.</span>
<button type="button" onClick={() => setDismissed(false)}>
Mostrar de nuevo
</button>
</div>
);
return (
<div
className={`gcxl-root ${className}`}
style={style}
data-tone={tone}
role={tone === "error" ? "alert" : "status"}
>
<style>{CSS}</style>
<svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
aria-hidden="true"
>
<circle cx="12" cy="12" r="9" />
<path d={tone === "success" ? "m7 12 3 3 6-6" : "M12 10v6m0-9v1"} />
</svg>
<div>
<strong>{title}</strong>
<p>{message}</p>
</div>
{dismissible && (
<button
type="button"
aria-label="Cerrar aviso"
onClick={() => {
setDismissed(true);
onDismiss?.();
}}
>
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="1.6"
aria-hidden="true"
>
<path d="m6 6 12 12M18 6 6 18" />
</svg>
</button>
)}
</div>
);
}
Úsalo en tu proyecto
Instálalo con la CLI de shadcn.
npx shadcn@latest add https://www.gassierconsulting.com/r/base-alertaEjemplo de uso de Alerta contextual descartable
Después de copiar base-alerta.tsx a tu proyecto, impórtalo y colócalo donde lo necesites.
import { BaseAlerta } from "./base-alerta";
export default function Ejemplo() {
return <BaseAlerta tone="info" title="Ejemplo" dismissible />;
}Propiedades de BaseAlerta
BaseAlerta acepta 7 propiedades: todas son opcionales y tienen un valor por defecto, así que funciona sin configurar nada.
| Propiedad | Tipo | Descripción |
|---|---|---|
title? | string | Opcional. |
message? | string | Opcional. |
tone? | "info" | "success" | "warning" | "error" | Opcional. |
dismissible? | boolean | Opcional. |
onDismiss? | () => void | Opcional. |
className? | string | Opcional. |
style? | CSSProperties | Opcional. |
Variantes disponibles
tone:info,success,warning,error
Personaliza Alerta contextual descartable con variables CSS
Define estas variables en el contenedor o en tu hoja de estilos para cambiar los colores sin tocar el componente. Si no las defines, se usa el valor por defecto.
| Variable | Valor por defecto |
|---|---|
--gcxl-fg | #202421 |
--gcxl-accent | #25724b |
--gcxl-bg | #e5eef4 |
Accesibilidad y rendimiento
El archivo tiene 107 líneas (4 KB) y no necesita @keyframes. Esto es lo que hace Alerta contextual descartable por quien lo usa:
- Detecta prefers-reduced-motion y baja o quita el movimiento cuando la persona lo pide.
- Muestra un anillo de foco visible al navegar con teclado (:focus-visible).
- Declara roles ARIA para lectores de pantalla: status.
- Es un componente de cliente de Next.js: incluye la directiva "use client".