View transitions
You animate between UI states with the browser View Transition API. Ilha does not wrap it. Call document.startViewTransition() and update an atom inside the callback.
import { const atom: AtomFnatom } from "ilha";
export default function function Card(): ViewCard() {
const const checked: AtomHandle<boolean>checked = atom<boolean>(init: boolean | Atom<boolean> | Effect<boolean, unknown, AtomRegistry> | Stream<boolean, unknown, AtomRegistry>, options?: AtomOptions<boolean> | undefined): AtomHandle<boolean>atom(false);
const const toggle: () => voidtoggle = () => {
var document: Document**`window.document`** returns a reference to the document contained in the window.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/document)document.Document.startViewTransition(callbackOptions?: ViewTransitionUpdateCallback | StartViewTransitionOptions): ViewTransitionThe **`startViewTransition()`** method of the Document interface starts a new same-document (SPA) view transition and returns a ViewTransition object to represent it.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Document/startViewTransition)startViewTransition(() => {
const checked: AtomHandle<boolean>checked.AtomHandle<boolean>.update: (f: (current: boolean) => boolean) => voidupdate((value: booleanvalue) => !value: booleanvalue);
});
};
return (
<JSX.IntrinsicElements.button: ButtonHTMLAttributesbutton
type?: string | undefinedtype="button"
class?: Attrclass="card"
EventProps<HTMLButtonElement>.onclick?: ((event: Targeted<HTMLButtonElement, MouseEvent>) => void) | undefinedonclick={const toggle: () => voidtoggle}
style?: StyleValuestyle="view-transition-name: card"
>
{const checked: AtomHandle
() => boolean
checked() ? "Checked" : "Unchecked"}
</JSX.IntrinsicElements.button: ButtonHTMLAttributesbutton>
);
}
The callback must update state synchronously so the browser can snapshot both sides of the transition.
Animate mount and unmount
There is no transition hook for entering or leaving. You render conditionally from an atom and wrap the write in startViewTransition():
import { const atom: AtomFnatom } from "ilha";
export default function function Panel(): ViewPanel() {
const const open: AtomHandle<boolean>open = atom<boolean>(init: boolean | Atom<boolean> | Effect<boolean, unknown, AtomRegistry> | Stream<boolean, unknown, AtomRegistry>, options?: AtomOptions<boolean> | undefined): AtomHandle<boolean>atom(false);
const const toggle: () => voidtoggle = () => {
var document: Document**`window.document`** returns a reference to the document contained in the window.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/document)document.Document.startViewTransition(callbackOptions?: ViewTransitionUpdateCallback | StartViewTransitionOptions): ViewTransitionThe **`startViewTransition()`** method of the Document interface starts a new same-document (SPA) view transition and returns a ViewTransition object to represent it.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Document/startViewTransition)startViewTransition(() => {
const open: AtomHandle<boolean>open.AtomHandle<boolean>.update: (f: (current: boolean) => boolean) => voidupdate((value: booleanvalue) => !value: booleanvalue);
});
};
return (
<>
<JSX.IntrinsicElements.button: ButtonHTMLAttributesbutton type?: string | undefinedtype="button" EventProps<HTMLButtonElement>.onclick?: ((event: Targeted<HTMLButtonElement, MouseEvent>) => void) | undefinedonclick={const toggle: () => voidtoggle}>
{const open: AtomHandle
() => boolean
open() ? "Hide" : "Show"}
</JSX.IntrinsicElements.button: ButtonHTMLAttributesbutton>
{const open: AtomHandle
() => boolean
open() ? <JSX.IntrinsicElements.p: HTMLAttributes<HTMLParagraphElement>p style?: StyleValuestyle="view-transition-name: panel">Details</JSX.IntrinsicElements.p: HTMLAttributes<HTMLParagraphElement>p> : null}
</>
);
}
The old subtree snapshots out while the new one snapshots in. Keep the conditional inside the same component so one atom write drives both sides.