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

ten-minds-beta

v1.0.1

Published

Librería de componentes UI para proyectos Angular de Ten Minds

Downloads

211

Readme

ten-minds-beta

Librería de componentes UI para Angular, construida con Tailwind CSS v4 y Lucide Icons.


Instalación

npm install ten-minds-beta

Configuración inicial

1.- Instalar la sgte libreria lucide-angular npm i lucide-angular

1. Agregar los estilos de la librería

Importá los estilos de la librería en el styles.css de tu proyecto:

@import 'ten-minds-beta/styles/styles.css';

Customización de Estilos

La librería ten-minds-beta ofrece tres niveles de personalización según el alcance que necesites.


Nivel 1 — Global: sobreescribir tokens CSS

Afecta todos los componentes que usen ese token en toda la app.

Agrega esto en el styles.css de tu proyecto:

:root {
  --tm-gray-900: #your-brand-color;
}

O directamente el token semántico:

:root {
  --color-tm-brand: #your-brand-color;
  --color-tm-primary: #your-text-color;
}

Tokens disponibles

| Token | Descripción | Default (light) | |---|---|---| | --color-tm-brand | Color principal de la marca | #141414 | | --color-tm-primary | Texto principal | #141414 | | --color-tm-secondary | Texto secundario | #737373 | | --color-tm-tertiary | Texto terciario | #A0A0A0 | | --color-tm-inverse | Texto sobre fondos oscuros | #FFFFFF | | --color-tm-canvas | Fondo general de la app | #FAFAFA | | --color-tm-surface | Fondo de tarjetas y paneles | #FFFFFF | | --color-tm-subtle | Fondo sutil | #F0F0F0 | | --color-tm-muted | Fondo atenuado | #E4E4E4 | | --color-tm-brand-hover-default | Hover del botón brand | #363636 | | --color-tm-brand-hover-subtle | Hover sutil del botón brand | #F0F0F0 | | --color-tm-default | Borde por defecto | #E4E4E4 | | --color-tm-strong | Borde fuerte | #C8C8C8 |


Nivel 2 — Scoped: cambiar un componente en una sección

Afecta solo los componentes dentro de un contenedor específico. Se usa CSS puro — no Tailwind.

/* styles.css del proyecto consumidor */
.mi-seccion tm-btn-primary button {
  background-color: #your-color !important;
  font-size: 1.25rem !important;
  border-radius: 999px !important;
}
<div class="mi-seccion">
  <tm-btn-primary>Solo este botón cambia</tm-btn-primary>
</div>

<tm-btn-primary>Este queda igual</tm-btn-primary>

⚠️ CSS puro únicamente. Tailwind no funciona aquí porque se compila en build time y las clases de la librería no están disponibles en el proyecto consumidor.


Nivel 3 — Puntual: un solo componente con customClass

Afecta únicamente el componente donde lo aplicas. Aquí sí puedes usar clases de Tailwind porque se compilan en el proyecto consumidor.

<!-- Tailwind funciona aquí -->
<tm-btn-primary customClass="!bg-orange-500 !text-black">
  Botón personalizado
</tm-btn-primary>

<!-- CSS también funciona con clase propia -->
<tm-btn-primary customClass="mi-boton-especial">
  Botón personalizado
</tm-btn-primary>
/* styles.css */
.mi-boton-especial {
  background-color: orange !important;
  font-size: 1.25rem !important;
}

ℹ️ El modificador ! de Tailwind es necesario para sobreescribir estilos ya definidos por el componente.


Resumen

| Método | Alcance | Tailwind | Cuándo usarlo | |---|---|---|---| | Token CSS en :root | Toda la app | ✗ | Cambiar la paleta de colores del proyecto | | CSS scoped con clase padre | Una sección | ✗ | Variante especial en una página o sección | | customClass input | Un componente | ✓ | Ajuste puntual en un solo componente |

2. Importar los componentes

Los componentes son standalone — solo importa los que necesites directamente en tu componente o módulo:

