<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Riposte</title>
  <link href="https://riposte.edadma.dev/feed.xml" rel="self"/>
  <link href="https://riposte.edadma.dev/"/>
  <id>https://riposte.edadma.dev/feed.xml</id>
  <updated>2026-05-31T15:40:05.566249418Z</updated>
  <author><name>Ed Maxedon</name></author>
  <entry>
    <title>State with Atoms</title>
    <link href="https://riposte.edadma.dev/guide/state/"/>
    <id>https://riposte.edadma.dev/guide/state/</id>
    <updated>2026-05-31T15:40:05.566249418Z</updated>
    <summary>useState is enough for state owned by one component. When state needs to be shared across components that aren’t in a simple parent-child line — and you’d rather not thread…</summary>
    <content type="html">&lt;p&gt;&lt;code&gt;useState&lt;/code&gt; is enough for state owned by one component. When state needs to be &lt;strong&gt;shared&lt;/strong&gt;
across components that aren’t in a simple parent-child line — and you’d rather not thread
props through every layer between them — reach for &lt;strong&gt;riposte-atoms&lt;/strong&gt;: a Jotai-inspired
model of small, independent, identity-based units of state.&lt;/p&gt;
&lt;p&gt;Add the dependency (it pulls in the core transitively):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;libraryDependencies &lt;span class=&quot;hl-keyword&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;io.github.edadma&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;%%%&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;riposte-atoms&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;0.0.2&amp;quot;&lt;/span&gt;

&lt;span class=&quot;hl-keyword&quot;&gt;import&lt;/span&gt; io&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;github&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;edadma&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;riposte&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;atoms&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;*&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;atoms&quot;&gt;Atoms&lt;/h2&gt;
&lt;p&gt;An atom is a &lt;em&gt;handle&lt;/em&gt; to a unit of state — a key, not a container. The value lives in a
&lt;code&gt;Store&lt;/code&gt;; the atom just identifies it. A &lt;strong&gt;primitive&lt;/strong&gt; atom is seeded with a value:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; countAtom &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; atom&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;   &lt;span class=&quot;hl-comment&quot;&gt;// WritableAtom[Int]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A &lt;strong&gt;derived&lt;/strong&gt; atom computes its value from the atoms it reads, and recomputes
automatically whenever any of those change. The &lt;code&gt;get&lt;/code&gt; it receives both reads a dependency
and records it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; doubledAtom &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; atom&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;get &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; get&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;countAtom&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;        &lt;span class=&quot;hl-comment&quot;&gt;// read-only&lt;/span&gt;
&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; sumAtom     &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; atom&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;get &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; get&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;aAtom&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;+&lt;/span&gt; get&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;bAtom&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt;   &lt;span class=&quot;hl-comment&quot;&gt;// tracks both&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because dependencies are recorded per evaluation, an atom that reads different atoms on
different runs is tracked correctly each time.&lt;/p&gt;
&lt;h2 id=&quot;using-atoms-in-components&quot;&gt;Using atoms in components&lt;/h2&gt;
&lt;p&gt;Three hooks connect atoms to components, mirroring &lt;code&gt;useState&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;useAtom(a)&lt;/code&gt; → &lt;code&gt;(value, set)&lt;/code&gt; — read &lt;strong&gt;and&lt;/strong&gt; write.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;useAtomValue(a)&lt;/code&gt; → &lt;code&gt;value&lt;/code&gt; — read only.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;useSetAtom(a)&lt;/code&gt; → &lt;code&gt;set&lt;/code&gt; — write only (doesn’t subscribe, so the component doesn’t
re-render when the atom changes).&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Counter&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; view &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;count&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; setCount&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useAtom&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;countAtom&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; doubled           &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useAtomValue&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;doubledAtom&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;

  div&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
    p&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;count: $count, doubled: $doubled&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
    button&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;onClick &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;_ &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; setCount&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;count &lt;span class=&quot;hl-keyword&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)),&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Increment&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reading an atom subscribes the component to &lt;em&gt;just that atom&lt;/em&gt;: it re-renders when that
atom’s value changes and not otherwise. So &lt;code&gt;Counter&lt;/code&gt; above re-renders when &lt;code&gt;countAtom&lt;/code&gt;
changes; &lt;code&gt;doubledAtom&lt;/code&gt; recomputes and its readers re-render too; a component that reads
neither is untouched. This fine-grained subscription is automatic — no selectors required
for the common case.&lt;/p&gt;
&lt;h2 id=&quot;stores-and-scoping&quot;&gt;Stores and scoping&lt;/h2&gt;
&lt;p&gt;Every atom hook resolves its store from the nearest &lt;code&gt;StoreProvider&lt;/code&gt; ancestor, falling back
to the process-wide &lt;code&gt;Store.default&lt;/code&gt;. For most apps the default is all you need, and the
examples above work with no setup.&lt;/p&gt;
&lt;p&gt;To give a subtree its &lt;strong&gt;own&lt;/strong&gt; isolated state — for tests, for resetting a region, or for
rendering the same UI against different data — create a store with &lt;code&gt;new Store&lt;/code&gt; and wrap the
subtree in a &lt;code&gt;StoreProvider&lt;/code&gt;. The same atoms then hold independent values under each
provider:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; view &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;hl-type&quot;&gt;StoreProvider&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Store&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;hl-type&quot;&gt;Counter&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;more-atom-kinds&quot;&gt;More atom kinds&lt;/h2&gt;
&lt;p&gt;Beyond primitive and read-only derived atoms, the module provides:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Writable-derived atoms&lt;/strong&gt; — a derived value that also knows how to be written, by fanning
the write out to the atoms that back it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; celsiusAtom &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; atom&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; fahrenheitAtom &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; atom&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  read  &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; get &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; get&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;celsiusAtom&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;9&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
  write &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;get&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; set&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; f&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; set&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;celsiusAtom&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;f &lt;span class=&quot;hl-keyword&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Action atoms&lt;/strong&gt; — a write-only “command” that carries no readable value but mutates
