npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

mbcj-ui-styles

v1.6.0

Published

Librería de estilos compartidos para el ecosistema TDF MBCyJ

Readme

mbcj-ui-styles

Librería de estilos compartidos y componentes de UI para el ecosistema de aplicaciones del Ministerio de Bienestar Ciudadano y Justicia (TDF).

Proporciona una identidad visual unificada (paleta, tipografía, layouts, formularios, tablas) para los sistemas React del ministerio.


Requisitos

Peer dependencies del proyecto consumidor:

  • React ^19.0.0
  • Wouter ^3.0.0 (para la navegación del Header)
  • @mui/material >=7.3.10
  • @mui/icons-material >=7.3.10
  • @emotion/react ^11.14.0
  • @emotion/styled ^11.14.1
  • react-toastify ^11.0.5

Instalación

npm install mbcj-ui-styles

En el archivo raíz (típicamente main.jsx o App.jsx):

import 'mbcj-ui-styles/src/index.css';

Tema y tipografía

El SDK exporta un theme de MUI que centraliza la tipografía institucional (Public Sans). Envolvé la app con ThemeProvider + CssBaseline para que todos los componentes MUI la hereden sin forzarla por CSS:

import { ThemeProvider, CssBaseline } from '@mui/material';
import { theme } from 'mbcj-ui-styles';
import 'mbcj-ui-styles/src/index.css';

export default function App() {
  return (
    <ThemeProvider theme={theme}>
      <CssBaseline />
      {/* ...resto de la app... */}
    </ThemeProvider>
  );
}

Componentes exportados

| Componente | Propósito | | ---------------- | ------------------------------------------------------------ | | Header | Barra de navegación institucional con menú responsive. | | Footer | Pie de página institucional. | | Tables | Tabla paginada con filas expandibles y server-side support. | | GenerarMessage | Diálogo de confirmación inline para usar en notificaciones. | | FormCard | Tarjeta contenedora para una sección de formulario. | | FormRow | Fila de campos en grilla responsive (1 a 4 columnas). | | CampoConLabel | Input con etiqueta visible arriba (variant standard). | | CampoSinLabel | Input estilo cápsula (pill) sin etiqueta, fondo gris suave. | | Boton | Botón institucional unificado; el estilo se elige con la prop variante. | | Header | (layout) Barra de navegación institucional. | | Footer | (layout) Pie de página institucional (sin props). | | Inicio | (layout) Pantalla de inicio: saludo + accesos agrupados por rol. | | theme | Tema MUI con la tipografía institucional (Public Sans). |

Nota de migración (1.4+): los antiguos BotonPrimario y BotonNaranja se unificaron en un único Boton con prop variante. Ver sección 2.


Reglas de uso

1. CampoConLabel vs CampoSinLabel — cuándo usar cada uno

Ambos cumplen la misma función técnica (un campo de texto controlado), pero responden a contextos visuales distintos. Elegir el correcto es importante para mantener consistencia entre sistemas.

| Criterio | CampoConLabel | CampoSinLabel | | ------------------------------- | ------------------------------------------------ | -------------------------------------------------------- | | Densidad de la pantalla | Alta (muchos campos) | Baja (1 a 3 campos visibles) | | Etiqueta | Siempre visible, en negrita, arriba del campo | Solo placeholder (gris dentro del campo) | | Borde / fondo | Línea inferior delgada | Cápsula con fondo gris suave | | Adornos (íconos) | Opcionales; el label sigue siendo la guía | Recomendados; el ícono refuerza la semántica del campo | | Estados disabled/error | Mensajes vía helperText y error | Solo opacidad reducida (sin texto de error) | | Tipo de pantalla | Edición de datos, formularios completos | Login, búsqueda rápida, autenticación en kiosco, modales |