import { BtnPrimary, Chip, CheckboxButton } from 'ten-minds-beta';

@Component({
  imports: [BtnPrimary, Chip, CheckboxButton],
})

3. Usar íconos

Los íconos se importan directamente desde lucide-angular como objetos. No se requiere ninguna configuración global en app.config.ts.

import { Download, Trash2, Check } from 'lucide-angular';

export class MiComponente {
  download = Download;
  trash    = Trash2;
  check    = Check;
}
<tm-btn-primary [iconLeft]="download">Exportar</tm-btn-primary>
<tm-chip [iconLeft]="check">Activo</tm-chip>

Componentes disponibles

| Componente | Selector | Descripción | |---|---|---| | BtnPrimary | tm-btn-primary | Botón principal con múltiples variantes | | Chip | tm-chip | Etiqueta de estado o categoría | | CheckboxButton | tm-checkbox | Checkbox personalizado | | Toggle | tm-toggle | Switch on/off | | RadioButton | tm-radio-button | Botón de selección única | | InputTen | tm-input | Campo de texto | | Avatar | tm-avatar | Avatar con iniciales y estado | | SnackbarHost | tm-snackbar-host | Contenedor de notificaciones |


Dark Mode

La librería soporta dark mode mediante la clase .dark en el elemento raíz. Agrégala o quítala desde tu componente:

onThemeChange(mode: 'light' | 'dark') {
  if (mode === 'dark') {
    document.documentElement.classList.add('dark');
  } else {
    document.documentElement.classList.remove('dark');
  }
}

Componentes

BtnPrimary

Componente de botón principal de la librería ten-minds-beta. Soporta múltiples colores, variantes, tamaños, bordes redondeados e íconos.


Instalación

npm install ten-minds-beta

Importa el componente en tu módulo o componente standalone:

import { BtnPrimary } from 'ten-minds-beta';

@Component({
  imports: [BtnPrimary],
})

API

Inputs

| Prop | Tipo | Default | Descripción | |---|---|---|---| | color | ButtonColor | 'primary' | Color del botón | | size | ButtonSize | 'md' | Tamaño del botón | | type | ButtonType | 'filled' | Variante visual | | rounded | ButtonRounded | 'default' | Forma del borde | | disabled | boolean | false | Desactiva el botón | | active | boolean | false | Aplica estado activo (brightness) | | iconLeft | LucideIconData | — | Ícono a la izquierda del texto | | iconRight | LucideIconData | — | Ícono a la derecha del texto |

Outputs

| Evento | Tipo | Descripción | |---|---|---| | clicked | MouseEvent | Se emite al hacer click (no emite si está disabled) |

Tipos

type ButtonColor   = 'primary' | 'danger' | 'success' | 'warning';
type ButtonSize    = 'lg' | 'md' | 'sm';
type ButtonType    = 'filled' | 'outlined' | 'ghost';
type ButtonRounded = 'default' | 'full';

Ejemplos

Colores

<btn-primary color="primary">Primary</btn-primary>
<btn-primary color="danger">Danger</btn-primary>
<btn-primary color="success">Success</btn-primary>
<btn-primary color="warning">Warning</btn-primary>

Variantes

<btn-primary type="filled">Filled</btn-primary>
<btn-primary type="outlined">Outlined</btn-primary>
<btn-primary type="ghost">Ghost</btn-primary>

Tamaños

<btn-primary size="lg">Large</btn-primary>
<btn-primary size="md">Medium</btn-primary>
<btn-primary size="sm">Small</btn-primary>

Bordes redondeados

<btn-primary rounded="default">Default</btn-primary>
<btn-primary rounded="full">Pill</btn-primary>

Disabled

<btn-primary [disabled]="true">No disponible</btn-primary>
<btn-primary color="danger" type="outlined" [disabled]="true">Eliminar</btn-primary>

Con íconos

Los íconos se pasan como objetos LucideIconData, no como strings.

// app.ts
import { Download, Trash2, Check } from 'lucide-angular';