several atoms at once when dispatched:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; resetAll &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; action&lt;span class=&quot;hl-punctuation&quot;&gt;((&lt;/span&gt;get&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; set&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; _&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Unit&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt;
  set&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;countAtom&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  set&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;celsiusAtom&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;// useSetAtom(resetAll) gives you a dispatcher&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;selectAtom(a, f)&lt;/code&gt;&lt;/strong&gt; — derive a narrowed slice of a larger atom that only notifies when
&lt;em&gt;that slice&lt;/em&gt; changes, so readers don’t re-render on unrelated parts of the source.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;atomFamily(make)&lt;/code&gt;&lt;/strong&gt; — a function from a parameter to an atom, memoized so the same
parameter always yields the same atom. Use it for per-id state (one atom per row, per
user, …).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;atomWithStorage(key, default)&lt;/code&gt;&lt;/strong&gt; — a primitive atom that persists to &lt;code&gt;localStorage&lt;/code&gt;
under &lt;code&gt;key&lt;/code&gt; and re-hydrates on load. A typed overload takes &lt;code&gt;encode&lt;/code&gt;/&lt;code&gt;decode&lt;/code&gt; for
non-string values.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;atomLoadable(future)&lt;/code&gt;&lt;/strong&gt; — wraps an async computation as an atom whose value is a
&lt;code&gt;Loadable&lt;/code&gt; with loading / data / error states, so a component can render each phase
without manual effect plumbing.&lt;/p&gt;
&lt;p&gt;Next: &lt;a href=&quot;/guide/routing/&quot;&gt;Routing&lt;/a&gt;.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Component library (salle)</title>
    <link href="https://riposte.edadma.dev/guide/salle/"/>
    <id>https://riposte.edadma.dev/guide/salle/</id>
    <updated>2026-05-31T15:40:05.566249418Z</updated>
    <summary>salle is a component library for Riposte — the styled, ready-made widgets layer that sits on top of the core’s elements and hooks. (The name is the fencing salle: the…</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;salle&lt;/strong&gt; is a component library for Riposte — the styled, ready-made widgets layer that
sits on top of the core’s elements and hooks. (The name is the fencing &lt;em&gt;salle&lt;/em&gt;: the hall
where the bouts happen.) It’s a separate published artifact that depends only on
&lt;code&gt;riposte&lt;/code&gt;‘s public API, so it adds no weight to an app that doesn’t use it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;libraryDependencies &lt;span class=&quot;hl-keyword&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;io.github.edadma&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;%%%&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;riposte-salle&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;0.0.2&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;import&lt;/span&gt; io&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;github&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;edadma&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;riposte&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;hl-keyword&quot;&gt;import&lt;/span&gt; io&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;github&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;edadma&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;riposte&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;salle&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;*&lt;/span&gt;

&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Form&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; view &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt;
  div&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;hl-type&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;placeholder &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Email&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; inputType &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;email&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;hl-type&quot;&gt;Checkbox&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;label &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Remember me&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; defaultChecked &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-variable&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;hl-type&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Sign in&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; color &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Primary&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; onClick &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; submit&lt;span class=&quot;hl-punctuation&quot;&gt;()),&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every salle component is an ordinary function with default arguments, returning a &lt;code&gt;VNode&lt;/code&gt;
— so you call it like any other component and override only the props you care about.&lt;/p&gt;
&lt;h2 id=&quot;the-skin-system&quot;&gt;The skin system&lt;/h2&gt;
&lt;p&gt;The idea that organizes salle: &lt;strong&gt;components describe intent, not appearance.&lt;/strong&gt; A button
doesn’t know it’s blue or rounded — it knows it’s a &lt;code&gt;Color.Primary&lt;/code&gt;, &lt;code&gt;Size.Md&lt;/code&gt;,
&lt;code&gt;ButtonVariant.Solid&lt;/code&gt;. A &lt;strong&gt;&lt;code&gt;Skin&lt;/code&gt;&lt;/strong&gt; translates that intent into the CSS classes of one
concrete styling system, and the active skin is read from context. So you can re-skin an
entire app’s look from a single provider at the root, with no change at any call site.&lt;/p&gt;
&lt;p&gt;Two skins ship:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;SalleSkin&lt;/code&gt;&lt;/strong&gt; (the default) — salle’s own look. It emits &lt;code&gt;salle-*&lt;/code&gt; classes whose rules
live in &lt;code&gt;salle.css&lt;/code&gt;. An app that configures nothing still gets a styled UI.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;DaisySkin&lt;/code&gt;&lt;/strong&gt; — emits the &lt;a href=&quot;https://daisyui.com/&quot;&gt;DaisyUI&lt;/a&gt; class vocabulary
(&lt;code&gt;btn btn-primary btn-sm&lt;/code&gt;). The styles come from DaisyUI + Tailwind in your own CSS
build; salle just produces the class names.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Choose one with &lt;code&gt;SkinProvider&lt;/code&gt; near the root; everything below it picks it up through
&lt;code&gt;useSkin&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;import&lt;/span&gt; io&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;github&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;edadma&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;riposte&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;salle&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;*&lt;/span&gt;

render&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;hl-type&quot;&gt;SkinProvider&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;DaisySkin&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;hl-type&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;},&lt;/span&gt;
  dom&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;document&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;getElementById&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;app&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Writing your own skin is implementing the &lt;code&gt;Skin&lt;/code&gt; trait — one method per component, mapping
its semantic props to your classes.&lt;/p&gt;
&lt;h2 id=&quot;light-dark-theming&quot;&gt;Light &amp;amp; dark theming&lt;/h2&gt;
&lt;p&gt;Skins decide &lt;em&gt;which design system&lt;/em&gt; renders a component; &lt;strong&gt;themes&lt;/strong&gt; decide its &lt;em&gt;light/dark
palette&lt;/em&gt;. They’re orthogonal: a single global theme name drives a &lt;code&gt;data-theme&lt;/code&gt; attribute on
the document root, and both skins respond to it — &lt;code&gt;SalleSkin&lt;/code&gt; through the &lt;code&gt;[data-theme=&amp;quot;…&amp;quot;]&lt;/code&gt;
token blocks in &lt;code&gt;salle.css&lt;/code&gt;, &lt;code&gt;DaisySkin&lt;/code&gt; through DaisyUI’s own themes.&lt;/p&gt;
&lt;p&gt;The theme set is &lt;strong&gt;open&lt;/strong&gt;. salle ships &lt;code&gt;light&lt;/code&gt; and &lt;code&gt;dark&lt;/code&gt;; &lt;code&gt;system&lt;/code&gt; follows the OS
preference; and any other name selects whatever &lt;code&gt;[data-theme=&amp;quot;…&amp;quot;]&lt;/code&gt; block your app defines
(&lt;code&gt;&amp;quot;dracula&amp;quot;&lt;/code&gt;, a brand theme, …).&lt;/p&gt;
&lt;p&gt;&lt;code&gt;useTheme()&lt;/code&gt; is the hook. It returns a named tuple and, on first read, installs salle’s
theme management — setting &lt;code&gt;data-theme&lt;/code&gt;, persisting the choice to &lt;code&gt;localStorage&lt;/code&gt;, following
the OS while on &lt;code&gt;&amp;quot;system&amp;quot;&lt;/code&gt;, and syncing across tabs:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; t &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useTheme&lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;//  t.theme    : String        — the stored name (&amp;quot;system&amp;quot; / &amp;quot;light&amp;quot; / &amp;quot;dark&amp;quot; / custom)&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;//  t.resolved : String        — the concrete theme (&amp;quot;system&amp;quot; resolved to the OS pref)&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;//  t.setTheme : String =&amp;gt; Unit — select any theme name&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;//  t.toggle   : () =&amp;gt; Unit     — flip light ⇄ dark&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the common cases salle ships two ready components:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-type&quot;&gt;ThemeToggle&lt;/span&gt;                              &lt;span class=&quot;hl-comment&quot;&gt;// an icon button (sun/moon): light ⇄ dark&lt;/span&gt;
&lt;span class=&quot;hl-type&quot;&gt;ThemeSelect&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;system&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;light&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;dark&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt;   &lt;span class=&quot;hl-comment&quot;&gt;// a &amp;lt;select&amp;gt; bound to the active theme&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both follow the active skin, and &lt;code&gt;ThemeSelect&lt;/code&gt; is the way to surface a larger open theme
set. To define your own theme, add a &lt;code&gt;[data-theme=&amp;quot;name&amp;quot;]&lt;/code&gt; block (overriding the
&lt;code&gt;--salle-*&lt;/code&gt; custom properties) and offer its name through &lt;code&gt;setTheme&lt;/code&gt;/&lt;code&gt;ThemeSelect&lt;/code&gt; — see
&lt;a href=&quot;/#theming-the-default-look&quot;&gt;Theming the default look&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;the-style-vocabulary&quot;&gt;The style vocabulary&lt;/h2&gt;
&lt;p&gt;Two enums are shared by every component that has them, so the same values mean the same
thing everywhere:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Color&lt;/code&gt;&lt;/strong&gt; — &lt;code&gt;Default&lt;/code&gt;, &lt;code&gt;Primary&lt;/code&gt;, &lt;code&gt;Secondary&lt;/code&gt;, &lt;code&gt;Accent&lt;/code&gt;, &lt;code&gt;Neutral&lt;/code&gt;, &lt;code&gt;Info&lt;/code&gt;,
&lt;code&gt;Success&lt;/code&gt;, &lt;code&gt;Warning&lt;/code&gt;, &lt;code&gt;Error&lt;/code&gt; (mirrors DaisyUI’s palette). &lt;code&gt;Default&lt;/code&gt; applies no color
modifier — the component keeps its base look.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Size&lt;/code&gt;&lt;/strong&gt; — &lt;code&gt;Xs&lt;/code&gt;, &lt;code&gt;Sm&lt;/code&gt;, &lt;code&gt;Md&lt;/code&gt;, &lt;code&gt;Lg&lt;/code&gt;, &lt;code&gt;Xl&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A component’s &lt;em&gt;variant&lt;/em&gt; axis is specific to it, so each declares its own enum — e.g.
&lt;code&gt;ButtonVariant&lt;/code&gt; (&lt;code&gt;Solid&lt;/code&gt;, &lt;code&gt;Outline&lt;/code&gt;, &lt;code&gt;Dash&lt;/code&gt;, &lt;code&gt;Soft&lt;/code&gt;, &lt;code&gt;Ghost&lt;/code&gt;, &lt;code&gt;Link&lt;/code&gt;).&lt;/p&gt;
&lt;h2 id=&quot;components&quot;&gt;Components&lt;/h2&gt;
&lt;h3 id=&quot;button&quot;&gt;Button&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-type&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Save&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;                                             &lt;span class=&quot;hl-comment&quot;&gt;// default&lt;/span&gt;
&lt;span class=&quot;hl-type&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Delete&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; color &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-type&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Cancel&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; color &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Primary&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; variant &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;ButtonVariant&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Outline&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-type&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Go&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; size &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Size&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Sm&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; onClick &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; navigate&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/next&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;hl-type&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Disabled&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; disabled &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-variable&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The three style axes — &lt;code&gt;color&lt;/code&gt;, &lt;code&gt;variant&lt;/code&gt;, &lt;code&gt;size&lt;/code&gt; — are independent. The disabled state is
reflected both as the HTML attribute and as &lt;code&gt;data-state&lt;/code&gt;, for styling hooks.&lt;/p&gt;
&lt;h3 id=&quot;input&quot;&gt;Input&lt;/h3&gt;
&lt;p&gt;A single-line text field that’s &lt;em&gt;controlled&lt;/em&gt; when you pass &lt;code&gt;value = Some(...)&lt;/code&gt; (your app
owns the text and is notified through &lt;code&gt;onChange&lt;/code&gt;) or &lt;em&gt;uncontrolled&lt;/em&gt; when you don’t (salle
holds it internally, seeded from &lt;code&gt;defaultValue&lt;/code&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-comment&quot;&gt;// Controlled — the app drives the value:&lt;/span&gt;
&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;email&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; setEmail&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; _&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useState&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-type&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;email&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt; onChange &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; setEmail&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; inputType &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;email&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;hl-comment&quot;&gt;// Uncontrolled — salle holds the text:&lt;/span&gt;
&lt;span class=&quot;hl-type&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;defaultValue &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;draft&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; placeholder &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Notes&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;hl-comment&quot;&gt;// Validation state:&lt;/span&gt;
&lt;span class=&quot;hl-type&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;invalid &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-variable&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; color &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;invalid&lt;/code&gt; switches to the error treatment and sets &lt;code&gt;aria-invalid&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;checkbox-and-toggle&quot;&gt;Checkbox and Toggle&lt;/h3&gt;
&lt;p&gt;Both share the Input’s controlled/uncontrolled model (&lt;code&gt;checked: Option[Boolean]&lt;/code&gt; +
&lt;code&gt;defaultChecked&lt;/code&gt;), and both render a &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; around the box when given a non-empty
&lt;code&gt;label&lt;/code&gt; so clicking the text toggles it. &lt;code&gt;Toggle&lt;/code&gt; is the same control styled as a switch,
with &lt;code&gt;role=&amp;quot;switch&amp;quot;&lt;/code&gt; and &lt;code&gt;aria-checked&lt;/code&gt; for assistive tech:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-type&quot;&gt;Checkbox&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;label &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Accept terms&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; onChange &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; setAccepted&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-type&quot;&gt;Checkbox&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;label &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Subscribed&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; defaultChecked &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-variable&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;hl-type&quot;&gt;Toggle&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;label &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Wi-Fi&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; defaultChecked &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-variable&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-type&quot;&gt;Toggle&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;label &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Dark mode&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; checked &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;dark&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt; onChange &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; setDark&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;select&quot;&gt;Select&lt;/h3&gt;
&lt;p&gt;A single-select dropdown — built as a custom ARIA combobox (not a native &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt;), so
it supports typeahead, keyboard navigation, a clear affordance, and skin-styled options.
Options are built with &lt;code&gt;Opt&lt;/code&gt;; the chosen value follows the same controlled/uncontrolled
model as the form controls above:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-type&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  options &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;hl-type&quot;&gt;Opt&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;us&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;United States&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;hl-type&quot;&gt;Opt&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;ca&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Canada&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;hl-type&quot;&gt;Opt&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;mx&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Mexico&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; disabled &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-variable&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  placeholder &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Country&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
  clearable &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-variable&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
  onChange &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; setCountry&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Opt(value, label, disabled)&lt;/code&gt; defaults &lt;code&gt;label&lt;/code&gt; to &lt;code&gt;value&lt;/code&gt;. &lt;code&gt;Select&lt;/code&gt; takes the usual
&lt;code&gt;color&lt;/code&gt;/&lt;code&gt;size&lt;/code&gt;/&lt;code&gt;disabled&lt;/code&gt;/&lt;code&gt;invalid&lt;/code&gt;, plus &lt;code&gt;clearable&lt;/code&gt; (adds a reset button) and &lt;code&gt;name&lt;/code&gt;
(emits a hidden input so it participates in native form submission). It’s fully
keyboard-driven (arrows, Home/End, Enter/Space, Escape, type-to-search) and mirrors its
state to &lt;code&gt;data-*&lt;/code&gt; (&lt;code&gt;data-state&lt;/code&gt;, &lt;code&gt;data-value&lt;/code&gt;, and per-option &lt;code&gt;data-selected&lt;/code&gt;/&lt;code&gt;data-active&lt;/code&gt;).&lt;/p&gt;
&lt;h3 id=&quot;imagecard&quot;&gt;ImageCard&lt;/h3&gt;
&lt;p&gt;A single image tile for a media grid. The full image loads lazily — only once the tile
nears the viewport (built on &lt;a href=&quot;/guide/hooks/#dom-hooks&quot;&gt;&lt;code&gt;useIntersectionObserver&lt;/code&gt;&lt;/a&gt;) — showing
a skeleton until it arrives and fading in on load. If &lt;code&gt;src&lt;/code&gt; fails it tries &lt;code&gt;fallback&lt;/code&gt;, then
shows an error placeholder:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-type&quot;&gt;ImageCard&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  src &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; thumbUrl&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
  alt &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Sunset over the bay&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
  ratio &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;16/9&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;                 &lt;span class=&quot;hl-comment&quot;&gt;// reserves space so the grid doesn&apos;t reflow&lt;/span&gt;
  fit &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;ImageFit&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Cover&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;           &lt;span class=&quot;hl-comment&quot;&gt;// Cover | Contain | Fill | ScaleDown&lt;/span&gt;
  badge &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;span&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;4K&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)),&lt;/span&gt;       &lt;span class=&quot;hl-comment&quot;&gt;// a corner tag&lt;/span&gt;
  overlay &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;downloadButton&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;hl-comment&quot;&gt;// hover content&lt;/span&gt;
  onClick &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; open&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;fullUrl&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;ratio&lt;/code&gt; is a CSS &lt;code&gt;aspect-ratio&lt;/code&gt; (&lt;code&gt;&amp;quot;16/9&amp;quot;&lt;/code&gt;, &lt;code&gt;&amp;quot;1/1&amp;quot;&lt;/code&gt;); &lt;code&gt;fit&lt;/code&gt; controls cropping; &lt;code&gt;rounded&lt;/code&gt;
(default &lt;code&gt;true&lt;/code&gt;) rounds the corners; &lt;code&gt;lazyLoad = false&lt;/code&gt; opts out of deferred loading.
State is mirrored to &lt;code&gt;data-state&lt;/code&gt; (&lt;code&gt;loading&lt;/code&gt;/&lt;code&gt;loaded&lt;/code&gt;/&lt;code&gt;error&lt;/code&gt;) and &lt;code&gt;data-inview&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;layout&quot;&gt;Layout&lt;/h2&gt;
&lt;p&gt;Two layout systems sit alongside the components — one for &lt;em&gt;precise&lt;/em&gt; column layouts, one for
&lt;em&gt;packed&lt;/em&gt; ones.&lt;/p&gt;
&lt;h3 id=&quot;grid-row-col&quot;&gt;Grid (Row / Col)&lt;/h3&gt;
&lt;p&gt;A 24-column grid in the Ant Design tradition: a &lt;code&gt;Row&lt;/code&gt; lays out equal column tracks, and each
&lt;code&gt;Col&lt;/code&gt; spans some of them, with per-breakpoint spans, offsets, and ordering. Reach for it
when you want columns to &lt;em&gt;line up&lt;/em&gt; on a stated grid:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-type&quot;&gt;Row&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;gutterX &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;
  &lt;span class=&quot;hl-type&quot;&gt;Col&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;span &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;main&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;hl-type&quot;&gt;Col&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;span &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;sidebar&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;hl-comment&quot;&gt;// Responsive: full width on phones, half on small, a third from medium up.&lt;/span&gt;
&lt;span class=&quot;hl-type&quot;&gt;Row&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;()(&lt;/span&gt;
  &lt;span class=&quot;hl-type&quot;&gt;Col&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;xs &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; sm &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; md &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;card1&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;hl-type&quot;&gt;Col&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;xs &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; sm &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; md &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;card2&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;hl-type&quot;&gt;Col&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;xs &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; sm &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; md &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;card3&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Row(cols, gutterX, gutterY, justify, align)&lt;/code&gt; — &lt;code&gt;cols&lt;/code&gt; defaults to 24; &lt;code&gt;gutterX&lt;/code&gt;/&lt;code&gt;gutterY&lt;/code&gt;
are the pixel gutters; &lt;code&gt;justify&lt;/code&gt; aligns columns along the row
(&lt;code&gt;start&lt;/code&gt;/&lt;code&gt;end&lt;/code&gt;/&lt;code&gt;center&lt;/code&gt;/&lt;code&gt;between&lt;/code&gt;/&lt;code&gt;around&lt;/code&gt;/&lt;code&gt;evenly&lt;/code&gt;) and &lt;code&gt;align&lt;/code&gt; across it
(&lt;code&gt;start&lt;/code&gt;/&lt;code&gt;end&lt;/code&gt;/&lt;code&gt;center&lt;/code&gt;/&lt;code&gt;stretch&lt;/code&gt;/&lt;code&gt;baseline&lt;/code&gt;). &lt;code&gt;Col(span, offset, order, xs…xxl)&lt;/code&gt; — &lt;code&gt;span&lt;/code&gt;
is the base width (full row by default), &lt;code&gt;offset&lt;/code&gt; pushes it right, &lt;code&gt;order&lt;/code&gt; overrides visual
position, and &lt;code&gt;xs&lt;/code&gt;/&lt;code&gt;sm&lt;/code&gt;/&lt;code&gt;md&lt;/code&gt;/&lt;code&gt;lg&lt;/code&gt;/&lt;code&gt;xl&lt;/code&gt;/&lt;code&gt;xxl&lt;/code&gt; set per-breakpoint spans (each inheriting the
next smaller when unset). Both take their children curried: &lt;code&gt;Row(…)(cols*)&lt;/code&gt;, &lt;code&gt;Col(…)(content*)&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;masonry&quot;&gt;Masonry&lt;/h3&gt;
&lt;p&gt;A Pinterest-style packed layout: tiles of differing heights flow into a fixed number of
equal-width columns, each tile placed into the currently shortest column (balanced columns,
left-to-right reading order — unlike a CSS multi-column flow). It measures real tile heights
(via &lt;a href=&quot;/guide/hooks/#dom-hooks&quot;&gt;&lt;code&gt;useResizeObserver&lt;/code&gt;&lt;/a&gt;) and re-packs on resize or when children
change:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-type&quot;&gt;Masonry&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;columns &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; gap &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;tiles&lt;span class=&quot;hl-keyword&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;hl-comment&quot;&gt;// Column count follows the viewport:&lt;/span&gt;
&lt;span class=&quot;hl-type&quot;&gt;Masonry&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;columns &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;MasonryColumns&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Responsive&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;base &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; md &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; lg &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;))(&lt;/span&gt;tiles&lt;span class=&quot;hl-keyword&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;// or the shorthand:&lt;/span&gt;
&lt;span class=&quot;hl-type&quot;&gt;MasonryResponsive&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;base &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; sm &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; lg &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;tiles&lt;span class=&quot;hl-keyword&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A bare &lt;code&gt;Int&lt;/code&gt; for &lt;code&gt;columns&lt;/code&gt; is a fixed count (&lt;code&gt;Masonry(columns = 3)&lt;/code&gt;); &lt;code&gt;MasonryColumns.Responsive&lt;/code&gt;
picks a count by viewport against the standard breakpoints. Tiles should size naturally — an
&lt;a href=&quot;/#imagecard&quot;&gt;&lt;code&gt;ImageCard&lt;/code&gt;&lt;/a&gt; with a &lt;code&gt;ratio&lt;/code&gt; is the canonical cell. The pure packing function,
&lt;code&gt;layoutMasonry(heights, columns, gap, containerWidth)&lt;/code&gt;, is exposed too if you need the
geometry without the component.&lt;/p&gt;
&lt;h2 id=&quot;usecontrollable&quot;&gt;useControllable&lt;/h2&gt;
&lt;p&gt;The hook every salle form control is built on, and one you can reuse for your own
controlled/uncontrolled components. Given the optional controlled value, a default, and an
&lt;code&gt;onChange&lt;/code&gt;, it returns a &lt;code&gt;(current, set)&lt;/code&gt; pair that does the right thing in either mode:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;current&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; set&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useControllable&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; default&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; onChange&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In &lt;strong&gt;controlled&lt;/strong&gt; mode (&lt;code&gt;value&lt;/code&gt; is &lt;code&gt;Some&lt;/code&gt;) &lt;code&gt;set&lt;/code&gt; only calls &lt;code&gt;onChange&lt;/code&gt; — the next render’s
&lt;code&gt;Some&lt;/code&gt; carries the new value back. In &lt;strong&gt;uncontrolled&lt;/strong&gt; mode (&lt;code&gt;value&lt;/code&gt; is &lt;code&gt;None&lt;/code&gt;) &lt;code&gt;set&lt;/code&gt;
updates internal state &lt;em&gt;and&lt;/em&gt; calls &lt;code&gt;onChange&lt;/code&gt; as a notification. Writing a component
against this pair means its body is identical in both modes.&lt;/p&gt;
&lt;h2 id=&quot;theming-the-default-look&quot;&gt;Theming the default look&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;SalleSkin&lt;/code&gt;‘s styles live in &lt;code&gt;salle.css&lt;/code&gt;. Include it once in your app, then put any
overrides after it. Every rule sits in a low-priority &lt;code&gt;@layer salle&lt;/code&gt; cascade layer, so
your own unlayered CSS always wins — no &lt;code&gt;!important&lt;/code&gt;, no specificity fights. Two ways to
retheme, both plain CSS:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Values&lt;/strong&gt; — override the &lt;code&gt;--salle-*&lt;/code&gt; custom properties (colors, radius, focus ring) at
&lt;code&gt;:root&lt;/code&gt; or under a &lt;code&gt;[data-theme=…]&lt;/code&gt; block.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rules&lt;/strong&gt; — restyle a selector directly (say, add a shadow to &lt;code&gt;.salle-btn&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-function&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;hl-variable&quot;&gt;--salle-radius&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;0.25&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;rem&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;hl-variable&quot;&gt;--salle-color-primary&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;hl-variable&quot;&gt;0c8599&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;hl-punctuation&quot;&gt;/*&lt;/span&gt;&lt;span class=&quot;hl-comment&quot;&gt; A custom theme, selectable by name through setTheme / ThemeSelect. &lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;*/&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-function&quot;&gt;data-theme&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;ocean&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;hl-type&quot;&gt;color-scheme&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; dark&lt;span class=&quot;hl-punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;hl-variable&quot;&gt;--salle-color-primary&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;hl-variable&quot;&gt;0c8599&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;salle’s own &lt;code&gt;light&lt;/code&gt; and &lt;code&gt;dark&lt;/code&gt; palettes are exactly such blocks, so the
&lt;a href=&quot;/#light-dark-theming&quot;&gt;theming hook and components&lt;/a&gt; above pick up a custom one with no
extra wiring.&lt;/p&gt;
&lt;p&gt;Classes follow a BEM-ish convention: a base (&lt;code&gt;.salle-btn&lt;/code&gt;) plus independent modifiers for
color (&lt;code&gt;--primary&lt;/code&gt;), variant (&lt;code&gt;--outline&lt;/code&gt;), and size (&lt;code&gt;--sm&lt;/code&gt;). Each color modifier sets a
single custom property that the variant rules reinterpret, so color × variant combine
without a rule per pair.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Routing</title>
    <link href="https://riposte.edadma.dev/guide/routing/"/>
    <id>https://riposte.edadma.dev/guide/routing/</id>
    <updated>2026-05-31T15:40:05.566249418Z</updated>
    <summary>riposte-router maps URLs to views for single-page apps. It’s built entirely on the core’s public API — useSyncExternalStore for the location, context for route params and the outlet, the DSL…</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;riposte-router&lt;/strong&gt; maps URLs to views for single-page apps. It’s built entirely on the
core’s public API — &lt;code&gt;useSyncExternalStore&lt;/code&gt; for the location, context for route params and
the outlet, the DSL for links — so it touches no internals and adds nothing you couldn’t
build yourself; it just saves you from doing so.&lt;/p&gt;
&lt;p&gt;Add the dependency (it pulls in the core transitively):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;libraryDependencies &lt;span class=&quot;hl-keyword&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;io.github.edadma&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;%%%&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;riposte-router&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;0.0.2&amp;quot;&lt;/span&gt;

&lt;span class=&quot;hl-keyword&quot;&gt;import&lt;/span&gt; io&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;github&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;edadma&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;riposte&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;hl-keyword&quot;&gt;import&lt;/span&gt; io&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;github&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;edadma&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;riposte&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;router&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;*&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;a-router-and-some-routes&quot;&gt;A router and some routes&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Router&lt;/code&gt; establishes the routing context and tracks the current location. Inside it,
&lt;code&gt;Routes&lt;/code&gt; picks the best-matching route for the current URL and renders it. &lt;code&gt;route&lt;/code&gt; pairs a
path pattern with a view:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; view &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;hl-type&quot;&gt;Router&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;hl-type&quot;&gt;Routes&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
      route&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Home&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;()),&lt;/span&gt;
      route&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/about&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;About&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;()),&lt;/span&gt;
      route&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/users/:id&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;UserPage&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;()),&lt;/span&gt;
      route&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;*&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;NotFound&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;()),&lt;/span&gt;
    &lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Routes&lt;/code&gt; picks the &lt;strong&gt;most specific&lt;/strong&gt; match regardless of declaration order, so a static
