Sileo
v0.1.3Toast con morphing gooey y spring physics. Core vanilla, cero dependencias y toda la animación en CSS. Varios toasts forman una fila de pestañas solapadas: pasa el cursor por encima, muévete entre ellas, arrastra para descartar.
Estados
Cada estado trae su color y su icono. Si no pasas título, se usa el propio estado.
Composición
El panel acepta texto, nodos del DOM o { html }, y un botón de
acción. Sin descripción, el toast no se despliega. «Contenido animado» pasa
un nodo con un SVG y sus propios controles, y con
duration: null se queda hasta que lo descartes.
sileo.promise() muestra loading y cambia al
resolverse. Mientras espera no se despliega ni se auto-cierra.
La fila de tabs
Solo la tab enfocada muestra su mensaje; las demás quedan como círculos con su icono. Se ven tres: la cuarta y la quinta esperan detrás.
Cada posición es un stack independiente, con su propia fila y su propio foco.
Uso
El core es vanilla: en cualquier framework se monta el toaster una vez y se
llama a sileo.*() desde donde sea.
import { sileo, createToaster } from "sileojs";
import "sileojs/styles.css";
// una sola vez, al arrancar la app
createToaster({ position: "top-right", theme: "system" });
// y en cualquier momento, desde donde sea:
// configure({ theme: "dark", styles: { toast: "mi-clase" } });
sileo.success({
title: "Guardado",
description: "Tus cambios se sincronizaron.",
});
// main.js — el plugin monta el toaster y deja $sileo global
import { createApp } from "vue";
import { SileoPlugin } from "sileojs/vue";
import "sileojs/styles.css";
import App from "./App.vue";
createApp(App)
.use(SileoPlugin, {
position: "top-right",
theme: "light",
options: { roundness: 14, styles: { toast: "top-90" } },
})
.mount("#app");
<!-- cualquier componente: sin imports, sin <SileoToaster /> -->
<template>
<button @click="$sileo.success({ title: 'Guardado' })">
Guardar
</button>
</template>
<!-- y en <script setup>, donde no llega $sileo: -->
<script setup>
import { sileo } from "sileojs"; // o inject("sileo")
</script>
<script setup>
import { SileoToaster, useSileoConfig } from "sileojs/vue";
import { sileo } from "sileojs";
import "sileojs/styles.css";
// config reactiva: tema, posicion y estilos en caliente
const cfg = useSileoConfig();
</script>
<template>
<SileoToaster position="top-right" theme="light" :styles="{ toast: 'top-90' }" />
<button @click="sileo.success({ title: 'Guardado' })">Guardar</button>
<button @click="cfg.theme = 'dark'">Tema oscuro</button>
<button @click="cfg.position = 'bottom-center'">Abajo</button>
</template>
import { useEffect } from "react";
import { sileo, createToaster } from "sileojs";
import "sileojs/styles.css";
export default function App() {
useEffect(() => {
const toaster = createToaster({ position: "top-right" });
return () => toaster.destroy();
}, []);
return (
<button onClick={() => sileo.success({ title: "Guardado" })}>
Guardar
</button>
);
}
<script>
import { onMount } from "svelte";
import { sileo, createToaster } from "sileojs";
import "sileojs/styles.css";
onMount(() => {
const toaster = createToaster({ position: "top-right" });
return () => toaster.destroy();
});
</script>
<button on:click={() => sileo.success({ title: "Guardado" })}>
Guardar
</button>
// angular.json -> styles: ["node_modules/sileojs/src/sileo.css"]
import { Component, OnInit, OnDestroy } from "@angular/core";
import { sileo, createToaster } from "sileojs";
@Component({
selector: "app-root",
template: `<button (click)="guardar()">Guardar</button>`,
})
export class AppComponent implements OnInit, OnDestroy {
private toaster?: ReturnType<typeof createToaster>;
ngOnInit() {
this.toaster = createToaster({ position: "top-right" });
}
ngOnDestroy() {
this.toaster?.destroy();
}
guardar() {
sileo.success({ title: "Guardado" });
}
}
La API completa, las variables CSS y el resto de opciones están en la documentación.