export class App {
  download = Download;
  trash    = Trash2;
  check    = Check;
}
<!-- Ícono izquierdo -->
<btn-primary [iconLeft]="download">Exportar</btn-primary>

<!-- Ícono derecho -->
<btn-primary color="success" [iconRight]="check">Confirmar</btn-primary>

<!-- Ambos íconos -->
<btn-primary color="danger" type="outlined" [iconLeft]="trash" [iconRight]="check">
  Eliminar y confirmar
</btn-primary>

Combinaciones comunes

<!-- Botón de acción principal -->
<btn-primary color="primary" type="filled" size="md" [iconLeft]="download">
  Exportar
</btn-primary>

<!-- Botón destructivo -->
<btn-primary color="danger" type="outlined" size="md" [iconLeft]="trash">
  Eliminar
</btn-primary>

<!-- Botón redondeado estilo pill -->
<btn-primary color="primary" type="filled" rounded="full" [iconLeft]="check">
  Confirmar
</btn-primary>

<!-- Botón pequeño fantasma -->
<btn-primary color="primary" type="ghost" size="sm">
  Ver más
</btn-primary>

Manejo de eventos

<btn-primary color="success" (clicked)="onGuardar($event)">
  Guardar
</btn-primary>
onGuardar(event: MouseEvent) {
  console.log('Guardando...', event);
}

Notas

  • El evento clicked no se emite cuando disabled es true, no es necesario manejarlo manualmente.
  • Los íconos deben importarse desde lucide-angular como objetos y asignarse a propiedades del componente. Pasar strings directamente no funciona.
  • No se requiere ninguna configuración global de íconos en app.config.ts.

Desarrollo

# Buildear la librería en modo watch
ng build ten-minds-beta --watch

# Servir la app de prueba
ng serve consumer-app

SnackbarService

Sistema de notificaciones globales para mostrar feedback visual al usuario.

Configuración (una sola vez)

app.html:

<snackbar-host /> <router-outlet />

app.ts:

import { SnackbarHost } from 'borealis-ui';

@Component({
  imports: [SnackbarHost],
})

Uso

import { SnackbarService } from 'borealis-ui';

export class MiComponente {
  snackbar = inject(SnackbarService);

  async onGuardar() {
    try {
      await this.service.guardar(data);
      this.snackbar.alert('success', 'Guardado', 'Los cambios fueron guardados correctamente');
    } catch {
      this.snackbar.alert('error', 'Error', 'No pudimos guardar los cambios');
    }
  }
}

Métodos

| Método | Descripción | | --------------------------------------------------------- | -------------------------------- | | alert(color, title, description?, duration?, position?) | Desaparece automáticamente | | notify(color, title, description?, position?) | El usuario la cierra manualmente |

Parámetros

| Parámetro | Tipo | Default | Valores | | ------------- | ------------------ | ----------- | -------------------------------------------------------------------------------- | | color | SnackbarColor | — | success error warning info | | title | string | — | — | | description | string | undefined | — | | duration | number | 5000 | Milisegundos | | position | SnackbarPosition | top-right | top-left top-center top-right bottom-left bottom-center bottom-right |

Ejemplos

// Mínimo
this.snackbar.alert('success', 'Guardado');

// Con descripción
this.snackbar.alert('error', 'Error de pago', 'No pudimos procesar tu método de pago');

// Con duración personalizada
this.snackbar.alert('warning', 'Licencia por expirar', 'Expira en 7 días', 2000);

// Con posición personalizada
this.snackbar.alert('info', 'Nueva versión', 'Yana 2.0 disponible', 5000, 'bottom-center');

// Notificación persistente
this.snackbar.notify('success', 'Compra exitosa', 'Tu licencia fue activada');

// Notificación con posición
this.snackbar.notify('error', 'Sin conexión', undefined, 'bottom-right');

Chip

Componente para mostrar estados, etiquetas o valores visuales. Puede ser estático o clickeable.


Instalación

import { Chip } from 'ten-minds-beta';