&lt;code&gt;/users/new&lt;/code&gt; wins over the dynamic &lt;code&gt;/users/:id&lt;/code&gt; even if &lt;code&gt;:id&lt;/code&gt; is declared first. A &lt;code&gt;&amp;quot;*&amp;quot;&lt;/code&gt;
pattern is a catch-all — the idiomatic not-found route.&lt;/p&gt;
&lt;h3 id=&quot;history-vs-hash-mode&quot;&gt;History vs. hash mode&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Router&lt;/code&gt; defaults to &lt;strong&gt;History&lt;/strong&gt; mode — clean URLs (&lt;code&gt;/users/7&lt;/code&gt;) via the History API, which
needs the server to fall back to &lt;code&gt;index.html&lt;/code&gt; for unknown paths. For static hosting with
no such fallback, use &lt;strong&gt;hash&lt;/strong&gt; mode, which keeps everything after a &lt;code&gt;#&lt;/code&gt; (&lt;code&gt;/#/users/7&lt;/code&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-type&quot;&gt;Router&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;RouterMode&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Hash&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;hl-type&quot;&gt;Routes&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;…&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;params&quot;&gt;Params&lt;/h2&gt;
&lt;p&gt;A &lt;code&gt;:name&lt;/code&gt; segment captures a path parameter. Read the matched params with &lt;code&gt;useParams&lt;/code&gt;,
which returns a &lt;code&gt;Params&lt;/code&gt; (a &lt;code&gt;Map[String, String]&lt;/code&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;UserPage&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; view &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; id &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useParams&lt;span class=&quot;hl-punctuation&quot;&gt;().&lt;/span&gt;getOrElse&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  p&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;User $id&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;useParams&lt;/code&gt; reads the params of the nearest enclosing route, and params &lt;strong&gt;accumulate down
a nested branch&lt;/strong&gt; — a child route sees its own captures plus all of its ancestors’.&lt;/p&gt;
&lt;p&gt;Alternatively, take the params directly in the route declaration:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;route&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/users/:id&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;params &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;UserDetail&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;params&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;links-and-navigation&quot;&gt;Links and navigation&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Link&lt;/code&gt; renders an &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; that navigates in-app — no full-page reload:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;nav&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;hl-type&quot;&gt;Link&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Home&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;hl-type&quot;&gt;Link&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/about&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;About&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;NavLink&lt;/code&gt; is a &lt;code&gt;Link&lt;/code&gt; that adds an active CSS class when its target matches the current
location. It’s curried — options first, then the children — and &lt;code&gt;end = true&lt;/code&gt; requires an
exact path match (otherwise a prefix match counts, so &lt;code&gt;/users&lt;/code&gt; is active on
&lt;code&gt;/users/7&lt;/code&gt; too):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;nav&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;hl-type&quot;&gt;NavLink&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; activeClass &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;current&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; end &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-variable&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Home&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;hl-type&quot;&gt;NavLink&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/users&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; activeClass &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;current&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Users&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To navigate imperatively — after a form submit, say — call &lt;code&gt;navigate&lt;/code&gt;. Pass
&lt;code&gt;replace = true&lt;/code&gt; to replace the current history entry instead of pushing a new one:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;button&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;onClick &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;_ &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; navigate&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/users/42&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)),&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Open user 42&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
navigate&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/login&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; replace &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-variable&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;nested-routes&quot;&gt;Nested routes&lt;/h2&gt;
&lt;p&gt;A route can take &lt;strong&gt;child routes&lt;/strong&gt;. The parent matches a &lt;em&gt;prefix&lt;/em&gt; of the path and renders
its matched child wherever its view places an &lt;code&gt;Outlet&lt;/code&gt;; the child’s pattern is relative to
the parent. &lt;code&gt;index(...)&lt;/code&gt; declares the child shown when the parent’s own path is matched
exactly:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-type&quot;&gt;Routes&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  route&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/dashboard&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Dashboard&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;())(&lt;/span&gt;
    index&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Overview&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;()),&lt;/span&gt;
    route&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;settings&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Settings&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;()),&lt;/span&gt;
    route&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;users/:id&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;UserDetail&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;()),&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Dashboard&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; view &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt;
  div&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
    h1&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Dashboard&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
    nav&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;hl-type&quot;&gt;NavLink&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/dashboard/settings&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; end &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-variable&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Settings&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;hl-type&quot;&gt;Outlet&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;   &lt;span class=&quot;hl-comment&quot;&gt;// Overview, Settings, or UserDetail renders here&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is how you build shared layouts: the parent route is the chrome (header, sidebar,
