Insignias de estado
Siete tonos con punto, pulso opcional y variantes suave y sólida.
En líneaNuevoPendienteErrorBetaDestacadoBorrador
Código completo
Es exactamente el archivo que se descarga. Cópialo a tu proyecto y úsalo.
"use client";
import type { CSSProperties, ReactNode } from "react";
/** Insignias de estado: siete tonos con punto, pulso opcional y variante sólida o suave. Elemento inline de lectura, no interactivo. React + TypeScript, sin dependencias. */
export type BaseInsigniaTono = "neutro" | "azul" | "verde" | "ambar" | "rojo" | "violeta" | "rosa";
export type BaseInsigniaProps = {
children?: ReactNode;
tone?: BaseInsigniaTono;
solid?: boolean;
dot?: boolean;
pulse?: boolean;
className?: string;
style?: CSSProperties;
};
const CSS = `
.gcbin-root { box-sizing: border-box; display: inline-flex; align-items: center; gap: 6px; height: 26px; padding: 0 10px; border-radius: 999px; background: var(--s); color: var(--i); font: 600 12px/1 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; letter-spacing: .01em; white-space: nowrap; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--i) 18%, transparent); }
.gcbin-root *, .gcbin-root *::before, .gcbin-root *::after { box-sizing: border-box; }
.gcbin-root[data-solid="true"] { background: var(--i); color: var(--on); box-shadow: none; }
.gcbin-dot { position: relative; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.gcbin-root[data-pulse="true"] .gcbin-dot::after { content: ""; position: absolute; inset: 0; border-radius: 50%; background: currentColor; animation: gcbin-ping 1.6s cubic-bezier(0, 0, .2, 1) infinite; }
@keyframes gcbin-ping { 75%, 100% { transform: scale(2.8); opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .gcbin-root[data-pulse="true"] .gcbin-dot::after { animation: none; } }`;
const TONES: Record<BaseInsigniaTono, { s: string; i: string; on: string }> = {
neutro: { s: "#f0f0f2", i: "#52525b", on: "#fff" },
azul: { s: "#e6efff", i: "#1d4fd8", on: "#fff" },
verde: { s: "#dcf5e6", i: "#117a42", on: "#fff" },
ambar: { s: "#fff0cf", i: "#9a5a00", on: "#fff" },
rojo: { s: "#ffe2e2", i: "#c42b2b", on: "#fff" },
violeta: { s: "#ece4ff", i: "#6b3fd4", on: "#fff" },
rosa: { s: "#ffe3f0", i: "#c1246b", on: "#fff" },
};
export function BaseInsignia({ children = "Activo", tone = "verde", solid = false, dot = true, pulse = false, className = "", style }: BaseInsigniaProps) {
const t = TONES[tone];
return (
<span className={`gcbin-root ${className}`} style={{ "--s": t.s, "--i": t.i, "--on": t.on, ...style } as CSSProperties} data-solid={solid} data-pulse={pulse}>
<style>{CSS}</style>
{dot ? <span className="gcbin-dot" aria-hidden="true" /> : null}
{children}
</span>
);
}
Úsalo en tu proyecto
Instálalo con la CLI de shadcn.
npx shadcn@latest add https://www.gassierconsulting.com/r/base-insigniaEjemplo de uso de Insignias de estado
Después de copiar base-insignia.tsx a tu proyecto, impórtalo y colócalo donde lo necesites.
import { BaseInsignia } from "./base-insignia";
export default function Ejemplo() {
return <BaseInsignia tone="neutro" solid>Contenido</BaseInsignia>;
}Propiedades de BaseInsignia
BaseInsignia acepta 7 propiedades: todas son opcionales y tienen un valor por defecto, así que funciona sin configurar nada.
| Propiedad | Tipo | Descripción |
|---|---|---|
children? | ReactNode | Opcional. |
tone? | BaseInsigniaTono | Opcional. |
solid? | boolean | Opcional. |
dot? | boolean | Opcional. |
pulse? | boolean | Opcional. |
className? | string | Opcional. |
style? | CSSProperties | Opcional. |
Variantes disponibles
tone:neutro,azul,verde,ambar,rojo,violeta,rosa
Accesibilidad y rendimiento
El archivo tiene 47 líneas (3 KB) y define 1 animación con @keyframes. Esto es lo que hace Insignias de estado por quien lo usa:
- Detecta prefers-reduced-motion y baja o quita el movimiento cuando la persona lo pide.
- Expone su estado a tecnologías de apoyo con atributos aria-*.
- Calcula tonos con color-mix(), así que un solo color base alcanza.
- Es un componente de cliente de Next.js: incluye la directiva "use client".