diff --git a/apps/docs/src/routes/docs/components/toast-sileo.tsx b/apps/docs/src/routes/docs/components/toast-sileo.tsx
index 52fe85b..b71bb8c 100644
--- a/apps/docs/src/routes/docs/components/toast-sileo.tsx
+++ b/apps/docs/src/routes/docs/components/toast-sileo.tsx
@@ -1,59 +1,69 @@
import { Button } from "@solidcn/core";
-import { sileo } from "@solidcn/toast";
+import { SileoToaster, sileo } from "@solidcn/toast";
import { DocPage } from "../../../components/ui/DocPage.js";
function SileoDemo() {
return (
-
-
-
-
-
-
-
-
+ <>
+
+
+
+
+
+
+
+
+
+ >
);
}
@@ -66,87 +76,145 @@ export default function ToastSileoPage() {
phase="Toast"
componentName="toast"
manualInstall="npm install @solidcn/toast"
- usage={`import { sileo } from "@solidcn/toast";
+ usage={`import { SileoToaster, sileo } from "@solidcn/toast";
+
+// Mount once in this page/layout
+
// Trigger a physics-based toast:
-sileo.success({ title: "Saved!" });
-sileo.error({ title: "Failed." });
-sileo.warning({ title: "Heads up." });
-sileo.info({ title: "FYI." });
+sileo.success({ title: "Changes saved" });
+
+sileo.error({
+ title: "Something went wrong",
+ description: "Please try again later.",
+ duration: 6000,
+});
+
+sileo.warning({ title: "Storage almost full", duration: 6000 });
+
+sileo.info({ title: "New update available", duration: 6000 });
// With options:
sileo.success({
title: "Event created",
description: "Sunday, December 03 at 9:00 AM",
preset: "glass",
+ duration: 6000,
animation: "spring",
});`}
examples={[
{
title: "Variants",
description:
- 'Click to trigger physics-based toasts. Requires in your layout.',
+ "Click any button to trigger Sileo toasts on this page, including description cards. Each toast stays open briefly before auto-closing.",
preview: ,
- code: `import { sileo } from "@solidcn/toast"
-import { Button } from "~/components/ui/button"
+ code: `import { SileoToaster, sileo } from "@solidcn/toast"
+import { Button } from "@solidcn/core"
export function ToastSileoVariants() {
return (
-
-
-
-
-
-
-
-
+ <>
+
+
+
+
+
+
+
+
+
+ >
)
}`,
},
]}
notes={
+
+
Standalone toast item
+
+ Use SileoToast when you want to
+ render a single toast card from your own state instead of the global store.
+
+
+
{`import { SileoToast } from "@solidcn/toast";
+
+ {
+ // remove the toast from your own state
+ }}
+/>
+`}
+
+
+
Setup
- Add {''}{" "}
- once in your app root:
+ Add{" "}
+ {''}{" "}
+ where you want Sileo toasts to appear:
-
{`import { Toaster } from "@solidcn/toast";
+ {`import { SileoToaster } from "@solidcn/toast";
-// In your App component:
-`}
+// In your page/layout:
+`}
diff --git a/biome.json b/biome.json
index 6ec08c4..89e8ed4 100644
--- a/biome.json
+++ b/biome.json
@@ -44,6 +44,7 @@
".pnpm-store",
"dist",
".turbo",
+ "packages/mcp-cloudflare/.wrangler",
"apps/docs/.solid",
"apps/docs/.output",
"apps/docs/.vinxi",
diff --git a/packages/toast/README.md b/packages/toast/README.md
index bdd8ab0..b8cab66 100644
--- a/packages/toast/README.md
+++ b/packages/toast/README.md
@@ -68,6 +68,24 @@ export default function App() {
}
```
+If you need to render a single toast card manually, you can also use the standalone `SileoToast` component:
+
+```tsx
+import { SileoToast } from "@solidcn/toast";
+
+
{
+ /* remove the toast from your own state */
+ }}
+/>
+```
+
```ts
// Sileo toast types
sileo.success({ title: "Success", description? });
@@ -96,7 +114,7 @@ toast.promise(submitForm(), {
```tsx
interface StandardToasterProps {
- position?: "top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right";
+ position?: "top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "top-center";
mode?: "standard";
theme?: "light" | "dark" | "system";
richColors?: boolean;
@@ -111,7 +129,7 @@ interface StandardToasterProps {
| Prop | Type | Default | Description |
|---|---|---|---|
-| `position` | See above | `"bottom-right"` | Toast placement |
+| `position` | See above | `"top-center"` | Toast placement |
| `theme` | `"light" \| "dark" \| "system"` | `"system"` | Toast theme override |
| `richColors` | `boolean` | `false` | When enabled, toast color reflects type (success=green, error=red, etc.) |
| `closeButton` | `boolean` | `false` | Show dismiss button on each toast |
@@ -141,7 +159,7 @@ interface SileoToasterProps {
### ToastPosition (Sileo)
-`"top-left" | "top-right" | "bottom-left" | "bottom-right" | "center"`
+`"top-left" | "top-right" | "bottom-left" | "top-center" | "center"`
## Toast Options
@@ -217,13 +235,14 @@ sileo.dismiss();
export { Toaster, toast, sileo };
// Mode-specific
-export { StandardToaster, SileoToaster };
+export { StandardToaster, SileoToaster, SileoToast };
// Types
export type {
ToasterProps,
StandardToasterProps,
SileoToasterProps,
+ SileoToastProps,
StandardToastOptions,
SileoToastOptions,
SileoPreset,
diff --git a/packages/toast/src/index.tsx b/packages/toast/src/index.tsx
index 3404d89..ef70680 100644
--- a/packages/toast/src/index.tsx
+++ b/packages/toast/src/index.tsx
@@ -16,6 +16,7 @@ export { toast } from "./standard/store.js";
export { sileo } from "./sileo/store.js";
export { StandardToaster } from "./standard/toaster.js";
export { SileoToaster } from "./sileo/toaster.js";
+export { SileoToast } from "./sileo/sileo.js";
// Types
export type {
@@ -24,6 +25,7 @@ export type {
SileoToasterProps,
StandardToastOptions,
SileoToastOptions,
+ SileoToastProps,
SileoPreset,
SileoStyles,
ToastPosition,
diff --git a/packages/toast/src/sileo/index.ts b/packages/toast/src/sileo/index.ts
index 714f416..1765245 100644
--- a/packages/toast/src/sileo/index.ts
+++ b/packages/toast/src/sileo/index.ts
@@ -1,5 +1,5 @@
export { sileo, sileoStore } from "./store.js";
export { SileoToaster } from "./toaster.js";
-export { SileoItem } from "./sileo.js";
+export { SileoToast } from "./sileo.js";
export { SILEO_PRESETS, resolveStyles } from "./presets.js";
export { SPRING_CONFIGS, springTick, isSettled, getAnimationClass } from "./animations.js";
diff --git a/packages/toast/src/sileo/sileo.tsx b/packages/toast/src/sileo/sileo.tsx
index c374488..97664a0 100644
--- a/packages/toast/src/sileo/sileo.tsx
+++ b/packages/toast/src/sileo/sileo.tsx
@@ -1,5 +1,5 @@
import { type Component, Show, createEffect, createSignal, onCleanup, onMount } from "solid-js";
-import type { SileoPreset, SileoToastItem, ToastAnimation } from "../types.js";
+import type { SileoToastProps, ToastAnimation } from "../types.js";
import { SPRING_CONFIGS, isSettled, springTick } from "./animations.js";
import { resolveStyles } from "./presets.js";
import { sileo } from "./store.js";
@@ -9,14 +9,10 @@ function prefersReducedMotion(): boolean {
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
}
-interface SileoItemProps {
- toast: SileoToastItem;
- preset: SileoPreset;
- globalAnimation: ToastAnimation;
-}
+export const SileoToast: Component = (props) => {
+ const getStartOffset = () => (props.position?.startsWith("top") ? -40 : 40);
-export const SileoItem: Component = (props) => {
- const [translateY, setTranslateY] = createSignal(40);
+ const [translateY, setTranslateY] = createSignal(getStartOffset());
const [opacity, setOpacity] = createSignal(0);
const [scale, setScale] = createSignal(0.92);
@@ -24,15 +20,21 @@ export const SileoItem: Component = (props) => {
let timer: ReturnType | undefined;
const styles = () =>
- resolveStyles(props.toast.preset ?? props.preset, props.toast.type, props.toast.styles);
+ resolveStyles(
+ props.toast.preset ?? props.preset ?? "default",
+ props.toast.type,
+ props.toast.styles,
+ );
const animation = () => {
// prefers-reduced-motion: auto-fallback ke "none"
if (prefersReducedMotion()) return "none" as ToastAnimation;
- return props.toast.animation ?? props.globalAnimation;
+ return props.toast.animation ?? props.animation ?? "spring";
};
onMount(() => {
+ const startOffset = getStartOffset();
+
if (animation() === "none") {
setTranslateY(0);
setOpacity(1);
@@ -42,6 +44,7 @@ export const SileoItem: Component = (props) => {
// fade: CSS opacity transition via rAF double-frame trick
if (animation() === "fade") {
+ setTranslateY(startOffset);
requestAnimationFrame(() => {
requestAnimationFrame(() => {
setOpacity(1);
@@ -54,6 +57,7 @@ export const SileoItem: Component = (props) => {
// slide: CSS translateY transition via rAF double-frame trick
if (animation() === "slide") {
+ setTranslateY(startOffset);
requestAnimationFrame(() => {
requestAnimationFrame(() => {
setTranslateY(0);
@@ -66,7 +70,7 @@ export const SileoItem: Component = (props) => {
const config = SPRING_CONFIGS[animation()] ??
SPRING_CONFIGS.spring ?? { stiffness: 280, damping: 20, mass: 1 };
- let yState = { value: 40, velocity: 0 };
+ let yState = { value: startOffset, velocity: 0 };
let opState = { value: 0, velocity: 0 };
let scaleState = { value: 0.92, velocity: 0 };
let lastTime: number | null = null;
@@ -103,8 +107,12 @@ export const SileoItem: Component = (props) => {
const ms = duration ?? 4000;
timer = setTimeout(() => {
- props.toast.onDismiss?.(props.toast.id);
- sileo.dismiss(props.toast.id);
+ if (props.onDismiss) {
+ props.onDismiss(props.toast.id);
+ } else {
+ props.toast.onDismiss?.(props.toast.id);
+ sileo.dismiss(props.toast.id);
+ }
}, ms);
});
@@ -126,6 +134,7 @@ export const SileoItem: Component = (props) => {
= (props) => {
"backdrop-filter": isGlass() ? "blur(12px)" : undefined,
"-webkit-backdrop-filter": isGlass() ? "blur(12px)" : undefined,
}}
- class="relative flex w-full items-start gap-3 rounded-2xl border p-4 shadow-lg"
>
@@ -173,7 +181,14 @@ export const SileoItem: Component = (props) => {
type="button"
style={{ color: styles().descriptionColor }}
class="shrink-0 rounded-full p-0.5 opacity-60 hover:opacity-100 transition-opacity focus:outline-none focus:ring-2"
- onClick={() => sileo.dismiss(props.toast.id)}
+ onClick={() => {
+ if (props.onDismiss) {
+ props.onDismiss(props.toast.id);
+ } else {
+ props.toast.onDismiss?.(props.toast.id);
+ sileo.dismiss(props.toast.id);
+ }
+ }}
aria-label="Dismiss"
>