nav), and &lt;code&gt;Outlet&lt;/code&gt; is the hole the active child fills.&lt;/p&gt;
&lt;h2 id=&quot;query-strings&quot;&gt;Query strings&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;useSearchParams&lt;/code&gt; reads and updates the query portion of the URL, in both History and hash
modes. It returns the current params and a setter; the setter takes the new params and a
&lt;code&gt;replace&lt;/code&gt; flag:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Search&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; view &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;params&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; setParams&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useSearchParams&lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; q &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; params&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;getOrElse&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;q&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;

  input&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
    value &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; q&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
    onInput &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;e &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; setParams&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;q&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;-&amp;gt;&lt;/span&gt; targetValue&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;hl-punctuation&quot;&gt;)),&lt;/span&gt; &lt;span class=&quot;hl-variable&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)),&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;per-route-error-boundaries&quot;&gt;Per-route error boundaries&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;route(...).catchErrors(fallback)&lt;/code&gt; wraps a route’s view in an
&lt;a href=&quot;/guide/components/#escape-hatches&quot;&gt;error boundary&lt;/a&gt;: if rendering that route — or any
descendant up to a nested route’s own boundary — throws, &lt;code&gt;fallback(error)&lt;/code&gt; shows in its
place instead of the failure tearing down the app. The route keeps matching, so fixing the
cause and re-rendering recovers:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;route&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/report/:id&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Report&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;())&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;catchErrors&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;err &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; div&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;cls &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;error&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Couldn&apos;t load report: ${err.getMessage}&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;lazy-routes&quot;&gt;Lazy routes&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;lazyView&lt;/code&gt; defers loading a view until it’s first rendered, backing onto JavaScript’s
dynamic &lt;code&gt;import()&lt;/code&gt; so the bundler can split that view into its own chunk. It takes a
function returning a &lt;code&gt;js.Promise[VNode]&lt;/code&gt; and an optional fallback shown while loading:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;route&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/admin&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;
  lazyView&lt;span class=&quot;hl-punctuation&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; loadAdminPanel&lt;span class=&quot;hl-punctuation&quot;&gt;(),&lt;/span&gt; fallback &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; p&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Loading…&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pair it with code-splitting in your build to keep the initial bundle small and load heavy
routes on demand.&lt;/p&gt;
&lt;h2 id=&quot;scroll-restoration&quot;&gt;Scroll restoration&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;ScrollRestoration&lt;/code&gt; is a component that resets (and, for back/forward navigation, restores)
the scroll position as the location changes — the behavior browsers do for free on full
page loads but not for in-app navigation. Render it once, inside the &lt;code&gt;Router&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; view &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;hl-type&quot;&gt;Router&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;hl-type&quot;&gt;ScrollRestoration&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;hl-type&quot;&gt;Routes&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;…&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content>
  </entry>
  <entry>
    <title>Quick Start</title>
    <link href="https://riposte.edadma.dev/getting-started/quick-start/"/>
    <id>https://riposte.edadma.dev/getting-started/quick-start/</id>
    <updated>2026-05-31T15:40:05.566249418Z</updated>
    <summary>This walks through a complete, tiny Riposte application: a counter you can click.</summary>
    <content type="html">&lt;p&gt;This walks through a complete, tiny Riposte application: a counter you can click.&lt;/p&gt;
&lt;h2 id=&quot;describe-the-ui&quot;&gt;Describe the UI&lt;/h2&gt;
&lt;p&gt;A component is a value built with &lt;code&gt;view { … }&lt;/code&gt;. Inside the block you call hooks like
&lt;code&gt;useState&lt;/code&gt; at the top level — &lt;code&gt;view&lt;/code&gt; runs the body with a &lt;code&gt;Hooks&lt;/code&gt; context in scope — and
return a &lt;code&gt;VNode&lt;/code&gt; built with the DSL’s HTML tags, attributes, and event handlers:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;import&lt;/span&gt; io&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;github&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;edadma&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;riposte&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;*&lt;/span&gt;

&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Counter&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; view &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;count&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; set&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; update&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useState&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;

  div&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
    p&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Count: $count&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
    button&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;onClick &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;_ &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; update&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;_ &lt;span class=&quot;hl-keyword&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)),&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Increment&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
    button&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;onClick &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;_ &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; set&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)),&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Reset&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;useState&lt;/code&gt; returns three things: the current &lt;code&gt;state&lt;/code&gt;, a &lt;code&gt;set&lt;/code&gt; function that replaces it,
and an &lt;code&gt;update&lt;/code&gt; function that derives the next value from the previous one. Reach for
&lt;code&gt;update&lt;/code&gt; whenever the new value depends on the old.&lt;/p&gt;
&lt;h2 id=&quot;mount-it&quot;&gt;Mount it&lt;/h2&gt;
&lt;p&gt;Rendering attaches a component to a DOM node and keeps it in sync as state changes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;import&lt;/span&gt; org&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;scalajs&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;dom

@main &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; main&lt;span class=&quot;hl-punctuation&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Unit&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; root &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; dom&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;document&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;getElementById&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;app&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  render&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Counter&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(),&lt;/span&gt; root&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note the &lt;code&gt;Counter()&lt;/code&gt; — a &lt;code&gt;view&lt;/code&gt; is a no-props component, so you call it with empty parens
to produce a node, whether you’re handing it to &lt;code&gt;render&lt;/code&gt; or nesting it as a child of
another element.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;&amp;lt;!--&lt;/span&gt;&lt;span class=&quot;hl-comment&quot;&gt; index.html --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;hl-function&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;hl-function&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;hl-function&quot;&gt;script&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;main.js&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&amp;lt;&lt;span class=&quot;hl-punctuation&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;hl-function&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Build the JavaScript with &lt;code&gt;fastLinkJS&lt;/code&gt; (development) or &lt;code&gt;fullLinkJS&lt;/code&gt; (production):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sbt app/fastLinkJS
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That is the whole loop: clicking a button calls a setter, the setter schedules a
re-render, the reconciler diffs the new tree against the old, and only the changed text
node is touched.&lt;/p&gt;
&lt;h2 id=&quot;where-to-go-next&quot;&gt;Where to go next&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/guide/components/&quot;&gt;Components &amp;amp; the DSL&lt;/a&gt; — tags, attributes, children, events.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/guide/hooks/&quot;&gt;Hooks&lt;/a&gt; — state, effects, refs, and the rules that govern them.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/guide/state/&quot;&gt;State with atoms&lt;/a&gt; — share state across components without prop-drilling.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/guide/routing/&quot;&gt;Routing&lt;/a&gt; — map URLs to views in a single-page app.&lt;/li&gt;
&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Modules &amp; API</title>
    <link href="https://riposte.edadma.dev/reference/modules/"/>
    <id>https://riposte.edadma.dev/reference/modules/</id>
    <updated>2026-05-31T15:40:05.566249418Z</updated>
    <summary>Riposte ships as four independently published artifacts under the io.github.edadma organization, all at the same version. The three sibling artifacts depend on the core transitively, so adding any of them…</summary>
    <content type="html">&lt;p&gt;Riposte ships as four independently published artifacts under the &lt;code&gt;io.github.edadma&lt;/code&gt;