@Component({
  imports: [Chip],
})

Status (colores)

| Status | Uso recomendado | | --------- | --------------------------------- | | success | Activo, completado, aprobado | | warning | Pendiente, por vencer, precaución | | danger | Inactivo, error, rechazado | | info | Informativo, en proceso | | indigo | Categorías, etiquetas neutras | | brand | Destacado, principal |

<chip status="success">Activo</chip>
<chip status="warning">Pendiente</chip>
<chip status="danger">Inactivo</chip>
<chip status="info">En proceso</chip>
<chip status="indigo">Categoría</chip>
<chip status="brand">Destacado</chip>

Type (estilo visual)

| Type | Descripción | | ---------- | --------------------------------------- | | filled | Fondo sólido con texto blanco | | subtle | Fondo suave con texto del color | | outlined | Sin fondo, solo borde y texto del color |

<chip status="success" type="filled">Filled</chip>
<chip status="success" type="subtle">Subtle</chip>
<chip status="success" type="outlined">Outlined</chip>

Rounded (forma)

| Rounded | Uso recomendado | | --------- | ------------------------------------------------ | | default | Valores estáticos, estados, badges informativos | | full | Filtros, tags interactivos, estados de productos |

<chip status="info" rounded="default">Estado</chip>
<chip status="info" rounded="full">Etiqueta</chip>

Iconos

Acepta cualquier nombre de icono de Lucide en iconLeft o iconRight.

<!-- Solo icono izquierdo -->
<chip status="success" iconLeft="check">Activo</chip>

<!-- Solo icono derecho -->
<chip status="info" iconRight="x">Etiqueta</chip>

<!-- Ambos iconos -->
<chip status="warning" iconLeft="triangle-alert" iconRight="x">Advertencia</chip>

Clickeable

Por defecto el chip es estático. Para hacerlo interactivo usa [clickable]="true" y escucha el evento (clicked).

<chip
  status="danger"
  type="filled"
  rounded="full"
  iconLeft="circle-x"
  [clickable]="true"
  (clicked)="onDesactivar()"
>
  Desactivar
</chip>
onDesactivar() {
  console.log('chip clickeado');
}

Si clickable es false (default), el chip no emite eventos ni muestra cursor pointer.


Referencia de inputs y outputs

| Input | Tipo | Default | Descripción | | ----------- | ------------- | ----------- | ----------------------------------------------------- | | status | ChipStatus | 'brand' | Color del chip | | type | ChipType | 'filled' | Estilo visual del chip | | rounded | ChipRounded | 'default' | Forma del borde | | iconLeft | string | — | Nombre del icono Lucide a la izquierda (opcional) | | iconRight | string | — | Nombre del icono Lucide a la derecha (opcional) | | clickable | boolean | false | Habilita cursor pointer y emite evento al hacer click |

| Output | Tipo | Descripción | | --------- | ------ | --------------------------------------------- | | clicked | void | Se emite al hacer click si clickable="true" |


Casos de uso reales

<!-- Estado de un producto -->
<chip status="success" type="subtle" rounded="full" iconLeft="check"> Activo </chip>

<!-- Licencia por vencer -->
<chip status="warning" type="filled" rounded="default" iconLeft="triangle-alert"> Por vencer </chip>

<!-- Filtro clickeable -->
<chip status="indigo" type="outlined" rounded="full" [clickable]="true" (clicked)="filtrar()">
  Electrónica
</chip>

<!-- Notificación de error -->
<chip status="danger" type="subtle" rounded="default" iconLeft="circle-x"> Pago rechazado </chip>

Componente Toggle

Componente de interruptor para activar o desactivar estados. Emite el nuevo valor al padre para que maneje la lógica correspondiente.


Instalación

import { Toggle } from 'ten-minds-beta';

@Component({
  imports: [Toggle],
})

Uso básico

<toggle [value]="isActive" (changed)="isActive = $event" />

Inputs y Outputs