Usar CampoConLabel cuando:

  • El formulario tiene 3 o más campos en pantalla.
  • El usuario necesita ver claramente qué dato pide cada campo, aún cuando el campo ya esté completado.
  • Se requiere validación con helperText (ej. "DNI inválido", "Las contraseñas no coinciden").
  • El campo va dentro de un FormCard con título y otros campos relacionados.

Placeholder por defecto: si no se pasa la prop placeholder, toma el mismo texto que label. Esto da una "ancla visual" dentro del input cuando está vacío. Para suprimirlo o personalizarlo, pasar placeholder="" o placeholder="Ej: ..." explícitamente.

Texto largo (multiline): para comentarios, motivos o descripciones, pasá multiline (y opcionalmente rows/minRows). El campo se presenta automáticamente como una caja con borde (textarea) en vez de la línea inferior, conservando el label arriba. El límite de caracteres se pasa con inputProps={{ maxLength: 300 }}.

<CampoConLabel label="Motivo" value={motivo} onChange={(e) => setMotivo(e.target.value.toUpperCase())} multiline minRows={5} inputProps={{ maxLength: 300 }} />
<FormCard titulo="Datos personales" onSubmit={guardar} acciones={<Boton variante="celeste" type="submit">Guardar</Boton>}>
  <FormRow>
    {/* placeholder automático "Apellidos" */}
    <CampoConLabel label="Apellidos" required value={apellidos} onChange={(e) => setApellidos(e.target.value.toUpperCase())} />
    {/* placeholder personalizado */}
    <CampoConLabel label="DNI" placeholder="Ej: 00.000.000" required value={dni} onChange={(e) => setDni(e.target.value)} />
  </FormRow>
</FormCard>

Usar CampoSinLabel cuando:

  • La pantalla tiene muy pocos campos y el contexto (título de la pantalla, ícono interno) ya transmite qué se pide.
  • Se busca una estética liviana y minimalista (login, splash de bienvenida, barra de búsqueda).
  • Se quiere reforzar el campo con íconos que actúan como guía visual.
<CampoSinLabel
  placeholder="00.000.000"
  type="number"
  required
  value={documento}
  onChange={(e) => setDocumento(e.target.value)}
  startAdornment={<Person />}
/>

NO usar CampoSinLabel cuando:

  • Hay más de 3 campos en pantalla (genera carga cognitiva: el usuario no sabe qué pide cada uno una vez completado).
  • Se requiere mostrar texto de error o helper específico.
  • El campo está dentro de un FormCard con otros CampoConLabel (mezclar estilos confunde).

2. Boton — variantes y cuándo usar cada una

Boton es el botón institucional unificado. La apariencia se elige con la prop variante (nombrada por color). Reemplaza a los antiguos BotonPrimario/BotonNaranja.