organization, all at the same version. The three sibling artifacts depend on the core
transitively, so adding any of them is enough — you don’t list &lt;code&gt;riposte&lt;/code&gt; separately.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Artifact&lt;/th&gt;&lt;th&gt;Import&lt;/th&gt;&lt;th&gt;What it gives you&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;riposte&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;io.github.edadma.riposte.*&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Components, hooks, the DSL, rendering&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;riposte-atoms&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;io.github.edadma.riposte.atoms.*&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Shared atomic state&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;riposte-router&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;io.github.edadma.riposte.router.*&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Client-side routing&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;riposte-salle&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;io.github.edadma.riposte.salle.*&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Styled, skinnable component library&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;riposte-salle&lt;/code&gt; also depends on &lt;code&gt;riposte&lt;/code&gt; transitively. The current published version is &lt;code&gt;0.0.2&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;riposte-core&quot;&gt;riposte (core)&lt;/h2&gt;
&lt;p&gt;The React-inspired core: an immutable &lt;code&gt;VNode&lt;/code&gt; tree, a reconciler that diffs it against the
live DOM, function components, and hooks.&lt;/p&gt;
&lt;h3 id=&quot;components&quot;&gt;Components&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;API&lt;/th&gt;&lt;th&gt;Purpose&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;view { … }&lt;/code&gt;&lt;/td&gt;&lt;td&gt;A no-props component. Call it &lt;code&gt;C()&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;component[P] { p =&amp;gt; … }&lt;/code&gt;&lt;/td&gt;&lt;td&gt;A component taking one prop (or a named tuple).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;component[A, B] { (a, b) =&amp;gt; … }&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Positional props (up to four).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;container { children =&amp;gt; … }&lt;/code&gt;&lt;/td&gt;&lt;td&gt;A component that wraps children (a slot).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;container[P] { (p, children) =&amp;gt; … }&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Props + children; called curried &lt;code&gt;C(p)(kids…)&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;memo(c)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Bail out of re-render when props are unchanged.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;See &lt;a href=&quot;/guide/components/&quot;&gt;Components &amp;amp; the DSL&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;hooks&quot;&gt;Hooks&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Hook&lt;/th&gt;&lt;th&gt;Returns / does&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;useState(initial)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;(value, set, update)&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;useReducer(reducer, initial)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;(state, dispatch)&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;useEffect(body, deps)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Run a side effect after paint; &lt;code&gt;body&lt;/code&gt; returns cleanup&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;useLayoutEffect(body, deps)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Same, but synchronously before paint&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;useRef(initial)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;A mutable &lt;code&gt;.current&lt;/code&gt; box; bind to a node with &lt;code&gt;ref&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;useMemo(compute, deps)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Cache an expensive value&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;useCallback(fn, deps)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Cache a function’s identity&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;useId()&lt;/code&gt;&lt;/td&gt;&lt;td&gt;A stable unique id string&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;useContext(ctx)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;The nearest provided context value&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;useTransition(target, durationMs)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;An eased &lt;code&gt;Double&lt;/code&gt; animated each frame&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;useSyncExternalStore(sub, snapshot)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Subscribe to an external store&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;useImperativeHandle(ref, factory, deps)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Expose an imperative handle to a parent’s &lt;code&gt;ref&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;useDeferredValue(value)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;A copy of &lt;code&gt;value&lt;/code&gt; that lags one commit&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;useDebouncedValue(value, delayMs)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;value&lt;/code&gt; after it stops changing for &lt;code&gt;delayMs&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;useThrottledValue(value, intervalMs)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;value&lt;/code&gt; at most once per &lt;code&gt;intervalMs&lt;/code&gt; (leading+trailing)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;useState&lt;/code&gt;‘s &lt;code&gt;initial&lt;/code&gt; is by-name (evaluated once), giving lazy initialization through the
same signature. Dependency arrays: &lt;code&gt;Array(a, b)&lt;/code&gt; re-runs on change, &lt;code&gt;Array()&lt;/code&gt; runs once,
&lt;code&gt;null&lt;/code&gt; runs every render. Effect cleanups return a function or &lt;code&gt;noCleanup&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DOM hooks&lt;/strong&gt; (built on the primitives, for common DOM patterns):&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Hook&lt;/th&gt;&lt;th&gt;Does&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;useEventListener(target, event, handler)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Subscribe to a DOM event for the lifetime&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;useClickOutside(ref, active, handler)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Fire when a press lands outside &lt;code&gt;ref&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;useMediaQuery(query)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Live &lt;code&gt;Boolean&lt;/code&gt; for a CSS media query&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;useIntersectionObserver(ref, …)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;Boolean&lt;/code&gt; viewport visibility (lazy load)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;See &lt;a href=&quot;/guide/hooks/&quot;&gt;Hooks&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;the-dsl&quot;&gt;The DSL&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Elements&lt;/strong&gt; — a function per HTML tag (&lt;code&gt;div&lt;/code&gt;, &lt;code&gt;p&lt;/code&gt;, &lt;code&gt;ul&lt;/code&gt;, &lt;code&gt;input&lt;/code&gt;, &lt;code&gt;button&lt;/code&gt;, &lt;code&gt;table&lt;/code&gt;, …)
plus namespaced SVG (&lt;code&gt;svg&lt;/code&gt;, &lt;code&gt;path&lt;/code&gt;, …).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Children&lt;/strong&gt; — &lt;code&gt;String&lt;/code&gt;, &lt;code&gt;Int&lt;/code&gt;, &lt;code&gt;VNode&lt;/code&gt;, &lt;code&gt;Seq[VNode]&lt;/code&gt;, and &lt;code&gt;Option[VNode]&lt;/code&gt; are all valid
children via automatic conversions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Attributes&lt;/strong&gt; — &lt;code&gt;AttrKey := value&lt;/code&gt; (&lt;code&gt;cls&lt;/code&gt;/&lt;code&gt;className&lt;/code&gt;, &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;href&lt;/code&gt;, &lt;code&gt;value&lt;/code&gt;, …);
&lt;code&gt;aria(name)&lt;/code&gt; / &lt;code&gt;data(name)&lt;/code&gt; for the long tail; &lt;code&gt;css(name -&amp;gt; value, …)&lt;/code&gt; for inline styles;
enumerated booleans render &lt;code&gt;&amp;quot;true&amp;quot;&lt;/code&gt;/&lt;code&gt;&amp;quot;false&amp;quot;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Events&lt;/strong&gt; — typed &lt;code&gt;EventKey := handler&lt;/code&gt; (&lt;code&gt;onClick&lt;/code&gt;, &lt;code&gt;onInput&lt;/code&gt;, &lt;code&gt;onKeyDown&lt;/code&gt;, …);
&lt;code&gt;on(name)&lt;/code&gt; for the rest; &lt;code&gt;targetValue(e)&lt;/code&gt; reads an input’s value. Chain &lt;code&gt;.capture&lt;/code&gt;,
&lt;code&gt;.once&lt;/code&gt;, and/or &lt;code&gt;.passive&lt;/code&gt; for listener options (&lt;code&gt;onScroll.passive := h&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Refs&lt;/strong&gt; — &lt;code&gt;ref := box&lt;/code&gt; binds an element to a &lt;code&gt;useRef&lt;/code&gt; box (or a callback); pass a
ref down as a prop to forward it, and &lt;code&gt;useImperativeHandle&lt;/code&gt; to expose a custom handle.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keys&lt;/strong&gt; — &lt;code&gt;key := id&lt;/code&gt; on an element (or a second arg to a component) for stable list
identity.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Conditionals&lt;/strong&gt; — &lt;code&gt;when(cond)(node)&lt;/code&gt;, &lt;code&gt;unless(cond)(node)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Escape hatches&lt;/strong&gt; — &lt;code&gt;unsafeHtml(s)&lt;/code&gt;, &lt;code&gt;portal(target, child)&lt;/code&gt;,
&lt;code&gt;errorBoundary(fallback)(child)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;context&quot;&gt;Context&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;createContext(default)&lt;/code&gt; makes a &lt;code&gt;Context[T]&lt;/code&gt;; &lt;code&gt;ctx.provide(value, child)&lt;/code&gt; scopes a value
to a subtree; &lt;code&gt;useContext(ctx)&lt;/code&gt; reads the nearest one.&lt;/p&gt;
&lt;h3 id=&quot;rendering&quot;&gt;Rendering&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;render(node, container)&lt;/code&gt; mounts a tree into a DOM element and returns a &lt;code&gt;Root&lt;/code&gt;. Calling
&lt;code&gt;render&lt;/code&gt; again reconciles; &lt;code&gt;root.unmount()&lt;/code&gt; tears down. &lt;code&gt;createRoot(container)&lt;/code&gt; gives you
the &lt;code&gt;Root&lt;/code&gt; without an initial render.&lt;/p&gt;
&lt;h2 id=&quot;riposte-atoms&quot;&gt;riposte-atoms&lt;/h2&gt;
&lt;p&gt;Atomic, identity-based shared state built on the core’s &lt;code&gt;useSyncExternalStore&lt;/code&gt; seam.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Create&lt;/strong&gt; — &lt;code&gt;atom(value)&lt;/code&gt; (primitive), &lt;code&gt;atom(get =&amp;gt; …)&lt;/code&gt; (derived, read-only),
&lt;code&gt;atom(read, write)&lt;/code&gt; (writable-derived), &lt;code&gt;action(write)&lt;/code&gt; (write-only command).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Utilities&lt;/strong&gt; — &lt;code&gt;selectAtom&lt;/code&gt;, &lt;code&gt;atomFamily&lt;/code&gt;, &lt;code&gt;atomWithStorage&lt;/code&gt;, &lt;code&gt;atomLoadable&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hooks&lt;/strong&gt; — &lt;code&gt;useAtom&lt;/code&gt; (read+write), &lt;code&gt;useAtomValue&lt;/code&gt; (read), &lt;code&gt;useSetAtom&lt;/code&gt; (write).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Store&lt;/strong&gt; — values live in a &lt;code&gt;Store&lt;/code&gt;; &lt;code&gt;Store.default&lt;/code&gt; is the global one, &lt;code&gt;new Store&lt;/code&gt;
makes a scoped one, &lt;code&gt;StoreProvider(store) { … }&lt;/code&gt; scopes it to a subtree.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See &lt;a href=&quot;/guide/state/&quot;&gt;State with Atoms&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;riposte-router&quot;&gt;riposte-router&lt;/h2&gt;
&lt;p&gt;Client-side routing built on the core’s public API.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Setup&lt;/strong&gt; — &lt;code&gt;Router(mode) { … }&lt;/code&gt;, &lt;code&gt;mode&lt;/code&gt; = &lt;code&gt;RouterMode.History&lt;/code&gt; (default) or
&lt;code&gt;RouterMode.Hash&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Routes&lt;/strong&gt; — &lt;code&gt;Routes(...)&lt;/code&gt;, &lt;code&gt;route(pattern)(view)&lt;/code&gt;, nested children via
&lt;code&gt;route(...)(view)(children…)&lt;/code&gt;, &lt;code&gt;index(view)&lt;/code&gt;, and &lt;code&gt;Outlet&lt;/code&gt; for the matched child.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Navigation&lt;/strong&gt; — &lt;code&gt;Link(to, children…)&lt;/code&gt;, &lt;code&gt;NavLink(to, activeClass, end)(children…)&lt;/code&gt;,
&lt;code&gt;navigate(to, replace)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Params &amp;amp; query&lt;/strong&gt; — &lt;code&gt;useParams()&lt;/code&gt; (a &lt;code&gt;Map&lt;/code&gt;), &lt;code&gt;useSearchParams()&lt;/code&gt; → &lt;code&gt;(params, set)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Advanced&lt;/strong&gt; — &lt;code&gt;route(...).catchErrors(fallback)&lt;/code&gt;, &lt;code&gt;lazyView(load, fallback)&lt;/code&gt;,
&lt;code&gt;ScrollRestoration()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See &lt;a href=&quot;/guide/routing/&quot;&gt;Routing&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;riposte-salle&quot;&gt;riposte-salle&lt;/h2&gt;
&lt;p&gt;A styled component library on top of the core.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Skin system&lt;/strong&gt; — components express intent (&lt;code&gt;Color&lt;/code&gt;, &lt;code&gt;Size&lt;/code&gt;, per-component variant
enums like &lt;code&gt;ButtonVariant&lt;/code&gt;); a &lt;code&gt;Skin&lt;/code&gt; maps that to CSS classes. Ships &lt;code&gt;SalleSkin&lt;/code&gt;
(default, styled by &lt;code&gt;salle.css&lt;/code&gt;) and &lt;code&gt;DaisySkin&lt;/code&gt; (DaisyUI vocabulary). Set one with
&lt;code&gt;SkinProvider(skin) { … }&lt;/code&gt;; read it with &lt;code&gt;useSkin()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Theming&lt;/strong&gt; — one open &lt;code&gt;data-theme&lt;/code&gt; set (ships &lt;code&gt;light&lt;/code&gt;/&lt;code&gt;dark&lt;/code&gt;, &lt;code&gt;system&lt;/code&gt; follows the OS).
&lt;code&gt;useTheme()&lt;/code&gt; → &lt;code&gt;(theme, resolved, setTheme, toggle)&lt;/code&gt;; plus &lt;code&gt;ThemeToggle&lt;/code&gt; (sun/moon icon
button) and &lt;code&gt;ThemeSelect(themes)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Components&lt;/strong&gt; — &lt;code&gt;Button&lt;/code&gt;, &lt;code&gt;Input&lt;/code&gt;, &lt;code&gt;Checkbox&lt;/code&gt;, &lt;code&gt;Toggle&lt;/code&gt;, &lt;code&gt;Select&lt;/code&gt; (&lt;code&gt;Opt&lt;/code&gt;), &lt;code&gt;ImageCard&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Layout&lt;/strong&gt; — &lt;code&gt;Row&lt;/code&gt;/&lt;code&gt;Col&lt;/code&gt; (24-column grid, responsive spans) and &lt;code&gt;Masonry&lt;/code&gt; /
&lt;code&gt;MasonryResponsive&lt;/code&gt; (packed columns; pure &lt;code&gt;layoutMasonry&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hook&lt;/strong&gt; — &lt;code&gt;useControllable(value, default, onChange)&lt;/code&gt; for controlled/uncontrolled state.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See &lt;a href=&quot;/guide/salle/&quot;&gt;Component library (salle)&lt;/a&gt;.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Installation</title>
    <link href="https://riposte.edadma.dev/getting-started/installation/"/>
    <id>https://riposte.edadma.dev/getting-started/installation/</id>
    <updated>2026-05-31T15:40:05.566249418Z</updated>
    <summary>Riposte targets Scala.js. You need an sbt project with the Scala.js plugin enabled.</summary>
    <content type="html">&lt;p&gt;Riposte targets Scala.js. You need an sbt project with the Scala.js plugin enabled.&lt;/p&gt;
&lt;h2 id=&quot;requirements&quot;&gt;Requirements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Scala 3&lt;/li&gt;
&lt;li&gt;sbt with the &lt;code&gt;sbt-scalajs&lt;/code&gt; plugin&lt;/li&gt;
&lt;li&gt;Node.js (for running and testing against a DOM via jsdom)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;add-the-plugin&quot;&gt;Add the plugin&lt;/h2&gt;
&lt;p&gt;In &lt;code&gt;project/plugins.sbt&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;addSbtPlugin&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;org.scala-js&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;sbt-scalajs&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;1.16.0&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;add-the-dependency&quot;&gt;Add the dependency&lt;/h2&gt;
&lt;p&gt;Riposte is published for Scala.js under the &lt;code&gt;io.github.edadma&lt;/code&gt; organization. Enable the
Scala.js plugin on your module and add the core library:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;lazy&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; app &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; project
  &lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;in&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;app&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;enablePlugins&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;ScalaJSPlugin&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;settings&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
    libraryDependencies &lt;span class=&quot;hl-keyword&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;io.github.edadma&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;%%%&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;riposte&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;0.0.2&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The optional sibling artifacts are added the same way when you need them:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;libraryDependencies &lt;span class=&quot;hl-keyword&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;io.github.edadma&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;%%%&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;riposte-atoms&amp;quot;&lt;/span&gt;  &lt;span class=&quot;hl-keyword&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;0.0.2&amp;quot;&lt;/span&gt;
libraryDependencies &lt;span class=&quot;hl-keyword&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;io.github.edadma&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;%%%&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;riposte-router&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;0.0.2&amp;quot;&lt;/span&gt;
libraryDependencies &lt;span class=&quot;hl-keyword&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;io.github.edadma&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;%%%&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;riposte-salle&amp;quot;&lt;/span&gt;  &lt;span class=&quot;hl-keyword&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;0.0.2&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All three depend on &lt;code&gt;riposte&lt;/code&gt; transitively, so you do not need to list the core separately.
(&lt;a href=&quot;/guide/salle/&quot;&gt;salle&lt;/a&gt; is the styled component library; it also ships a &lt;code&gt;salle.css&lt;/code&gt; you
include in your page.)&lt;/p&gt;
&lt;h2 id=&quot;a-dom-to-render-into&quot;&gt;A DOM to render into&lt;/h2&gt;
&lt;p&gt;Riposte renders into a real DOM node. In the browser that is an element on the page; in
tests it is a &lt;a href=&quot;https://github.com/jsdom/jsdom&quot;&gt;jsdom&lt;/a&gt; document. Configure the jsdom
test environment in your build so tests have a DOM:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;import&lt;/span&gt; org&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;scalajs&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;jsenv&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;jsdomnodejs&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;JSDOMNodeJSEnv&lt;/span&gt;

&lt;span class=&quot;hl-type&quot;&gt;Test&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;/&lt;/span&gt; jsEnv &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;JSDOMNodeJSEnv&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Continue to the &lt;a href=&quot;/getting-started/quick-start/&quot;&gt;quick start&lt;/a&gt; to mount a component.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Hooks</title>
    <link href="https://riposte.edadma.dev/guide/hooks/"/>
    <id>https://riposte.edadma.dev/guide/hooks/</id>
    <updated>2026-05-31T15:40:05.566249418Z</updated>
    <summary>Hooks give a component local state, side effects, memoized values, and access to the DOM. They’re available through a Hooks context that Riposte supplies while rendering — which is exactly…</summary>
    <content type="html">&lt;p&gt;Hooks give a component local state, side effects, memoized values, and access to the DOM.
They’re available through a &lt;code&gt;Hooks&lt;/code&gt; context that Riposte supplies while rendering — which
is exactly what the component constructors (&lt;code&gt;view&lt;/code&gt;, &lt;code&gt;component&lt;/code&gt;, &lt;code&gt;container&lt;/code&gt;) set up. Call
hooks at the top level of a component body and you never pass the context explicitly:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;import&lt;/span&gt; io&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;github&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;edadma&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;riposte&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;*&lt;/span&gt;

&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Greeting&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; view &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; setName&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; _&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useState&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;world&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  div&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
    input&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; name&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; onInput &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;e &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; setName&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;targetValue&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;hl-punctuation&quot;&gt;)))),&lt;/span&gt;
    p&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Hello, $name!&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The same is true inside &lt;code&gt;component[P] { props =&amp;gt; … }&lt;/code&gt; and &lt;code&gt;container { children =&amp;gt; … }&lt;/code&gt; —