| Input | Tipo | Default | Descripción | | ---------- | --------- | ------- | ------------------------ | | value | boolean | false | Estado actual del toggle | | disabled | boolean | false | Desactiva la interacción |

| Output | Tipo | Descripción | | --------- | --------- | ------------------------------------ | | changed | boolean | Emite el nuevo estado al hacer click |


Estados

Default (apagado)

<toggle [value]="false" (changed)="onChanged($event)" />

Active (encendido)

<toggle [value]="true" (changed)="onChanged($event)" />

Disabled

<toggle [value]="false" [disabled]="true" /> <toggle [value]="true" [disabled]="true" />

Manejo del estado

El toggle no guarda estado internamente. El padre es responsable de actualizar el valor:

isActive = false;

onChanged(value: boolean) {
  this.isActive = value;
}
<toggle [value]="isActive" (changed)="onChanged($event)" />

O de forma inline:

<toggle [value]="isActive" (changed)="isActive = $event" />

Casos de uso reales

Activar notificaciones

<div class="flex items-center gap-3">
  <toggle [value]="notificaciones" (changed)="notificaciones = $event" />
  <span>Notificaciones</span>
</div>

@if(notificaciones) {
<p>Recibirás notificaciones de nuevos eventos.</p>
} @else {
<p>Las notificaciones están desactivadas.</p>
}

Activar/desactivar un producto

<div class="flex items-center gap-3">
  <toggle [value]="producto.activo" (changed)="toggleProducto($event)" />
  <span>{{ producto.activo ? 'Activo' : 'Inactivo' }}</span>
</div>
toggleProducto(value: boolean) {
  this.producto.activo = value;
  this.productoService.actualizarEstado(this.producto.id, value);
}

Con disabled según permisos

<toggle
  [value]="configuracion.habilitado"
  [disabled]="!tienePermisos"
  (changed)="configuracion.habilitado = $event"
/>

Checkbox

Componente de selección para formularios. Permite marcar o desmarcar una opción individual. Semánticamente correcto y accesible.


Instalación

import { CheckboxButton } from 'ten-minds-beta';

@Component({
  imports: [CheckboxButton],
})

Uso básico

<checkbox-button [checked]="isChecked" (changed)="isChecked = $event" />

Inputs y Outputs

| Input | Tipo | Default | Descripción | | ---------- | --------- | ------------- | ------------------------------------------------- | | checked | boolean | false | Estado actual del checkbox | | disabled | boolean | false | Desactiva la interacción | | label | string | — | Texto descriptivo al lado del checkbox (opcional) | | id | string | auto-generado | ID del input, útil para asociar labels externos |

| Output | Tipo | Descripción | | --------- | --------- | ------------------------------------ | | changed | boolean | Emite el nuevo estado al hacer click |


Estados

Default (desmarcado)

<checkbox-button [checked]="false" (changed)="isChecked = $event" />

Checked (marcado)

<checkbox-button [checked]="true" (changed)="isChecked = $event" />

Disabled desmarcado

<checkbox-button [checked]="false" [disabled]="true" />

Disabled marcado

<checkbox-button [checked]="true" [disabled]="true" />

Con label

<checkbox-button
  [checked]="isChecked"
  label="Aceptar términos y condiciones"
  (changed)="isChecked = $event"
/>

Manejo del estado

El checkbox no guarda estado internamente. El padre es responsable de actualizar el valor:

isChecked = false;

onChanged(value: boolean) {
  this.isChecked = value;
}
<checkbox-button [checked]="isChecked" (changed)="onChanged($event)" />

O de forma inline:

<checkbox-button [checked]="isChecked" (changed)="isChecked = $event" />

Casos de uso reales

Aceptar términos

<checkbox-button
  [checked]="aceptaTerminos"
  label="Acepto los términos y condiciones"
  (changed)="aceptaTerminos = $event"
/>

<btn-primary [disabled]="!aceptaTerminos" type="filled" (clicked)="continuar()">
  Continuar
</btn-primary>

Lista de permisos

