Apariencia
Guía de diseño
Un plugin del panel no tiene diseño propio: usa el del panel. La interfaz de un plugin debe ser indistinguible de la del núcleo, y eso se consigue de una sola manera: todo sale de los tokens y del kit de componentes.
Regla del sistema (
admin/src/styles/tokens.css): ningún componente ni página usa colores, radios, sombras o tamaños sueltos; todo sale de los tokens. Así todas las pantallas se ven igual.
- Tokens de diseño: colores, tipografía, espaciado, radios y sombras, en claro y oscuro, leídos de
tokens.css. - Catálogo de componentes: los componentes de
admin/src/components/uicon sus props, eventos, slots y un ejemplo de uso. - Iconos: los iconos disponibles.
Principios
- Usa el kit antes de escribir HTML. Cada pantalla empieza por
AppPage; las listas sonUiTableoUiList; los formularios,UiFieldcon su control. Si el kit no tiene lo que necesitas, compón con él antes de crear un componente nuevo. - Nada de valores sueltos. En el CSS de tu plugin usa
var(--s-4),var(--r-md),var(--surface),var(--fs-md). Un#fff, un12pxo unborder-radius: 8pxliteral son un defecto. - El color solo tiene significado. El acento (
--accent) es la marca; verde, ámbar, rojo y azul son estados (success,warning,danger,info). No se colorea por decoración. - Claro y oscuro desde el principio. Los tokens cambian solos con el tema (
prefers-color-schemeodata-theme). Si usas solo variables, tu pantalla funciona en los dos. Comprueba siempre en oscuro. - Móvil.
UiTablese convierte en tarjetas por debajo de 720 px. Define qué columna es el título (mobile: "title") y cuáles se ocultan (mobile: "hide"). - Accesibilidad. El foco visible (
:focus-visible) lo pone la base. Los textosmutedyfaintcumplen 4,5:1 sobre las superficies indicadas en Tokens. Los botones de solo icono llevanlabel; los controles sin texto visible llevanaria-label. - Estados completos. Cada lista tiene su estado de carga (
loadingoUiSkeleton), vacío (UiEmptycon el primer paso) y error (UiLoadErrorcon reintento).
Estructura típica de una página
vue
<AppPage :title="t('Notas')" :description="t('Notas internas del local.')">
<template #actions><UiButton variant="primary" icon="plus">{{ t("Nueva nota") }}</UiButton></template>
<UiToolbar><UiSearch v-model="q" /></UiToolbar>
<UiCard flush>
<UiTable … />
</UiCard>
</AppPage>- Una acción principal por pantalla (
variant="primary"). - Editar sin perder la lista:
UiDrawerpara fichas largas,UiModalpara formularios cortos y confirmaciones. - Confirmar lo destructivo con
useFeedback().confirm({ tone: "danger" }); avisar del resultado conuseFeedback().toast().
Tono de los textos
Las pantallas internas hablan serio y técnico:
- Frases cortas, en infinitivo para acciones (
Guardar,Crear nota) y en indicativo para estados (Sin notas). - Sin chistes, modismos, exclamaciones ni emojis.
- Di qué ocurre y qué hacer: «No se pudo guardar. Inténtalo de nuevo.», no «¡Ups!».
- Los errores de la API llegan traducidos por su
code; muestra el mensaje conuseFeedback().error(err). - Un estado vacío explica qué es la pantalla y ofrece el primer paso.
- Los botones nombran la acción, no «Aceptar»:
Borrar nota,Guardar cambios. - Cifras, fechas y dinero con
@/lib/format(money,number,percent,date,dateTime): respetan el idioma.
Iconos
Un único estilo: trazo de 1,75 sobre rejilla de 24x24 (UiIcon). El icono acompaña al texto, no lo sustituye; si va solo, el botón lleva label. El nombre de icono de plugin.json y de la entrada de menú es uno de este catálogo. No incluyas SVG propios en la interfaz salvo ilustraciones que el kit no cubra.
Idiomas (i18n)
- El español es la clave.
t("Nueva nota")es el texto en español y también la clave del diccionario. El español no lleva fichero. - Cada plugin trae
admin/i18n/{en,ca,fr}.json: un objeto plano{ "Texto en español": "Translation" }. El runtime carga solo el idioma activo. - Parámetros:
t("Hay {n} notas", { n: 3 }). El nombre va entre llaves; si falta, la llave se queda tal cual. - Si una clave no tiene traducción, se muestra el español: ninguna pantalla se rompe, pero no dejes claves sin traducir.
- Un texto que ya existe en el panel con la misma clave no se pisa desde un plugin: gana el del panel. Usa un texto distinto si necesitas otro matiz.
- En la API, los textos van con
trKey("texto en español"); en notificaciones,trFor(locale, texto, params). - Un tutorial (
tour.ts) lleva sus textos en el mismo diccionario del plugin. - Idiomas del panel:
es,ca,fr,en.
Qué evitar
- Importar CSS global o una librería de componentes ajena: el panel ya trae el kit.
- Fijar fuentes: la tipografía viene de
--font,--font-displayy--mono. - Estilos con
!importanto que afecten fuera de tu componente: usa<style scoped>. - Mostrar la pantalla de un módulo apagado: declara
featureymeta.permissionen la ruta.