all three run their body with the &lt;code&gt;Hooks&lt;/code&gt; context in scope.&lt;/p&gt;
&lt;h2 id=&quot;the-rules-of-hooks&quot;&gt;The rules of hooks&lt;/h2&gt;
&lt;p&gt;Hooks are &lt;strong&gt;positional&lt;/strong&gt;: Riposte identifies each one by its call order within a component,
not by name. Two rules follow, and they’re the same as React’s:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Call hooks unconditionally, in the same order every render.&lt;/strong&gt; Never put a hook inside
an &lt;code&gt;if&lt;/code&gt;, a loop, or a nested function — that would shift the positions on some renders
and scramble the state. Put the condition &lt;em&gt;inside&lt;/em&gt; the hook instead.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Call hooks only from a component body&lt;/strong&gt; (or from another hook), not from ordinary
functions.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-comment&quot;&gt;// Wrong — the hook is conditional:&lt;/span&gt;
&lt;span class=&quot;hl-keyword&quot;&gt;if&lt;/span&gt; loggedIn &lt;span class=&quot;hl-keyword&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; setX&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; _&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useState&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;hl-comment&quot;&gt;// Right — the hook always runs; the condition lives in how you use it:&lt;/span&gt;
&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; setX&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; _&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useState&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-keyword&quot;&gt;if&lt;/span&gt; loggedIn &lt;span class=&quot;hl-keyword&quot;&gt;then&lt;/span&gt; …use x…&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;usestate&quot;&gt;useState&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;useState(initial)&lt;/code&gt; returns a triple: the current value, a &lt;code&gt;set&lt;/code&gt; that replaces it, and an
&lt;code&gt;update&lt;/code&gt; that derives the next value from the previous one.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;count&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; set&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; update&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useState&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;

button&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;onClick &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;_ &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; update&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;_ &lt;span class=&quot;hl-keyword&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)),&lt;/span&gt; s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Count: $count&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;   &lt;span class=&quot;hl-comment&quot;&gt;// increment&lt;/span&gt;
button&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;onClick &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;_ &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; set&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)),&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Reset&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;                   &lt;span class=&quot;hl-comment&quot;&gt;// replace&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reach for &lt;code&gt;update&lt;/code&gt; whenever the next value depends on the current one — it composes
correctly when several updates happen in the same event, where repeated &lt;code&gt;set(count + 1)&lt;/code&gt;
calls would all see the same stale &lt;code&gt;count&lt;/code&gt;. Setters schedule a re-render on the microtask
queue, so multiple updates in one event handler are batched into a single render.&lt;/p&gt;
&lt;p&gt;The state can be any type — a &lt;code&gt;Vector&lt;/code&gt;, a case class, whatever:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;items&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; setItems&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; updateItems&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useState&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Vector&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;empty&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;])&lt;/span&gt;
updateItems&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;_ &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;new&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;initial&lt;/code&gt; argument is &lt;strong&gt;by-name&lt;/strong&gt;: it’s evaluated only on the first render, when the
state cell is created, and never again. So an allocating initializer you don’t want re-run
each render just works through the same signature — &lt;code&gt;useState(new Buffer)&lt;/code&gt; builds the
buffer once, not on every render. No separate “lazy initializer” form is needed.&lt;/p&gt;
&lt;h2 id=&quot;usereducer&quot;&gt;useReducer&lt;/h2&gt;
&lt;p&gt;For state with several distinct transitions, &lt;code&gt;useReducer&lt;/code&gt; centralizes them in one
function. It returns the current state and a &lt;code&gt;dispatch&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Increment&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Decrement&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Reset&lt;/span&gt;

&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;count&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; dispatch&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useReducer&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;](&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; action&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; action &lt;span class=&quot;hl-keyword&quot;&gt;match&lt;/span&gt;
    &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Increment&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; state &lt;span class=&quot;hl-keyword&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Decrement&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; state &lt;span class=&quot;hl-keyword&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Reset&lt;/span&gt;     &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;hl-number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;

button&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;onClick &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;_ &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; dispatch&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Increment&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)),&lt;/span&gt; s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Count: $count&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;useeffect&quot;&gt;useEffect&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;useEffect(body, deps)&lt;/code&gt; runs a side effect &lt;em&gt;after&lt;/em&gt; the DOM has been updated and the
browser has painted (it runs on the macrotask queue, so it never blocks a frame). Use it
for anything outside the render-to-DOM flow: network requests, subscriptions, timers,
manual DOM measurement, logging.&lt;/p&gt;
&lt;p&gt;The body returns a &lt;strong&gt;cleanup&lt;/strong&gt; — either a function that undoes the effect, or &lt;code&gt;noCleanup&lt;/code&gt;
when there’s nothing to tear down. Riposte runs the cleanup before the effect re-runs and
once more on unmount:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;useEffect&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; id &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; dom&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;window&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;setInterval&lt;span class=&quot;hl-punctuation&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; tick&lt;span class=&quot;hl-punctuation&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; dom&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;window&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;clearInterval&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;   &lt;span class=&quot;hl-comment&quot;&gt;// cleanup&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;hl-type&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(),&lt;/span&gt;                                &lt;span class=&quot;hl-comment&quot;&gt;// deps&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;strong&gt;dependency array&lt;/strong&gt; controls when the effect re-runs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Array(a, b)&lt;/code&gt; — re-run only when &lt;code&gt;a&lt;/code&gt; or &lt;code&gt;b&lt;/code&gt; changed since the last render.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Array()&lt;/code&gt; (empty) — run once, after the first mount, and clean up on unmount.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;null&lt;/code&gt; — run after &lt;em&gt;every&lt;/em&gt; render.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;useEffect&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt; dom&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;document&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;title &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Count: $count&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;;&lt;/span&gt; noCleanup &lt;span class=&quot;hl-punctuation&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;hl-type&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;count&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;useLayoutEffect&lt;/code&gt; has the same signature but runs &lt;strong&gt;synchronously after DOM mutations and
before paint&lt;/strong&gt; — use it (sparingly) when you must read layout and re-mutate the DOM
without the user seeing an intermediate frame.&lt;/p&gt;
&lt;h2 id=&quot;useref&quot;&gt;useRef&lt;/h2&gt;
&lt;p&gt;A ref is a mutable box whose &lt;code&gt;.current&lt;/code&gt; survives across renders without causing a
re-render when you change it. Two uses:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A handle to a DOM node.&lt;/strong&gt; Bind it with the &lt;code&gt;ref&lt;/code&gt; attribute, then reach for the node
imperatively — the thing state can’t do:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; inputRef &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useRef&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;dom&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;html&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Input&lt;/span&gt; | &lt;span class=&quot;hl-type&quot;&gt;Null&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;hl-variable&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;

div&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  input&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;ref &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; inputRef&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; placeholder &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;press focus →&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  button&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
    onClick &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt; _ &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; node &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; inputRef&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;current
      &lt;span class=&quot;hl-keyword&quot;&gt;if&lt;/span&gt; node &lt;span class=&quot;hl-keyword&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;hl-variable&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;then&lt;/span&gt; node&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;focus&lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;hl-punctuation&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;focus&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;ref&lt;/code&gt; also accepts a callback — &lt;code&gt;ref := (node =&amp;gt; …)&lt;/code&gt; — invoked with the node on mount and
&lt;code&gt;null&lt;/code&gt; on unmount, for when you need to react to attach/detach.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mutable instance state that isn’t UI.&lt;/strong&gt; A ref is the place for a value you want to
remember but that shouldn’t trigger rendering — a previous value, a timer id, a flag.&lt;/p&gt;
&lt;h2 id=&quot;usememo-and-usecallback&quot;&gt;useMemo and useCallback&lt;/h2&gt;
&lt;p&gt;Both cache something across renders, keyed on a dependency array, to avoid redundant work.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;useMemo(compute, deps)&lt;/code&gt; caches the &lt;em&gt;result&lt;/em&gt; of an expensive computation, recomputing only
when a dependency changes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; sorted &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useMemo&lt;span class=&quot;hl-punctuation&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; items&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;sortBy&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;_&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;items&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;useCallback(fn, deps)&lt;/code&gt; caches a &lt;em&gt;function value&lt;/em&gt; so its identity is stable across renders
— important when you pass a handler to a &lt;code&gt;memo&lt;/code&gt;-ized child (an inline lambda would be a new
object each render and defeat the memoization) or use it as an effect dependency:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; onSelect &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useCallback&lt;span class=&quot;hl-punctuation&quot;&gt;((&lt;/span&gt;id&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; setSelected&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;hl-type&quot;&gt;Row&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;rowProps&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; onSelect&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;   &lt;span class=&quot;hl-comment&quot;&gt;// Row can now actually bail out&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;useid&quot;&gt;useId&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;useId()&lt;/code&gt; returns a stable, unique string id — the same value across re-renders of that
instance. Use it to wire an input to its label without hand-managing globally unique ids:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; id &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useId&lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt;
label&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;htmlFor &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; id&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Email&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
input&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;id &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; id&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; `&lt;span class=&quot;hl-keyword&quot;&gt;type&lt;/span&gt;` &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;email&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;usecontext&quot;&gt;useContext&lt;/h2&gt;
&lt;p&gt;Context passes a value down to descendants without threading it through every component’s
props. Create a context with a default, provide a value to a subtree, and read the nearest
provided value with &lt;code&gt;useContext&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;ThemeContext&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; createContext&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;light&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Themed&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; view &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; theme &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useContext&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;ThemeContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  div&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;cls &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;theme-$theme&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;…&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;hl-comment&quot;&gt;// Provide a value to a subtree:&lt;/span&gt;
&lt;span class=&quot;hl-type&quot;&gt;ThemeContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;provide&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;dark&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Themed&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;())&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reading a context subscribes the component to it: when a provider higher up supplies a new
value, every descendant that reads the context re-renders. The &lt;a href=&quot;/guide/state/&quot;&gt;atoms&lt;/a&gt; and
&lt;a href=&quot;/guide/routing/&quot;&gt;router&lt;/a&gt; modules are both built on context.&lt;/p&gt;
&lt;h2 id=&quot;usetransition&quot;&gt;useTransition&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;useTransition(target, durationMs)&lt;/code&gt; eases a &lt;code&gt;Double&lt;/code&gt; toward &lt;code&gt;target&lt;/code&gt; over &lt;code&gt;durationMs&lt;/code&gt;,
re-rendering the component each animation frame until it settles. It’s a self-driving
animation primitive for simple numeric transitions — progress bars, fades, slides:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;open&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; _&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; toggle&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useState&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-variable&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; width &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useTransition&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;if&lt;/span&gt; open &lt;span class=&quot;hl-keyword&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;100.0&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;

div&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  css&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;width&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;-&amp;gt;&lt;/span&gt; s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;$width%&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;height&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;0.5rem&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;background&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;#0c8599&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;hl-comment&quot;&gt;// a button elsewhere calls toggle(o =&amp;gt; !o)&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;usesyncexternalstore&quot;&gt;useSyncExternalStore&lt;/h2&gt;
&lt;p&gt;The low-level seam for subscribing a component to an external, mutable data source. You
give it a &lt;code&gt;subscribe&lt;/code&gt; function (called with a callback to run on change; returns an
unsubscribe) and a &lt;code&gt;getSnapshot&lt;/code&gt; that reads the current value:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; value &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useSyncExternalStore&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  subscribe   &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; cb &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt; source&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;addListener&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;cb&lt;span class=&quot;hl-punctuation&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; source&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;removeListener&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;cb&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;},&lt;/span&gt;
  getSnapshot &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; source&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;current&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You rarely call this directly — it’s the foundation the &lt;a href=&quot;/guide/state/&quot;&gt;atoms&lt;/a&gt; module and
