SolandaUI
Biblioteca de Web Components sin dependencias de framework. El nombre viene del Barrio Solanda (Quito, Ecuador): un proyecto planificado en 1984 que creció desbordando sus propios límites — como todo buen sistema de diseño que termina siendo adoptado, extendido y llevado más lejos de lo que sus autores previeron.
La paleta de colores se extrae de la materialidad del barrio: ladrillo terracota, fachadas amarillas, concreto andino, muros encalados y el cielo azulado de las noches quiteñas. Compatible con cualquier stack: HTML puro, Vue, React, Svelte, Astro.
instalación
Importa los tokens CSS globalmente y los componentes que necesites como módulos ES.
npm install @federa/solanda-uiimport '@federa/solanda-ui/css'import '@federa/solanda-ui/sl-button'
import '@federa/solanda-ui/sl-input'
// … importa solo los que uses<sl-button variant="default">Hola mundo</sl-button>import '@federa/solanda-ui/css/themes/social-neutral'
import '@federa/solanda-ui/css/themes/catppuccin'
import '@federa/solanda-ui/css/themes/gruvbox'
import '@federa/solanda-ui/css/themes/tokyo-night'
import '@federa/solanda-ui/css/themes/nord'import { ThemeManager } from '@federa/solanda-ui/theme'
const theme = ThemeManager.init({
storageKey: 'my-app-theme',
defaultPreset: 'solanda', // 'solanda' | 'social-neutral' | 'catppuccin' | 'gruvbox' | 'tokyo-night' | 'nord'
defaultTheme: 'system', // 'light' | 'dark' | 'system'
autoApply: true
})
theme.setTheme('dark')
theme.setPreset('catppuccin')Input sl-input
Campo de texto de una línea. Envuelve el elemento nativo <input> con estilos de tokens y eventos normalizados.
Valor: —
<sl-input type="email" placeholder="tu@email.com" required></sl-input>
<script>
document.querySelector('sl-input').addEventListener('input', (e) => {
console.log(e.detail.value)
})
</script>| Atributo | Tipo | Por defecto | Descripción |
|---|---|---|---|
type | string | text | Tipo de input nativo: text · email · password · number · file · etc. |
value | string | — | Valor del campo |
placeholder | string | — | Texto placeholder |
disabled | boolean | false | Deshabilita el input |
readonly | boolean | false | Solo lectura |
required | boolean | false | Campo obligatorio |
name | string | — | Nombre para formularios nativos |
Textarea sl-textarea
Campo de texto multilínea. Misma API que sl-input con soporte para filas y redimensionado.
<sl-textarea
placeholder="Descripción del producto…"
rows="5"
name="description"
></sl-textarea>| Atributo | Tipo | Por defecto | Descripción |
|---|---|---|---|
value | string | — | Contenido del textarea |
placeholder | string | — | Texto placeholder |
rows | number | 3 | Número de filas visibles |
disabled | boolean | false | Deshabilita el campo |
readonly | boolean | false | Solo lectura |
name | string | — | Nombre para formularios |
Select sl-select
Selector desplegable. Las opciones se pasan como JSON en el atributo options.
Seleccionado: —
<sl-select
placeholder="Elige una opción…"
options='[
{ "value": "a", "label": "Opción A" },
{ "value": "b", "label": "Opción B" },
{ "value": "c", "label": "Deshabilitada", "disabled": true }
]'
></sl-select>
<script>
document.querySelector('sl-select').addEventListener('change', (e) => {
console.log(e.detail.value)
})
</script>| Atributo | Tipo | Por defecto | Descripción |
|---|---|---|---|
options | JSON | [] | Array de { value, label, disabled? } |
value | string | — | Valor seleccionado |
placeholder | string | — | Texto cuando no hay selección |
disabled | boolean | false | Deshabilita el select |
name | string | — | Nombre para formularios |
Label sl-label
Etiqueta semántica para campos de formulario. Añade indicador visual de campo requerido.
<sl-label for="mi-input" required>Email *</sl-label>
<sl-input id="mi-input" type="email" required></sl-input>| Atributo | Tipo | Por defecto | Descripción |
|---|---|---|---|
for | string | — | ID del campo asociado |
required | boolean | false | Muestra indicador de obligatorio |
Checkbox sl-checkbox
Casilla de selección booleana con etiqueta integrada.
Términos: sin marcar
<sl-checkbox label="Acepto los términos" name="terms"></sl-checkbox>
<script>
document.querySelector('sl-checkbox').addEventListener('change', (e) => {
console.log(e.detail.checked) // boolean
})
</script>| Atributo | Tipo | Por defecto | Descripción |
|---|---|---|---|
label | string | — | Texto de la etiqueta |
checked | boolean | false | Estado marcado |
disabled | boolean | false | Deshabilita el checkbox |
value | string | — | Valor para formularios |
name | string | — | Nombre para formularios |
Switch sl-switch
Interruptor de activación/desactivación. Alternativa visual a checkbox para preferencias.
MD switch: on
<sl-switch size="md" name="notifications"></sl-switch>
<script>
document.querySelector('sl-switch').addEventListener('change', (e) => {
console.log(e.detail.checked) // boolean
})
</script>| Atributo | Tipo | Por defecto | Descripción |
|---|---|---|---|
size | string | md | sm · md · lg |
checked | boolean | false | Estado activo |
disabled | boolean | false | Deshabilita el switch |
name | string | — | Nombre para formularios |
Badge sl-badge
Etiqueta visual compacta para estados, categorías o contadores.
<sl-badge variant="success">Activo</sl-badge>
<sl-badge variant="info" size="sm">3</sl-badge>| Atributo | Tipo | Por defecto | Descripción |
|---|---|---|---|
variant | string | default | default · secondary · success · error · warning · info · outline |
size | string | md | sm · md · lg |
Avatar sl-avatar
Representación visual de un usuario. Muestra imagen cuando está disponible, cae en fallback con iniciales si falla.
<sl-avatar size="md">
<sl-avatar-image src="/foto.jpg" alt="Nombre"></sl-avatar-image>
<sl-avatar-fallback>NM</sl-avatar-fallback>
</sl-avatar>| Elemento | Atributo | Descripción |
|---|---|---|
sl-avatar | size | sm · md · lg · xl |
sl-avatar-image | src | URL de la imagen. Si falla carga, muestra el fallback. |
sl-avatar-image | alt | Texto alternativo accesible |
sl-avatar-fallback | (slot) | Contenido mostrado si la imagen no carga. Generalmente 1–2 iniciales. |
Card sl-card
Contenedor de superficie con bordes y padding. Soporta slots para estructura compleja.
Card básica con contenido en el slot por defecto.
Slot header + atributo shadow.
Slot media + padding="none"
Todos los slots disponibles.
<sl-card shadow>
<div slot="header">Título</div>
<img slot="media" src="..." alt="...">
<p>Contenido principal</p>
<div slot="actions">
<sl-button size="sm">Acción</sl-button>
</div>
<div slot="footer">
<sl-badge>Etiqueta</sl-badge>
</div>
</sl-card>| Nombre | Tipo | Descripción |
|---|---|---|
shadow | atributo | Añade sombra elevada |
padding | atributo | none · sm · md (por defecto) |
header | slot | Zona de cabecera con borde inferior |
media | slot | Imagen o contenido visual al inicio |
| (default) | slot | Contenido principal |
actions | slot | Botones de acción alineados a la derecha |
footer | slot | Zona de pie con borde superior |
Icon sl-icon
Contenedor de iconos SVG inline con tamaños normalizados y semántica de accesibilidad correcta.
<!-- Con label = accesible (role="img") -->
<sl-icon size="md" label="Cerrar">
<svg viewBox="0 0 24 24" fill="currentColor">...</svg>
</sl-icon>
<!-- Sin label = decorativo (aria-hidden="true") -->
<sl-icon size="md">
<svg viewBox="0 0 24 24" fill="currentColor">...</svg>
</sl-icon>
<!-- Tamaño personalizado -->
<sl-icon style="--sl-icon-size: 3rem">...</sl-icon>| Atributo | Tipo | Por defecto | Descripción |
|---|---|---|---|
size | string | md | xs · sm · md · lg · xl |
label | string | — | Descripción accesible. Si no se especifica, el icono se oculta a lectores de pantalla. |
--sl-icon-size | CSS prop | — | Anula el tamaño predefinido con cualquier valor CSS |
Tabs sl-tabs sl-tab sl-tab-content
Sistema de pestañas compuesto por tres elementos coordinados. Las pestañas y el contenido se sincronizan por nombre.
Configuración de cuenta: nombre, email, foto de perfil.
Contraseña, autenticación en dos pasos, sesiones activas.
Tienes 3 notificaciones sin leer.
<sl-tabs>
<sl-tab name="a" active>Primera</sl-tab>
<sl-tab name="b">Segunda</sl-tab>
</sl-tabs>
<sl-tab-content name="a" active>Contenido A</sl-tab-content>
<sl-tab-content name="b">Contenido B</sl-tab-content>
<script>
document.querySelector('sl-tabs').addEventListener('sl-tab-change', (e) => {
console.log(e.detail.tab) // nombre de la pestaña activa
})
</script>| Elemento | Atributo | Descripción |
|---|---|---|
sl-tab | name | Identificador único de la pestaña |
sl-tab | active | Marca la pestaña como activa por defecto |
sl-tab | disabled | Deshabilita la pestaña |
sl-tab-content | name | Debe coincidir con el name del tab |
sl-tab-content | active | Muestra el contenido por defecto |
Step Indicator sl-step-indicator
Indicador de progreso de pasos para flujos multi-etapa (wizard). Soporta variante horizontal y vertical.
<sl-step-indicator
steps='[
{ "label": "Cuenta" },
{ "label": "Personal" },
{ "label": "Revisión" },
{ "label": "Listo" }
]'
current="0"
variant="horizontal"
></sl-step-indicator>
<script>
document.querySelector('sl-step-indicator').addEventListener('step-click', (e) => {
console.log(e.detail.index) // índice del paso clicado
})
</script>| Atributo | Tipo | Por defecto | Descripción |
|---|---|---|---|
steps | JSON | [] | Array de { label: string, description?: string } |
current | number | 0 | Índice del paso activo (base 0). Los anteriores se marcan como completados. |
variant | string | horizontal | horizontal · vertical |
Topbar sl-topbar
Barra de navegación superior. Soporta tres variantes: fija (pinned), flotante con márgenes (floating), o inline sin posición fija (flat).
<!-- Topbar fija (default) -->
<sl-topbar variant="pinned">
<a slot="start" href="/">Logo</a>
<nav slot="center">...</nav>
<div slot="end">Acciones</div>
</sl-topbar>
<!-- Topbar flotante -->
<sl-topbar variant="floating">...</sl-topbar>
<!-- Topbar inline -->
<sl-topbar variant="flat">...</sl-topbar>| Atributo | Tipo | Por defecto | Descripción |
|---|---|---|---|
variant | string | pinned | pinned · floating · flat |
height | string | 3.5rem | Altura de la topbar |
Dialog sl-dialog
Modal accesible construido sobre el elemento nativo <dialog>. Se abre y cierra con métodos JS nativos.
Acción rápida
Este es un dialog pequeño para acciones simples.
Editar perfil
Términos y condiciones
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. Donec in efficitur leo, in commodo orci. Morbi commodo dui vel risus dignissim, vel commodo erat maximus.
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper.
¿Eliminar elemento?
Esta acción no se puede deshacer. El elemento será eliminado permanentemente.
<sl-button onclick="document.getElementById('mi-dialog').showModal()">Abrir</sl-button>
<sl-dialog id="mi-dialog" size="md">
<h2 slot="title">Título del dialog</h2>
<p>Contenido del dialog.</p>
<div slot="footer">
<sl-button variant="ghost" onclick="this.closest('sl-dialog').close()">Cancelar</sl-button>
<sl-button onclick="this.closest('sl-dialog').close()">Confirmar</sl-button>
</div>
</sl-dialog>| Nombre | Tipo | Descripción |
|---|---|---|
size | atributo | sm · md · lg |
title | slot | Cabecera del dialog con botón de cierre |
| (default) | slot | Contenido del body |
footer | slot | Pie del dialog, típicamente botones de acción |
showModal() | método | Abre el dialog (nativo) |
close() | método | Cierra el dialog (nativo) |
Tooltip sl-tooltip
Globo informativo que aparece al pasar el cursor. Se posiciona automáticamente alrededor del trigger.
<sl-tooltip content="Más información" placement="top">
<sl-button variant="outline">Hover</sl-button>
</sl-tooltip>| Atributo | Tipo | Por defecto | Descripción |
|---|---|---|---|
content | string | — | Texto del tooltip |
placement | string | top | top · bottom · left · right |
delay | number | 200 | Retardo de aparición en ms |
Dropdown Menu sl-dropdown-menu
Menú contextual desplegable compuesto por trigger, contenedor e items individuales.
Seleccionado: —
<sl-dropdown-menu>
<sl-dropdown-menu-trigger>
<sl-button variant="outline">Menú ▾</sl-button>
</sl-dropdown-menu-trigger>
<sl-dropdown-menu-content>
<sl-dropdown-menu-item value="edit" label="Editar"></sl-dropdown-menu-item>
<sl-dropdown-menu-separator></sl-dropdown-menu-separator>
<sl-dropdown-menu-item value="delete" label="Eliminar"></sl-dropdown-menu-item>
</sl-dropdown-menu-content>
</sl-dropdown-menu>
<script>
document.querySelector('sl-dropdown-menu').addEventListener('select', (e) => {
console.log(e.detail.value)
})
</script>| Atributo | Tipo | Descripción |
|---|---|---|
value | string | Valor emitido en el evento select |
label | string | Texto visible del ítem |
disabled | boolean | Deshabilita el ítem |
Popover sl-popover
Panel flotante de contenido libre anclado a un trigger. A diferencia del tooltip, puede contener cualquier HTML.
Clic fuera para cerrar.
Popover anclado arriba.
Popover anclado a la derecha.
<sl-popover placement="bottom">
<sl-popover-trigger>
<sl-button>Abrir</sl-button>
</sl-popover-trigger>
<sl-popover-content>
<p>Cualquier contenido HTML aquí.</p>
</sl-popover-content>
</sl-popover>| Atributo (en sl-popover) | Tipo | Por defecto | Descripción |
|---|---|---|---|
placement | string | bottom | top · bottom · left · right |
Toast sl-toast
Notificaciones temporales imperativas. Se invocan con la función showToast(), no con HTML declarativo.
import { showToast } from '@federa/solanda-ui/sl-toast'
// Toast básico
showToast({ message: '¡Guardado correctamente!' })
// Con opciones
showToast({
message: 'El archivo se ha procesado.',
variant: 'success', // 'default' | 'success' | 'error' | 'warning' | 'info'
duration: 4000, // ms. 0 = no se cierra automáticamente
dismissible: false // muestra botón de cerrar
})
// Toast persistente con dismiss
showToast({
message: 'Acción requerida.',
variant: 'warning',
duration: 0,
dismissible: true
})| Opción | Tipo | Por defecto | Descripción |
|---|---|---|---|
message | string | — | Texto del toast (requerido) |
variant | string | default | default · success · error · warning · info |
duration | number | 3000 | Duración en ms. 0 = permanente hasta dismiss. |
dismissible | boolean | false | Muestra botón de cierre manual |
Alert sl-alert
Mensaje contextual con variantes visuales. Soporta cierre programático y slots para contenido.
<sl-alert variant="success" title="Guardado" dismissible>
Los cambios se han guardado.
</sl-alert>
<!-- Cerrar programáticamente -->
<script>
document.querySelector('sl-alert').addEventListener('dismiss', (e) => {
console.log('Alert cerrado')
})
</script>| Atributo | Tipo | Por defecto | Descripción |
|---|---|---|---|
variant | string | default | default · info · success · warning · error |
dismissible | boolean | false | Muestra botón de cierre |
title | string | — | Título opcional del alert |
Spinner sl-spinner
Indicador de carga animado con tamaños predefinidos. Accesible vía role="status" y aria-label.
<sl-spinner size="md"></sl-spinner>
<sl-spinner size="sm" label="Cargando..."></sl-spinner>| Atributo | Tipo | Por defecto | Descripción |
|---|---|---|---|
size | string | md | sm · md · lg · xl |
label | string | Loading | Texto accesible (aria-label) |
Skeleton sl-skeleton
Placeholder animado para contenido en carga. Variantes de forma y dimensiones personalizables.
<sl-skeleton variant="text"></sl-skeleton>
<sl-skeleton variant="rectangle" width="200px" height="100px"></sl-skeleton>
<sl-skeleton variant="circle" width="48px" height="48px"></sl-skeleton>| Atributo | Tipo | Por defecto | Descripción |
|---|---|---|---|
variant | string | text | text · circle · rectangle · avatar |
width | string | — | Ancho CSS (ej: 100px, 50%) |
height | string | — | Alto CSS (ej: 1rem, 20px) |
Pagination sl-pagination
Navegación de páginas con soporte para elipsis, tamaños y eventos de cambio.
Página actual: 3
<sl-pagination total="20" current="5" size="md"></sl-pagination>
<script>
document.querySelector('sl-pagination').addEventListener('sl-pagination-change', (e) => {
console.log('Página:', e.detail.page)
})
</script>| Atributo | Tipo | Por defecto | Descripción |
|---|---|---|---|
total | number | 1 | Número total de páginas |
current | number | 1 | Página activa actual |
size | string | md | sm · md · lg |
show-ellipsis | boolean | true | Muestra elipsis cuando hay muchas páginas |
sibling-count | number | 1 | Páginas visibles a cada lado de la actual |
Accordion sl-accordion sl-accordion-item
Secciones colapsables usando <details>/<summary> nativos. Soporta modo exclusivo y flush.
Sistema de componentes web sin dependencias, construido con TypeScript y Shadow DOM.
Importa sl-accordion desde npm o CDN y úsalo como HTML nativo.
Este panel no se puede abrir.
Perfecto para integrar en cards o paneles.
Con multiple puedes abrir varios paneles simultáneamente.
<sl-accordion>
<sl-accordion-item open>
<span slot="title">Título</span>
<p>Contenido colapsable.</p>
</sl-accordion-item>
</sl-accordion>| Elemento | Atributo | Descripción |
|---|---|---|
sl-accordion | multiple | Permite abrir varios ítems a la vez |
sl-accordion | flush | none · start · end · full |
sl-accordion-item | open | Ítem abierto inicialmente |
sl-accordion-item | disabled | Deshabilita el ítem |
List Group sl-list-group sl-list-group-item
Lista flexible con variantes visuales, items activos/deshabilitados y modo horizontal.
<sl-list-group>
<sl-list-group-item active>Activo</sl-list-group-item>
<sl-list-group-item>Normal</sl-list-group-item>
<sl-list-group-item disabled>Deshabilitado</sl-list-group-item>
</sl-list-group>| Elemento | Atributo | Descripción |
|---|---|---|
sl-list-group | variant | default · flush · horizontal |
sl-list-group | size | sm · md · lg |
sl-list-group | clickable | Hace los items clickables con selección automática |
sl-list-group-item | active | Marca el ítem como activo |
sl-list-group-item | disabled | Deshabilita el ítem |
sl-list-group-item | href | Convierte el ítem en un enlace |
Input Group sl-input-group sl-input-group-addon
Agrupa inputs con texto, iconos o botones a los lados.
<sl-input-group>
<span slot="prepend">$</span>
<sl-input placeholder="Amount"></sl-input>
</sl-input-group>| Elemento | Atributo/Slot | Descripción |
|---|---|---|
sl-input-group | size | sm · md · lg |
sl-input-group | has-validation | Activa borde de focus para validación |
sl-input-group | slot="prepend" | Contenido al inicio del grupo |
sl-input-group | slot="append" | Contenido al final del grupo |
sl-input-group-addon | text | Texto del addon (alternativa al slot) |
Offcanvas sl-offcanvas
Panel deslizante desde cualquier borde con backdrop, focus trap y keyboard dismiss.
Panel desde la izquierda.
Panel desde la derecha.
Panel desde arriba.
Panel desde abajo.
<sl-offcanvas id="menu" placement="start" size="md" backdrop>
<span slot="title">Título</span>
<p>Contenido...</p>
</sl-offcanvas>
<script>
const offcanvas = document.getElementById('menu')
offcanvas.show() // Mostrar
offcanvas.hide() // Ocultar
offcanvas.toggle() // Alternar
</script>| Atributo | Tipo | Por defecto | Descripción |
|---|---|---|---|
placement | string | end | start · end · top · bottom |
size | string | md | sm · md · lg · full |
closable | boolean | true | Permite cerrar con Escape, botón o backdrop |
backdrop | boolean | false | Muestra overlay semitransparente |
| Evento | Descripción |
|---|---|
sl-offcanvas-open | Se dispara cuando el offcanvas se abre |
sl-offcanvas-close | Se dispara cuando el offcanvas se cierra |
Section Header sl-section-header
Cabecera de sección con eyebrow, título y descripción. Componente de composición para landing pages y documentación.
Una descripción opcional que contextualiza la sección.
<sl-section-header eyebrow="Overview">
<span slot="title">Section Title</span>
<p>Description text goes here.</p>
</sl-section-header>| Nombre | Tipo | Descripción |
|---|---|---|
eyebrow | atributo | Texto pequeño sobre el título |
title | slot | Título de la sección |
| (default) | slot | Descripción o contenido adicional |
Feature Card sl-feature-card
Tarjeta promocional para listar características con icono, título y descripción.
Construido para rendimiento con bundle mínimo.
ARIA attributes y navegación por teclado incluidos.
<sl-feature-card title="Lightning Fast">
<sl-icon slot="icon" size="lg">⚡</sl-icon>
<p>Optimized for performance.</p>
</sl-feature-card>| Nombre | Tipo | Descripción |
|---|---|---|
title | atributo | Título de la feature card |
icon | slot | Icono visible en la parte superior |
| (default) | slot | Descripción o contenido |
Showcase Card sl-showcase-card
Contenedor tipo ventana con barra de título (estilo macOS) para mostrar demos y previsualizaciones.
Contenido de ejemplo dentro del showcase card.
<sl-showcase-card title="Preview">
<p>Demo content here.</p>
</sl-showcase-card>| Nombre | Tipo | Descripción |
|---|---|---|
title | atributo | Título mostrado en la barra del encabezado |
| (default) | slot | Contenido del cuerpo del showcase |
Doc Block sl-doc-block
Bloque de contenido para páginas de documentación con título y área de contenido.
Este es el contenido dentro del doc-block.
<sl-doc-block title="Installation">
<p>Install with npm:</p>
<code>npm install @federa/solanda-ui</code>
</sl-doc-block>| Atributo | Tipo | Por defecto | Descripción |
|---|---|---|---|
title | string | '' | Título del bloque documental |
| (default) | slot | — | Contenido del bloque |