Toolbar.
Patrón canónico de toolbar para todos los sitios y servicios.
Verlo por niveles
Primero el componente, después la variación de marca y finalmente su uso dentro de una página.
Ver y copiar el código de este render
<div class="ui-sample ui-actions" role="toolbar" aria-label="Edición del documento"><button class="btn btn--icon" type="button" aria-label="Negrita" title="Negrita" aria-pressed="false"><b aria-hidden="true">N</b></button><button class="btn btn--icon" type="button" aria-label="Cursiva" title="Cursiva" aria-pressed="false"><i aria-hidden="true">K</i></button><span role="separator" aria-orientation="vertical"></span><button class="btn btn--secondary" type="button">Insertar enlace</button></div>Variaciones por marca y sitio
Cada cambio visible declara su autoridad; lo que permanece canónico no se vuelve a inventar en el producto.
Compañía de Innovación de Santiago
Autoridad: CONSTITUTION.md + LOGOS-CANONICOS.mdCambia en esta marca
- Navegación y contenido propios del servicio
- Acento reservado para acciones y datos
Permanece canónico
- Mark CIS y namebrand de tres líneas
- IBM Plex Sans, foco, espaciado y anatomía
Círculo de Santiago
Autoridad: core-style/DECISIONES.md M10 y LOGOS-CANONICOS.mdCambia en esta marca
- Mark circular y namebrand Círculo de Santiago en caja normal
- Contenido societario y navegación propia
Permanece canónico
- Misma estructura, orden de cuenta y footer
- Tipografía, foco y comportamiento canónicos
Comparación histórica recuperable
Compara la propuesta con cortes reales del repositorio. La ausencia de una muestra se declara y nunca se reconstruye de memoria.
- Estado actual
- Toolbar no tiene una implementación vigente demostrada; sus ejemplos son material candidato.
- Cambio propuesto
- Publicar en r19 el contrato de Toolbar para Patrón canónico de toolbar para todos los sitios y servicios.
- Qué ocurre si respondes No
- Toolbar queda fuera de r19 y sus ejemplos siguen sólo como evidencia pendiente de reformulación.
Preparando comparación histórica…
Antes / r19
La comparación convierte una regla abstracta en una diferencia visible y verificable.
- Usa HTML semántico, foco visible y estados explícitos.
- Conserva contenido útil a 320 px y sin JavaScript.
- No agregues sombras, radios ni franjas laterales decorativas.
- No uses color, placeholder o icono como única explicación.
Responsive
La misma anatomía debe conservar lectura, foco y acciones sin cortar contenido ni crear scroll horizontal en la página.
Cobertura
| Dimensión | Contrato |
|---|---|
| Propietario canónico | Sistema de diseño CIS · core/core-style |
| Consumidores | Todos los sitios y servicios |
| Dependencias | HTML nativo |
| Estado | candidato |
Evidencia de adopción
Patrón propuesto. 0 implementaciones observadas en 0 repositorios.
La observación demuestra uso existente; la aceptación canónica sigue dependiendo de esta ficha y de su propietario.
| Dato | Resultado |
|---|---|
| Clasificación | Patrón propuesto |
| Implementaciones | 0 implementaciones observadas |
| Repositorios | Ningún repositorio observado |
| Snapshot | kit/componentes/componentes-observados.json |
Consumidores observados
El inventario conserva repositorio, archivo fuente y criterio de asociación. Observar una implementación no la convierte automáticamente en canónica.
Estados
| Estado requerido |
|---|
| base |
| hover |
| focus |
| disabled |
| loading |
Accesibilidad
- Conserva nombre accesible, orden de lectura y foco visible.
- Funciona a 320 px, con zoom de 200% y texto largo.
- El color nunca es la única señal de estado o acción.
- El contenido esencial permanece disponible sin JavaScript.
Código HTML
Copia el bloque completo; después cambia contenido y destinos, no su anatomía.
<div class="ui-sample ui-actions" role="toolbar" aria-label="Edición del documento"><button class="btn btn--icon" type="button" aria-label="Negrita" title="Negrita" aria-pressed="false"><b aria-hidden="true">N</b></button><button class="btn btn--icon" type="button" aria-label="Cursiva" title="Cursiva" aria-pressed="false"><i aria-hidden="true">K</i></button><span role="separator" aria-orientation="vertical"></span><button class="btn btn--secondary" type="button">Insertar enlace</button></div>Código TAILWIND
Copia el bloque completo; después cambia contenido y destinos, no su anatomía.
<div class="ui-sample ui-actions border border-slate-300 bg-white p-4 text-slate-950" role="toolbar" aria-label="Edición del documento"><button class="btn btn--icon border border-slate-950 px-3 py-2 focus-visible:outline-2 focus-visible:outline-offset-2 disabled:cursor-not-allowed" type="button" aria-label="Negrita" title="Negrita" aria-pressed="false"><b aria-hidden="true">N</b></button><button class="btn btn--icon border border-slate-950 px-3 py-2 focus-visible:outline-2 focus-visible:outline-offset-2 disabled:cursor-not-allowed" type="button" aria-label="Cursiva" title="Cursiva" aria-pressed="false"><i aria-hidden="true">K</i></button><span role="separator" aria-orientation="vertical"></span><button class="btn btn--secondary border border-slate-950 px-3 py-2 focus-visible:outline-2 focus-visible:outline-offset-2 disabled:cursor-not-allowed" type="button">Insertar enlace</button></div>Código REACT
Copia el bloque completo; después cambia contenido y destinos, no su anatomía.
import type { MouseEventHandler } from "react";
export interface ToolbarButtonItem {
type?: "button";
/** Nombre de la acción; con `icon` va en `aria-label` y `title`, y el botón muestra sólo el icono. */
label: string;
/** Carácter que reemplaza al texto visible (N, K). */
icon?: string;
/** Envuelve el icono en `<b>` o `<i>` para anticipar el formato que aplica. */
iconStyle?: "bold" | "italic";
/** Estado de un botón que se activa y desactiva (`aria-pressed` e `is-active`); sin valor, el botón no es conmutable. */
pressed?: boolean;
disabled?: boolean;
onClick?: MouseEventHandler<HTMLButtonElement>;
}
export interface ToolbarSeparatorItem {
type: "separator";
}
export type ToolbarItem = ToolbarButtonItem | ToolbarSeparatorItem;
export interface ToolbarProps {
/** Nombre accesible de la barra (`aria-label`). */
label?: string;
/** Botones y separadores, en orden. */
items?: ToolbarItem[];
className?: string;
}
const HERRAMIENTAS: ToolbarItem[] = [
{ label: "Negrita", icon: "N", iconStyle: "bold", pressed: false },
{ label: "Cursiva", icon: "K", iconStyle: "italic", pressed: false },
{ type: "separator" },
{ label: "Insertar enlace" },
];
function ToolbarIcono({ icon, iconStyle }: Pick<ToolbarButtonItem, "icon" | "iconStyle">) {
if (iconStyle === "bold") return <b aria-hidden="true">{icon}</b>;
if (iconStyle === "italic") return <i aria-hidden="true">{icon}</i>;
return <span aria-hidden="true">{icon}</span>;
}
/**
* Barra de herramientas: acciones de edición sobre un mismo objeto (negrita, cursiva, insertar enlace), agrupadas en una fila.
*
* Los botones conmutables exponen su estado con `aria-pressed` y la clase `is-active`; un ítem `{ type: "separator" }` divide grupos.
*
* @example <Toolbar label="Edición del documento" items={[{ label: "Negrita", icon: "N", iconStyle: "bold", pressed: true }, { type: "separator" }, { label: "Insertar enlace" }]} />
*/
export function Toolbar({ label = "Edición del documento", items = HERRAMIENTAS, className }: ToolbarProps) {
if (!items.length) return null;
return (
<div className={["ui-actions", className].filter(Boolean).join(" ")} role="toolbar" aria-label={label}>
{items.map((item, indice) =>
item.type === "separator" ? (
<span key={indice} role="separator" aria-orientation="vertical"></span>
) : item.icon ? (
<button
key={indice}
className={item.pressed ? "btn btn--icon is-active" : "btn btn--icon"}
type="button"
aria-label={item.label}
title={item.label}
aria-pressed={item.pressed}
disabled={item.disabled}
onClick={item.onClick}
>
<ToolbarIcono icon={item.icon} iconStyle={item.iconStyle} />
</button>
) : (
<button
key={indice}
className={item.pressed ? "btn btn--secondary is-active" : "btn btn--secondary"}
type="button"
aria-pressed={item.pressed}
disabled={item.disabled}
onClick={item.onClick}
>
{item.label}
</button>
),
)}
</div>
);
}