the router’s location tracking are built on. Reach for those higher-level APIs first;
&lt;code&gt;useSyncExternalStore&lt;/code&gt; is here for integrating a store Riposte doesn’t already wrap.&lt;/p&gt;
&lt;h2 id=&quot;dom-hooks&quot;&gt;DOM hooks&lt;/h2&gt;
&lt;p&gt;A handful of higher-level hooks wrap common DOM patterns so you don’t re-implement the
&lt;code&gt;addEventListener&lt;/code&gt;/&lt;code&gt;removeEventListener&lt;/code&gt; bookkeeping each time. They’re built on the
primitives above and live in the core alongside them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;useEventListener(target, event, handler)&lt;/code&gt;&lt;/strong&gt; attaches &lt;code&gt;handler&lt;/code&gt; for &lt;code&gt;event&lt;/code&gt; on &lt;code&gt;target&lt;/code&gt;
for the component’s lifetime, removing it on unmount. The latest &lt;code&gt;handler&lt;/code&gt; is always used
(it’s kept in a ref), so a handler closing over changing state stays current without
re-subscribing every render:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;useEventListener&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;dom&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;window&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;resize&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; _ &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; recompute&lt;span class=&quot;hl-punctuation&quot;&gt;())&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;An overload takes &lt;code&gt;EventListenerOptions(capture, passive, once)&lt;/code&gt;, mirroring the DOM’s
&lt;code&gt;addEventListener&lt;/code&gt; options — e.g. &lt;code&gt;passive = true&lt;/code&gt; to promise the handler won’t
&lt;code&gt;preventDefault&lt;/code&gt; (so the browser can scroll without waiting):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;useEventListener&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;dom&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;window&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;scroll&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; onScroll&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;EventListenerOptions&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;passive &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-variable&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;useClickOutside(ref, active, handler)&lt;/code&gt;&lt;/strong&gt; calls &lt;code&gt;handler&lt;/code&gt; when a pointer press lands
outside the element held by &lt;code&gt;ref&lt;/code&gt;, but only while &lt;code&gt;active&lt;/code&gt; is true — the robust way to
dismiss an open popup or menu (a real document &lt;code&gt;pointerdown&lt;/code&gt; listener checking containment,
not a focus-blur race):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; box &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useRef&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;dom&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Element&lt;/span&gt; | &lt;span class=&quot;hl-type&quot;&gt;Null&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;hl-variable&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
useClickOutside&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;box&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; menuOpen&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; setMenuOpen&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-variable&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt;
div&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;ref &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; box&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-comment&quot;&gt;/* … the menu … */&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;useMediaQuery(query)&lt;/code&gt;&lt;/strong&gt; returns a &lt;code&gt;Boolean&lt;/code&gt; that tracks a CSS media query live —
re-rendering when it starts or stops matching:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; isWide &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useMediaQuery&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;(min-width: 768px)&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; prefersDark &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useMediaQuery&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;(prefers-color-scheme: dark)&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;useIntersectionObserver(ref, rootMargin, threshold, once)&lt;/code&gt;&lt;/strong&gt; reports whether the
element held by &lt;code&gt;ref&lt;/code&gt; is in (or near) the viewport — for lazy loading and infinite scroll.
It returns a &lt;code&gt;Boolean&lt;/code&gt; that flips to &lt;code&gt;true&lt;/code&gt; when the element intersects the viewport
(expanded by &lt;code&gt;rootMargin&lt;/code&gt;, default &lt;code&gt;&amp;quot;200px&amp;quot;&lt;/code&gt;, so work can start just before it scrolls in).
With &lt;code&gt;once = true&lt;/code&gt; (the default) it latches and disconnects — right for lazy-loading an
image; with &lt;code&gt;once = false&lt;/code&gt; it tracks visibility both ways. Where &lt;code&gt;IntersectionObserver&lt;/code&gt;
isn’t available it returns &lt;code&gt;true&lt;/code&gt;, so dependent content still loads:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; tile &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useRef&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;dom&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Element&lt;/span&gt; | &lt;span class=&quot;hl-type&quot;&gt;Null&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;hl-variable&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; visible &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useIntersectionObserver&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;tile&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
div&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;ref &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; tile&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; when&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;visible&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;img&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;src &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; heavyImageUrl&lt;span class=&quot;hl-punctuation&quot;&gt;)))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(salle’s &lt;a href=&quot;/guide/salle/&quot;&gt;&lt;code&gt;ImageCard&lt;/code&gt;&lt;/a&gt; is built on this hook.)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;useResizeObserver(ref, onResize)&lt;/code&gt;&lt;/strong&gt; calls &lt;code&gt;onResize&lt;/code&gt; whenever the element held by &lt;code&gt;ref&lt;/code&gt;
changes size — the building block for any layout that recomputes when its &lt;em&gt;container&lt;/em&gt;
resizes (which a &lt;code&gt;window&lt;/code&gt; &lt;code&gt;resize&lt;/code&gt; listener misses: a sidebar opening, a flex sibling
growing). Where &lt;code&gt;ResizeObserver&lt;/code&gt; is unavailable it falls back to a &lt;code&gt;window&lt;/code&gt; listener:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; box &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useRef&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;dom&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Element&lt;/span&gt; | &lt;span class=&quot;hl-type&quot;&gt;Null&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;hl-variable&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
useResizeObserver&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;box&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; remeasure&lt;span class=&quot;hl-punctuation&quot;&gt;())&lt;/span&gt;
div&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;ref &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; box&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-comment&quot;&gt;/* … */&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(salle’s &lt;a href=&quot;/guide/salle/&quot;&gt;&lt;code&gt;Masonry&lt;/code&gt;&lt;/a&gt; measures with this hook.)&lt;/p&gt;
&lt;h2 id=&quot;rate-limiting-a-value&quot;&gt;Rate-limiting a value&lt;/h2&gt;
&lt;p&gt;Three hooks derive a slower-changing copy of a fast-changing value, so expensive renders
downstream don’t fire on every keystroke or scroll tick. Each returns a value of the same
type:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;useDeferredValue(value)&lt;/code&gt;&lt;/strong&gt; lets the returned value &lt;em&gt;lag&lt;/em&gt; the input — it catches up on
a later macrotask when the main thread is idle, and a burst of updates collapses to the
last one. For a search box driving a heavy results list, where you want the input itself
to stay responsive.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;useDebouncedValue(value, delayMs)&lt;/code&gt;&lt;/strong&gt; updates only after &lt;code&gt;delayMs&lt;/code&gt; of quiet — each new
value resets the timer. For a query that should fire only once typing pauses.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;useThrottledValue(value, intervalMs)&lt;/code&gt;&lt;/strong&gt; updates at most once per &lt;code&gt;intervalMs&lt;/code&gt;, always
delivering the trailing value. For scroll/resize-driven state.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;query&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; setQuery&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; _&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useState&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; debounced &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useDebouncedValue&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;query&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;// …a search effect keyed on `debounced`, not `query`&lt;/span&gt;
useEffect&lt;span class=&quot;hl-punctuation&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt; search&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;debounced&lt;span class=&quot;hl-punctuation&quot;&gt;);&lt;/span&gt; noCleanup &lt;span class=&quot;hl-punctuation&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;debounced&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;useimperativehandle&quot;&gt;useImperativeHandle&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;useImperativeHandle(ref, create, deps)&lt;/code&gt; populates a &lt;code&gt;ref&lt;/code&gt; with a custom handle (rather
than the raw DOM node), so a parent can call methods a child exposes — &lt;code&gt;focus()&lt;/code&gt;, &lt;code&gt;scroll&lt;/code&gt;,
imperative form APIs. The handle is built after commit and rebuilt when &lt;code&gt;deps&lt;/code&gt; change, and
cleared on unmount so a parent never calls into an unmounted child:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-comment&quot;&gt;// In the child, given a `ref: Ref[Controls | Null]` passed down as a prop:&lt;/span&gt;
useImperativeHandle&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;ref&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Controls&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; focus&lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; inputRef&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;current&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;focus&lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;())&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reach for it sparingly — most parent→child communication should be props, not method calls.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Components &amp; the DSL</title>
    <link href="https://riposte.edadma.dev/guide/components/"/>
    <id>https://riposte.edadma.dev/guide/components/</id>
    <updated>2026-05-31T15:40:05.566249418Z</updated>
    <summary>A Riposte UI is an immutable tree of VNodes. You don’t build that tree by hand — you describe it with the DSL (element functions, attribute keys, event keys) and…</summary>
    <content type="html">&lt;p&gt;A Riposte UI is an immutable tree of &lt;code&gt;VNode&lt;/code&gt;s. You don’t build that tree by hand — you
describe it with the &lt;strong&gt;DSL&lt;/strong&gt; (element functions, attribute keys, event keys) and package
reusable pieces of it as &lt;strong&gt;components&lt;/strong&gt;. This page covers both: first how to describe
markup, then how to factor it into components that take props and children.&lt;/p&gt;
&lt;h2 id=&quot;elements&quot;&gt;Elements&lt;/h2&gt;
&lt;p&gt;Every HTML element has a function of the same name. Each takes a varargs list of
&lt;em&gt;modifiers&lt;/em&gt; — attributes, event handlers, and children, in any order:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;import&lt;/span&gt; io&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;github&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;edadma&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;riposte&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;*&lt;/span&gt;

div&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  cls &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;card&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
  h2&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Riposte&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  p&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;A React-inspired library for Scala.js.&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The element functions cover the everyday HTML vocabulary — sectioning (&lt;code&gt;section&lt;/code&gt;,
&lt;code&gt;article&lt;/code&gt;, &lt;code&gt;nav&lt;/code&gt;, &lt;code&gt;header&lt;/code&gt;, &lt;code&gt;footer&lt;/code&gt;, &lt;code&gt;main&lt;/code&gt;, &lt;code&gt;aside&lt;/code&gt;), grouping (&lt;code&gt;div&lt;/code&gt;, &lt;code&gt;p&lt;/code&gt;, &lt;code&gt;ul&lt;/code&gt;, &lt;code&gt;ol&lt;/code&gt;,
&lt;code&gt;li&lt;/code&gt;, &lt;code&gt;pre&lt;/code&gt;, &lt;code&gt;blockquote&lt;/code&gt;, &lt;code&gt;figure&lt;/code&gt;), text (&lt;code&gt;span&lt;/code&gt;, &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;strong&lt;/code&gt;, &lt;code&gt;em&lt;/code&gt;, &lt;code&gt;code&lt;/code&gt;, &lt;code&gt;small&lt;/code&gt;,
&lt;code&gt;mark&lt;/code&gt;), forms (&lt;code&gt;form&lt;/code&gt;, &lt;code&gt;input&lt;/code&gt;, &lt;code&gt;textarea&lt;/code&gt;, &lt;code&gt;select&lt;/code&gt;, &lt;code&gt;option&lt;/code&gt;, &lt;code&gt;button&lt;/code&gt;, &lt;code&gt;label&lt;/code&gt;),
tables (&lt;code&gt;table&lt;/code&gt;, &lt;code&gt;thead&lt;/code&gt;, &lt;code&gt;tbody&lt;/code&gt;, &lt;code&gt;tr&lt;/code&gt;, &lt;code&gt;th&lt;/code&gt;, &lt;code&gt;td&lt;/code&gt;), and media (&lt;code&gt;img&lt;/code&gt;, &lt;code&gt;video&lt;/code&gt;, &lt;code&gt;audio&lt;/code&gt;,
&lt;code&gt;source&lt;/code&gt;). SVG elements (&lt;code&gt;svg&lt;/code&gt;, &lt;code&gt;path&lt;/code&gt;, &lt;code&gt;circle&lt;/code&gt;, &lt;code&gt;g&lt;/code&gt;, …) are there too and are namespaced
automatically.&lt;/p&gt;
&lt;h3 id=&quot;children&quot;&gt;Children&lt;/h3&gt;
&lt;p&gt;A child can be another element, a &lt;code&gt;String&lt;/code&gt; (becomes a text node), or an &lt;code&gt;Int&lt;/code&gt; (rendered
as its decimal text). These conversions are automatic, so you can mix them freely:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;p&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;You have &amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; strong&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;count&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot; unread messages.&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A &lt;code&gt;Seq[VNode]&lt;/code&gt; is spliced in as a run of children — the idiom for rendering a list from
data:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;ul&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  items&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;map&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;item &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; li&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;An &lt;code&gt;Option[VNode]&lt;/code&gt; is also a valid child: &lt;code&gt;Some(node)&lt;/code&gt; renders the node, &lt;code&gt;None&lt;/code&gt; renders an
empty placeholder (not nothing) so that toggling between the two keeps the surrounding
siblings — and their state — in stable positions.&lt;/p&gt;
&lt;h2 id=&quot;attributes&quot;&gt;Attributes&lt;/h2&gt;
&lt;p&gt;Attributes are &lt;code&gt;AttrKey&lt;/code&gt; values combined with a value through &lt;code&gt;:=&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;input&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  cls         &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;field&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
  id          &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;email&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
  value       &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; text&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
  placeholder &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;you@example.com&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
  disabled    &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; submitting&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;        &lt;span class=&quot;hl-comment&quot;&gt;// Boolean&lt;/span&gt;
  tabIndex    &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;                  &lt;span class=&quot;hl-comment&quot;&gt;// rendered as text&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;:=&lt;/code&gt; accepts a &lt;code&gt;String&lt;/code&gt;, a &lt;code&gt;Boolean&lt;/code&gt;, or a &lt;code&gt;Double&lt;/code&gt;. &lt;code&gt;cls&lt;/code&gt; (also available as