| variante | Estilo | Usar para | | ----------- | ------------------------------- | ------------------------------------------------------------ | | celeste | Outline azul (#0073E6) | Acción principal repetible: Guardar, Actualizar, Buscar, Nuevo, Continuar (valor por defecto). | | naranja | Relleno naranja (#F57600) | Acción final/crítica del flujo: Enviar, Validar, Registrar, Confirmar. Idealmente una por flujo. | | verde | Relleno verde | Acción positiva final: Aprobar, Activar, Finalizar, Terminar. | | rojo | Relleno rojo | Acción destructiva: Eliminar, Anular, Desactivar, Quitar. | | negro | Outline gris neutro | Acción neutra: Cancelar, Volver, Salir, Corregir. | | azul | Relleno azul institucional | Sesión / navegación: Iniciar sesión, Cerrar sesión. |

Props: variante (default 'celeste'), children (etiqueta), onClick, type ('button'|'submit'|'reset'), disabled, startIcon, endIcon, fullWidth, sx.

import { Boton } from 'mbcj-ui-styles';

// Botonera típica de un formulario (salir + guardar)
<Boton variante="negro"   onClick={salir}>Salir</Boton>
<Boton variante="celeste" type="submit">Guardar</Boton>

// Acción final / destructiva
<Boton variante="naranja" onClick={enviar}>Enviar</Boton>
<Boton variante="rojo"    startIcon={<DeleteIcon/>} onClick={eliminar}>Eliminar</Boton>

3. FormCard + FormRow — estructura de formularios

FormCard es el contenedor de una sección de formulario. FormRow distribuye los campos dentro en una grilla responsive.

<FormCard
  titulo="Datos de Contacto"
  onSubmit={guardar}
  acciones={<Boton variante="celeste" type="submit">Guardar</Boton>}
>
  <FormRow columns={1}>
    <Autocomplete ... renderInput={(p) => <TextField {...p} variant="standard" InputLabelProps={{shrink:true}} />} />
  </FormRow>
  <FormRow>
    <CampoConLabel label="Localidad" ... />
    <CampoConLabel label="Código Postal" ... />
  </FormRow>
  <FormRow columns={3}>
    <CampoConLabel label="Número" ... />
    <CampoConLabel label="Puerta" ... />
    <CampoConLabel label="Piso" ... />
  </FormRow>
</FormCard>
  • columns (1, 2, 3 o 4) define el máximo en desktop; colapsa automáticamente a 1 columna en mobile (auto-fit minmax(240px, 1fr)).
  • Para anidar (por ejemplo, calle ocupa media fila y a la derecha tres campitos), usar un FormRow dentro de otro.
  • El botón submit va en acciones, no como hijo del FormCard.
  • Si el formulario no es un <form> HTML, omitir onSubmit y disparar la lógica desde los botones con onClick.

4. Tokens CSS (en variables.css)

Disponibles globalmente; preferir tokens antes que hardcodear colores:

| Token | Uso | | ---------------------------------- | -------------------------------------------------------- | | --color-primary | Azul institucional general (#1976d2). | | --color-outline-primary | Azul outline (#0073E6) — botones primarios. | | --color-outline-primary-hover | Hover de outline primario. | | --color-accent / --color-accent-hover | Naranja crítico (#F57600) — Boton variante="naranja". | | --color-success / --color-success-hover | Verde — Boton variante="verde". | | --color-danger / --color-danger-hover | Rojo — Boton variante="rojo". | | --color-neutral / --color-neutral-hover | Gris neutro — Boton variante="negro". | | --color-azul / --color-azul-hover | Azul institucional — Boton variante="azul". | | --bg-white / --bg-subtle | Fondos claros (cards, filas de tabla, inputs pill). | | --bg-inactive | Fondo gris para filas/inputs deshabilitados. | | --border-input / --border-light| Bordes de inputs y separadores suaves. | | --radius-button / --radius-card| Radios consistentes (10px / 12px). | | --label-weight / --label-color | Negrita y color del label en CampoConLabel. | | --font-family-base | Tipografía base (Public Sans, system-ui). | | --z-modal / --z-toast / etc. | Escalera de z-index institucional. |


5. Tables — paginación server-side

Ver JSDoc del componente en src/layout/Tables.jsx. Resumen:

  • Acepta cabeceras (definición de columnas con id, titulo, align, render), resultados, total, page (0-indexed), rowsPerPage, callbacks handleChangePage y handleChangeRowsPerPage.
  • Para filas expandibles, pasar renderExpandableRow={(row) => <Detalle ... />}.
  • Las filas se colorean según row.activo (booleano).
  • El backend debe aceptar limit, offset y opcionalmente busqueda como query params.

6. Patrones recomendados

  • No mezclar CampoConLabel y CampoSinLabel en la misma pantalla.
  • Un solo Boton variante="naranja" visible a la vez (acción final principal).
  • Validaciones por sección: cada FormCard con su propio onSubmit y su propio botón, para mantener la sintaxis de endpoints separados (perfil, contacto, password, etc.).
  • Mobile-first: el SDK ya colapsa filas y secciones; evitar overrides de ancho que rompan ese comportamiento.
  • Iconos: usar @mui/icons-material para mantener consistencia. Pasar como startAdornment / endAdornment.

7. Layout — Header, Footer, Inicio

Header — barra de navegación institucional. Las opciones deben llegar ya filtradas por rol (el Header no filtra internamente).

| Prop | Tipo | Descripción | | --------------- | ------------------ | ------------------------------------------------------------------ | | logged | boolean | Si hay usuario logueado. Si es false, oculta el menú y muestra "Ingresar". | | opciones | {titulo, url}[] | Ítems de menú (ya filtrados por rol). | | onLogout | function | Handler de "Cerrar Sesión". | | login | string | Ruta de login (default "/ingresar"). | | tituloSistema | string | Texto alt del logo institucional. |

<Header logged={logged} opciones={opcionesDelRol} onLogout={logout} />

Footer — sin props; el contenido se gestiona en el SDK y se distribuye uniforme a todos los sistemas.

<Footer />

Inicio — pantalla de inicio con accesos agrupados por rol. No navega internamente: cada item.onClick y loginCta.onClick los provee el consumidor (sirve con cualquier router).

| Prop | Tipo | Descripción | | --------------- | -------------------------------------- | ----------------------------------------------------------------- | | usuario | object | null | Usuario logueado, o null para la vista pública (con loginCta). | | nombreSistema | string | Nombre del sistema. | | roles | { [rolId]: {nombre, color} } | Mapeo rol_id → etiqueta y color del chip. | | secciones | {titulo, descripcion?, roles, items}[] | items: {label, icon?, onClick, roles, variante?}. | | loginCta | {texto, icon?, mensaje?, onClick} | CTA mostrado cuando usuario === null. | | getRolId | function | Resuelve el rol_id (default parseInt(u.tipo_usuario_id, 10)). |

<Inicio
  usuario={usuario}
  nombreSistema="Sistema de Comedores"
  roles={{ 4: { nombre: 'Administrador', color: 'error' } }}
  secciones={[{
    titulo: 'Gestión', roles: [4],
    items: [{ label: 'Comedores', icon: <RestaurantIcon/>, onClick: () => navigate('/comedores'), roles: [4] }]
  }]}
  loginCta={{ texto: 'INICIAR SESIÓN', onClick: () => navigate('/ingresar') }}
/>

Desarrollo local

Si se hace npm link desde el SDK a un proyecto consumidor:

  1. Asegurar que el proyecto consumidor tenga en vite.config.js:

    resolve: {
      dedupe: ['react', 'react-dom', 'wouter', '@mui/material', '@emotion/react', '@emotion/styled']
    }

    Esto evita instancias duplicadas que rompen Context (ej. <Router base> de wouter).

  2. No usar preserveSymlinks: true; Vite necesita resolver tras el symlink para deduplicar.

  3. Borrar node_modules/.vite si los cambios de CSS del SDK no se reflejan.


Versiones

  • 1.6.0CampoConLabel con multiline ahora se presenta como caja con borde (textarea), no como línea inferior. Aplica automáticamente, dentro o fuera de FormCard. Header: el ítem activo se resuelve por sección (primer segmento de la ruta), por lo que las sub-rutas (ej. /visitas/cargas) resaltan su ítem padre.
  • 1.4.0 – 1.5.0BotonPrimario/BotonNaranja unificados en Boton con prop variante (celeste/naranja/verde/rojo/negro/azul). Nuevo theme MUI (tipografía Public Sans) y componente Inicio. Paleta semántica de tokens.
  • 1.3.2CampoConLabel ahora hereda automáticamente placeholder = label cuando no se especifica uno.
  • 1.3.0CampoConLabel y CampoSinLabel (renombrado de CampoTexto y CampoPill).
  • 1.2.0 — Nuevos componentes de formulario: FormCard, FormRow, CampoTexto, CampoPill, BotonPrimario, BotonNaranja. Tokens de paleta institucional.
  • 1.1.0Tables con paginación server-side y filas expandibles.
  • 1.0.x — Header, Footer, GenerarMessage.