@for(permiso of permisos; track permiso.id) {
<checkbox-button
  [checked]="permiso.activo"
  [label]="permiso.nombre"
  (changed)="togglePermiso(permiso.id, $event)"
/>
}
togglePermiso(id: string, value: boolean) {
  const permiso = this.permisos.find(p => p.id === id);
  if (permiso) permiso.activo = value;
}

Con disabled según permisos

<checkbox-button
  [checked]="opcion.activa"
  [disabled]="!tienePermisos"
  [label]="opcion.nombre"
  (changed)="opcion.activa = $event"
/>

Radio Button

Componente de selección única para formularios. Se usa en grupos donde solo una opción puede estar seleccionada a la vez.


Instalación

import { RadioButton } from 'ten-minds-beta';

@Component({
  imports: [RadioButton],
})

Uso básico

<radio-button
  value="opcion1"
  name="mi-grupo"
  [checked]="selected === 'opcion1'"
  (changed)="selected = $event"
/>

Inputs y Outputs

| Input | Tipo | Default | Descripción | | ---------- | --------- | --------------- | ------------------------------------------------------- | | value | string | requerido | Valor que emite cuando se selecciona | | name | string | 'radio-group' | Nombre del grupo, debe ser igual en todos los del grupo | | checked | boolean | false | Estado actual del radio | | disabled | boolean | false | Desactiva la interacción | | label | string | — | Texto descriptivo al lado del radio (opcional) | | id | string | auto-generado | ID del input, útil para asociar labels externos |

| Output | Tipo | Descripción | | --------- | -------- | --------------------------------------- | | changed | string | Emite el value del radio seleccionado |


Estados

Default (sin seleccionar)

<radio-button value="opcion1" name="grupo" [checked]="false" (changed)="onChanged($event)" />

Checked (seleccionado)

<radio-button value="opcion1" name="grupo" [checked]="true" (changed)="onChanged($event)" />

Disabled desmarcado

<radio-button value="opcion1" name="grupo" [checked]="false" [disabled]="true" />

Disabled marcado

<radio-button value="opcion1" name="grupo" [checked]="true" [disabled]="true" />

Uso en grupo

El name debe ser el mismo en todos los radio del grupo. El padre controla cuál está seleccionado comparando value con el valor actual:

selected = 'mensual';

onChanged(value: string) {
  this.selected = value;
}
<div class="flex flex-col gap-3">
  <radio-button
    value="mensual"
    name="plan"
    label="Mensual"
    [checked]="selected === 'mensual'"
    (changed)="onChanged($event)"
  />
  <radio-button
    value="anual"
    name="plan"
    label="Anual"
    [checked]="selected === 'anual'"
    (changed)="onChanged($event)"
  />
  <radio-button
    value="lifetime"
    name="plan"
    label="De por vida"
    [checked]="selected === 'lifetime'"
    (changed)="onChanged($event)"
  />
</div>

Casos de uso reales

Selección de plan

<div class="flex flex-col gap-3">
  @for(plan of planes; track plan.id) {
  <radio-button
    [value]="plan.id"
    name="planes"
    [label]="plan.nombre"
    [checked]="selectedPlan === plan.id"
    (changed)="selectedPlan = $event"
  />
  }
</div>

Con opción desactivada

<radio-button
  value="premium"
  name="plan"
  label="Premium (próximamente)"
  [checked]="false"
  [disabled]="true"
/>

Selección de rol en formulario de usuario

<div class="flex flex-col gap-3">
  <radio-button
    value="admin"
    name="rol"
    label="Administrador"
    [checked]="rol === 'admin'"
    (changed)="rol = $event"
  />
  <radio-button
    value="editor"
    name="rol"
    label="Editor"
    [checked]="rol === 'editor'"
    (changed)="rol = $event"
  />
  <radio-button
    value="viewer"
    name="rol"
    label="Solo lectura"
    [checked]="rol === 'viewer'"
    (changed)="rol = $event"
  />
</div>
rol = 'viewer';