&lt;code&gt;className&lt;/code&gt;), &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;href&lt;/code&gt;, &lt;code&gt;src&lt;/code&gt;, &lt;code&gt;value&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;type&lt;/code&gt;, &lt;code&gt;placeholder&lt;/code&gt;, &lt;code&gt;title&lt;/code&gt;, and the
rest of the common attributes are provided as keys.&lt;/p&gt;
&lt;p&gt;For attributes without a dedicated key, two families of helpers cover the long tail:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;div&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  aria&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;label&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Close&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;        &lt;span class=&quot;hl-comment&quot;&gt;// aria-label=&amp;quot;Close&amp;quot;&lt;/span&gt;
  data&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;user-id&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; userId&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;       &lt;span class=&quot;hl-comment&quot;&gt;// data-user-id=&amp;quot;…&amp;quot;&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Enumerated booleans — &lt;code&gt;draggable&lt;/code&gt;, &lt;code&gt;spellcheck&lt;/code&gt;, &lt;code&gt;contenteditable&lt;/code&gt;, and the ARIA states —
render the literal strings &lt;code&gt;&amp;quot;true&amp;quot;&lt;/code&gt; / &lt;code&gt;&amp;quot;false&amp;quot;&lt;/code&gt; rather than toggling by presence, which is
what those attributes actually require.&lt;/p&gt;
&lt;h3 id=&quot;inline-styles&quot;&gt;Inline styles&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;css&lt;/code&gt; takes &lt;code&gt;name -&amp;gt; value&lt;/code&gt; pairs and sets the &lt;code&gt;style&lt;/code&gt; attribute:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;div&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  css&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;background&amp;quot;&lt;/span&gt;    &lt;span class=&quot;hl-keyword&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;#0c8599&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;height&amp;quot;&lt;/span&gt;        &lt;span class=&quot;hl-keyword&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;0.75rem&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;border-radius&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;4px&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;width&amp;quot;&lt;/span&gt;         &lt;span class=&quot;hl-keyword&quot;&gt;-&amp;gt;&lt;/span&gt; s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;$pct%&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;events&quot;&gt;Events&lt;/h2&gt;
&lt;p&gt;Event handlers are &lt;code&gt;EventKey&lt;/code&gt; values, also combined with &lt;code&gt;:=&lt;/code&gt;. Each key is typed by the
event it delivers, so the handler parameter needs no annotation — &lt;code&gt;onClick&lt;/code&gt; hands you a
&lt;code&gt;dom.MouseEvent&lt;/code&gt;, &lt;code&gt;onKeyDown&lt;/code&gt; a &lt;code&gt;dom.KeyboardEvent&lt;/code&gt;, &lt;code&gt;onInput&lt;/code&gt; a &lt;code&gt;dom.Event&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;button&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;onClick &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;e &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;clicked at ${e.clientX}, ${e.clientY}&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)),&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Click me&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;

input&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;onKeyDown &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;e &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;if&lt;/span&gt; e&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;key &lt;span class=&quot;hl-keyword&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Enter&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;then&lt;/span&gt; submit&lt;span class=&quot;hl-punctuation&quot;&gt;()))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The full set covers mouse, pointer, keyboard, focus, drag, touch, and clipboard events.
For an event without a dedicated key, &lt;code&gt;on(name)&lt;/code&gt; gives you a handler typed as the base
&lt;code&gt;dom.Event&lt;/code&gt;. Reading the current value of an &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; is common enough to have a helper:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;input&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; draft&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; onInput &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;e &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; setDraft&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;targetValue&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;hl-punctuation&quot;&gt;))))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;components&quot;&gt;Components&lt;/h2&gt;
&lt;p&gt;A component is a reusable, named piece of UI. It’s an ordinary &lt;code&gt;val&lt;/code&gt;, built with one of
three constructors depending on what it accepts. You &lt;em&gt;call&lt;/em&gt; it — &lt;code&gt;Counter()&lt;/code&gt;,
&lt;code&gt;Stat(&amp;quot;Clicks&amp;quot;, n)&lt;/code&gt; — wherever a child is expected, and the result is a &lt;code&gt;VNode&lt;/code&gt; like any
other.&lt;/p&gt;
&lt;p&gt;Components matter for more than reuse: the reconciler tracks each mounted component by
identity, so hook state (see &lt;a href=&quot;/guide/hooks/&quot;&gt;Hooks&lt;/a&gt;) lives on the instance and survives
re-renders, and only the components whose inputs changed re-render.&lt;/p&gt;
&lt;h3 id=&quot;view-no-props&quot;&gt;&lt;code&gt;view&lt;/code&gt; — no props&lt;/h3&gt;
&lt;p&gt;The simplest component takes nothing. Build it with &lt;code&gt;view { … }&lt;/code&gt;; the block is the render
body, and hooks work inside it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Counter&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; view &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;count&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; _&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; update&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; useState&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  button&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;onClick &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;_ &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; update&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;_ &lt;span class=&quot;hl-keyword&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)),&lt;/span&gt; s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Count: $count&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Call it with empty parens — &lt;code&gt;Counter()&lt;/code&gt; — to get a node:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;div&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;h1&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Demo&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Counter&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;())&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;component-props&quot;&gt;&lt;code&gt;component&lt;/code&gt; — props&lt;/h3&gt;
&lt;p&gt;When a component needs inputs, build it with &lt;code&gt;component&lt;/code&gt;. The one-prop form takes a single
value:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Greeting&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; component&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt; name &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt;
  p&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Hello, $name!&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;hl-type&quot;&gt;Greeting&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;world&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For several inputs, the positional forms (&lt;code&gt;component[A, B]&lt;/code&gt;, up to four) let you pass
arguments directly instead of bundling them:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Stat&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; component&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;label&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt;
  div&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
    cls &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;stat&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
    span&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;cls &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;label&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; label&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
    span&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;: &amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
    strong&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;hl-type&quot;&gt;Stat&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Clicks&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; clicks&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-type&quot;&gt;Stat&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Likes&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; likes&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you’d rather have named fields without declaring a case class, pass a single &lt;em&gt;named
tuple&lt;/em&gt; to &lt;code&gt;component[P]&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Card&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; component&lt;span class=&quot;hl-punctuation&quot;&gt;[(&lt;/span&gt;title&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; count&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)]&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt; p &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt;
  div&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;span&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;p&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt; strong&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;p&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;count&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;hl-type&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;((&lt;/span&gt;title &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Unread&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; count &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A parent re-renders its children by passing them new props; each child re-renders only
when the props it received actually changed.&lt;/p&gt;
&lt;h3 id=&quot;container-children-slots&quot;&gt;&lt;code&gt;container&lt;/code&gt; — children (slots)&lt;/h3&gt;
&lt;p&gt;A component that wraps arbitrary children — the analogue of React’s &lt;code&gt;props.children&lt;/code&gt; — is
built with &lt;code&gt;container&lt;/code&gt;. The children arrive as a &lt;code&gt;Children&lt;/code&gt; (a &lt;code&gt;Vector[VNode]&lt;/code&gt;); splice
them wherever you like with the usual seq-as-children conversion:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Card&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; container &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt; children &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt;
  div&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;cls &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;card&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; children&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;hl-type&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  h2&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Title&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  p&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Body text.&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A container can take props &lt;em&gt;and&lt;/em&gt; children. Declare it with &lt;code&gt;container[P]&lt;/code&gt; and call it
curried — props first, then the children:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Panel&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; container&lt;span class=&quot;hl-punctuation&quot;&gt;[(&lt;/span&gt;title&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)]&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;p&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; children&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt;
  section&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
    cls &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;panel&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
    h2&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;p&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
    div&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;cls &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;panel-body&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; children&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;hl-type&quot;&gt;Panel&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;((&lt;/span&gt;title &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Settings&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;))(&lt;/span&gt;
  toggle&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
  slider&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;keyed-lists&quot;&gt;Keyed lists&lt;/h3&gt;
&lt;p&gt;When you render a list that can reorder, insert, or delete, give each item a stable &lt;code&gt;key&lt;/code&gt;
so the reconciler moves DOM nodes instead of rebuilding them — preserving their state and
focus. Inside an element, set it with the &lt;code&gt;key&lt;/code&gt; attribute:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;ul&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  items&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;map&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;item &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt;
    li&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
      key &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; item&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
      span&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
      button&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;onClick &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;_ &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; remove&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;hl-punctuation&quot;&gt;)),&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;✕&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A component instance can be keyed too, by passing a key as the second argument:
&lt;code&gt;Row(rowProps, item.id)&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;memo-skip-unchanged-re-renders&quot;&gt;&lt;code&gt;memo&lt;/code&gt; — skip unchanged re-renders&lt;/h3&gt;
&lt;p&gt;Wrapping a component in &lt;code&gt;memo&lt;/code&gt; makes it bail out of a parent-driven re-render when its new
props are equal (&lt;code&gt;==&lt;/code&gt;) to the previous ones — Riposte’s &lt;code&gt;React.memo&lt;/code&gt;. It still re-renders
on its own state changes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Row&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; memo&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;component&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;RowProps&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt; props &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; … &lt;span class=&quot;hl-punctuation&quot;&gt;})&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Define the memoized component once as a stable &lt;code&gt;val&lt;/code&gt;; calling &lt;code&gt;memo&lt;/code&gt; inline in a render
produces a new identity each time and defeats the purpose. Props that carry freshly
allocated closures compare unequal every render, so stabilize handlers with &lt;code&gt;useCallback&lt;/code&gt;
(see &lt;a href=&quot;/guide/hooks/&quot;&gt;Hooks&lt;/a&gt;) when memoizing.&lt;/p&gt;
&lt;h2 id=&quot;escape-hatches&quot;&gt;Escape hatches&lt;/h2&gt;
&lt;p&gt;A few constructs step outside the normal element-tree flow:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;unsafeHtml(s)&lt;/code&gt;&lt;/strong&gt; sets an element’s inner HTML directly — Riposte’s
&lt;code&gt;dangerouslySetInnerHTML&lt;/code&gt;. Only pass markup you trust.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;portal(target, child)&lt;/code&gt;&lt;/strong&gt; renders &lt;code&gt;child&lt;/code&gt; into a &lt;em&gt;different&lt;/em&gt; DOM node (one outside the
component’s own subtree) while keeping it logically part of the tree — for modals,
tooltips, and overlays that must escape &lt;code&gt;overflow: hidden&lt;/code&gt; or stacking contexts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;errorBoundary(fallback)(child)&lt;/code&gt;&lt;/strong&gt; catches throws during &lt;code&gt;child&lt;/code&gt;‘s mount, patch, and
re-render, rendering &lt;code&gt;fallback(error)&lt;/code&gt; in its place instead of tearing down the whole
tree.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;errorBoundary&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;err &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; div&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;cls &lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;error&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Something broke: ${err.getMessage}&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;hl-type&quot;&gt;RiskyWidget&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Continue to &lt;a href=&quot;/guide/hooks/&quot;&gt;Hooks&lt;/a&gt; for state, effects, and the rest of the hook family.&lt;/p&gt;</content>
  </entry>
</feed>
