<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>suit</title>
  <link href="https://suit.edadma.dev/feed.xml" rel="self"/>
  <link href="https://suit.edadma.dev/"/>
  <id>https://suit.edadma.dev/feed.xml</id>
  <updated>2026-06-07T03:28:13.464209126Z</updated>
  <author><name>Ed Maxedon</name></author>
  <entry>
    <title>Widgets</title>
    <link href="https://suit.edadma.dev/reference/widgets/"/>
    <id>https://suit.edadma.dev/reference/widgets/</id>
    <updated>2026-06-07T03:28:13.464209126Z</updated>
    <summary>A small library of reusable controls composed from the DSL primitives and vdom hooks. A widget is an ordinary vdom component, so its interaction state (hover, pressed) lives in useState…</summary>
    <content type="html">&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;suit&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;widgets&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;p&gt;A small library of reusable controls composed from the DSL primitives and vdom hooks. A
widget is an ordinary vdom component, so its interaction state (hover, pressed) lives in
&lt;code&gt;useState&lt;/code&gt; and survives re-renders, and it reconciles in place exactly like an application
component. Each widget is purely declarative output over &lt;code&gt;box&lt;/code&gt; / &lt;code&gt;text&lt;/code&gt; / &lt;code&gt;row&lt;/code&gt; / &lt;code&gt;stack&lt;/code&gt;;
the render tree, layout, and input routing underneath give it pixels and behaviour.&lt;/p&gt;
&lt;div class=&quot;juicer-callout juicer-callout-note&quot;&gt;
  &lt;strong&gt;Note&lt;/strong&gt;
  &lt;div class=&quot;juicer-callout-body&quot;&gt;&lt;p&gt;These widgets are &lt;strong&gt;pointer- and keyboard-driven only&lt;/strong&gt;. A text field needs text-input
support in the SDL binding, and a scroll view needs renderer clip-rects; until those land
in &lt;a href=&quot;https://sdl3.edadma.dev/&quot;&gt;sdl3&lt;/a&gt;, &lt;code&gt;TextField&lt;/code&gt; and &lt;code&gt;ScrollView&lt;/code&gt; are deliberately absent
rather than approximated. The focus and keyboard infrastructure they need already exists.&lt;/p&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id=&quot;button&quot;&gt;Button&lt;/h2&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;Button&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Component2&lt;/span&gt;&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-keyword&quot;&gt;=&amp;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;/code&gt;&lt;/pre&gt;
&lt;p&gt;A push button: a labelled, focusable rectangle that calls &lt;code&gt;onPressed&lt;/code&gt; when clicked (a press
and release on the button) or activated from the keyboard (Space or Enter while focused). It
tints on hover and while held.&lt;/p&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;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-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;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;checkbox&quot;&gt;Checkbox&lt;/h2&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;Checkbox&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Component2&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;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;/code&gt;&lt;/pre&gt;
&lt;p&gt;A small focusable square that toggles, calling &lt;code&gt;onChange&lt;/code&gt; with the new state on a click or
on Space while focused. It is &lt;strong&gt;controlled&lt;/strong&gt; — it draws the &lt;code&gt;checked&lt;/code&gt; it is given and never
holds the value itself, so the parent owns the state. The check is a filled inner square (no
glyph-font 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; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;checked&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; setChecked&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-variable&quot;&gt;false&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;checked&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; setChecked&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;slider&quot;&gt;Slider&lt;/h2&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;Slider&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Component2&lt;/span&gt;&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-type&quot;&gt;Double&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;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;/code&gt;&lt;/pre&gt;
&lt;p&gt;A horizontal slider over the range &lt;code&gt;0..1&lt;/code&gt;: a full-width track with a draggable thumb. It is
&lt;strong&gt;controlled&lt;/strong&gt; — it renders the &lt;code&gt;value&lt;/code&gt; it is given and reports a new value through
&lt;code&gt;onChange&lt;/code&gt; on a press, a drag, or the arrow keys while focused. The new value comes from the
press position in the slider’s own coordinate space (&lt;code&gt;local.x / size.width&lt;/code&gt;), which is why
the handlers live on the outer track, not the thumb (see
&lt;a href=&quot;/guide/input/#pointer-capture-drag&quot;&gt;pointer capture&lt;/a&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-punctuation&quot;&gt;(&lt;/span&gt;level&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; setLevel&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.4&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
box&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;width &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;240&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;
  &lt;span class=&quot;hl-type&quot;&gt;Slider&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;level&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; setLevel&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;theme&quot;&gt;Theme&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Theme&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; primary&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; primaryHover&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; primaryActive&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; onPrimary&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Color&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; surface&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; border&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; accent&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; track&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Color&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The default palette the built-in widgets paint with — a small dark-on-accent theme. It is
just a set of named &lt;code&gt;Color&lt;/code&gt;s; applications can ignore it and pass their own, but the widgets
read from it so a stock control looks consistent.&lt;/p&gt;
&lt;h2 id=&quot;a-controlled-widget-example&quot;&gt;A controlled-widget example&lt;/h2&gt;
&lt;p&gt;Because &lt;code&gt;Checkbox&lt;/code&gt; and &lt;code&gt;Slider&lt;/code&gt; are controlled, the pattern is always the same: hold the
value in &lt;code&gt;useState&lt;/code&gt;, render the widget with it, and pass the setter as &lt;code&gt;onChange&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-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;on&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; setOn&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-variable&quot;&gt;true&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-punctuation&quot;&gt;(&lt;/span&gt;level&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; setLevel&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.5&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;

  col&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;spacing &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;
    row&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;crossAxisAlignment &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;CrossAxisAlignment&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Center&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; spacing &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;
      &lt;span class=&quot;hl-type&quot;&gt;Checkbox&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;on&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; setOn&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
      text&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;if&lt;/span&gt; on &lt;span class=&quot;hl-keyword&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;on&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;off&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;white&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
    text&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;${(level * 100).toInt}%&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;white&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
    box&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;width &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;240&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Slider&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;level&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; setLevel&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://suit.edadma.dev/getting-started/quick-start/"/>
    <id>https://suit.edadma.dev/getting-started/quick-start/</id>
    <updated>2026-06-07T03:28:13.464209126Z</updated>
    <summary>A complete suit program: define a component with hooks, lay it out with the DSL, and run it in a native window.</summary>
    <content type="html">&lt;p&gt;A complete suit program: define a component with hooks, lay it out with the DSL, and run
it in a native window.&lt;/p&gt;
&lt;h2 id=&quot;a-counter&quot;&gt;A counter&lt;/h2&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;suit&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;suit&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;dsl&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;suit&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;widgets&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-comment&quot;&gt;// A component is an ordinary vdom view: its state lives in hooks and survives&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;// re-renders, and it reconciles in place when state changes.&lt;/span&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-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-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;

  col&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;spacing &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; mainAxisAlignment &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;MainAxisAlignment&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Center&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; crossAxisAlignment &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;CrossAxisAlignment&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Center&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;
    text&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; size &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; 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;white&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
    row&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;spacing &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;
      &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;−&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;=&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-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;+&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;=&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-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;

@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-type&quot;&gt;Suit&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;run&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;counter&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;320&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;200&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run it with &lt;code&gt;sbt suitNative/run&lt;/code&gt; (the call blocks on the frame loop for the window’s
lifetime). &lt;code&gt;sbt suitNative/nativeLink&lt;/code&gt; just produces the binary without launching it.&lt;/p&gt;
&lt;h2 id=&quot;how-a-frame-happens&quot;&gt;How a frame happens&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Suit.run&lt;/code&gt; opens the SDL window, installs the vdom host and the scheduler seams, mounts
your app, and runs the frame loop. A state change flows like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;An event handler calls a &lt;code&gt;useState&lt;/code&gt; setter.&lt;/li&gt;
&lt;li&gt;vdom enqueues a re-render on the microtask seam.&lt;/li&gt;
&lt;li&gt;The loop drains the queue: the reconciler re-renders and &lt;strong&gt;mutates the render tree in
place&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The change bubbles a &lt;em&gt;dirty&lt;/em&gt; flag to the root.&lt;/li&gt;
&lt;li&gt;The next loop iteration repaints — and &lt;strong&gt;only&lt;/strong&gt; then, so an idle UI costs nothing but
event polling.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You never call “render” or “repaint” yourself; you change state and the tree follows.&lt;/p&gt;
&lt;h2 id=&quot;laying-things-out&quot;&gt;Laying things out&lt;/h2&gt;
&lt;p&gt;The DSL mirrors the constraint-layout primitives. A quick taste:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-comment&quot;&gt;// A header bar over a flexible body — the body expands to fill the leftover height.&lt;/span&gt;
col&lt;span class=&quot;hl-punctuation&quot;&gt;()(&lt;/span&gt;
  box&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;bg &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;rgb&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;0x222831&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt; height &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;48&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; padding &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;EdgeInsets&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;all&lt;span class=&quot;hl-punctuation&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;
    text&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;My App&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-number&quot;&gt;20&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;white&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  box&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;flex &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; padding &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;EdgeInsets&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;all&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;))(&lt;/span&gt;
    text&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Body fills the rest.&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;white&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;spacer()&lt;/code&gt; eats leftover space (the replacement for flex-grow); &lt;code&gt;flex = n&lt;/code&gt; on a child makes
it claim a share of the leftover main-axis space; &lt;code&gt;stack&lt;/code&gt;/&lt;code&gt;align&lt;/code&gt;/&lt;code&gt;center&lt;/code&gt; position
children by fractional alignment. See the &lt;strong&gt;&lt;a href=&quot;/guide/layout/&quot;&gt;layout guide&lt;/a&gt;&lt;/strong&gt; for the full
model and the &lt;strong&gt;&lt;a href=&quot;/reference/dsl/&quot;&gt;DSL reference&lt;/a&gt;&lt;/strong&gt; for every builder.&lt;/p&gt;
&lt;h2 id=&quot;choosing-a-font&quot;&gt;Choosing a font&lt;/h2&gt;
&lt;p&gt;Text is rendered through Cairo. &lt;code&gt;Suit.run&lt;/code&gt; uses the &lt;strong&gt;Inter&lt;/strong&gt; font bundled into the binary by
default — no installed font required, identical on every OS. Pass &lt;code&gt;fontPath&lt;/code&gt; to load a
specific TrueType/OpenType file through FreeType instead:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-type&quot;&gt;Suit&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;run&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;my app&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;640&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;480&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; fontPath &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/path/to/MyFont.ttf&amp;quot;&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;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;handling-input&quot;&gt;Handling input&lt;/h2&gt;
&lt;p&gt;Widgets are pointer- and keyboard-driven out of the box. To handle raw input yourself, the
&lt;code&gt;box&lt;/code&gt; builder takes typed handlers:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;box&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  width &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; height &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; bg &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;rgb&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;0x4dabf7&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  focusable   &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;
  onClick     &lt;span class=&quot;hl-keyword&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.localX}, ${e.localY}&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  onMouseEnter &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; _ &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;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;hover in&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  onMouseLeave &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; _ &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;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;hover out&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  onKeyDown   &lt;span class=&quot;hl-keyword&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;scancode &lt;span class=&quot;hl-keyword&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Space&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;then&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;space&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;See the &lt;strong&gt;&lt;a href=&quot;/guide/input/&quot;&gt;input guide&lt;/a&gt;&lt;/strong&gt; for bubbling, pointer capture, and focus.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Layout</title>
    <link href="https://suit.edadma.dev/guide/layout/"/>
    <id>https://suit.edadma.dev/guide/layout/</id>
    <updated>2026-06-07T03:28:13.464209126Z</updated>
    <summary>suit lays out with the protocol Flutter and SwiftUI both adopted — two greenfield frameworks that independently rejected flexbox for the same idea:</summary>
    <content type="html">&lt;p&gt;suit lays out with the protocol Flutter and SwiftUI both adopted — two greenfield
frameworks that independently rejected flexbox for the same idea:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Constraints go down, sizes come up, the parent sets positions.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A parent hands each child a &lt;code&gt;Constraints&lt;/code&gt; (the range of sizes it may take); the child
chooses its own &lt;code&gt;Size&lt;/code&gt; within that range; the parent then positions the child by writing
its offset. One rule, applied recursively — no monolithic multi-property solver.&lt;/p&gt;
&lt;h2 id=&quot;constraints&quot;&gt;Constraints&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Constraints&lt;/code&gt; is a min/max range on each axis:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Constraints&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;minWidth&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; maxWidth&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; minHeight&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; maxHeight&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;strong&gt;tight vs loose&lt;/strong&gt; distinction is the load-bearing one:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An axis is &lt;strong&gt;tight&lt;/strong&gt; when its min equals its max — the parent is &lt;em&gt;dictating&lt;/em&gt; that
dimension (“be exactly this wide”). &lt;code&gt;Constraints.tight(size)&lt;/code&gt; forces both axes.&lt;/li&gt;
&lt;li&gt;An axis is &lt;strong&gt;loose&lt;/strong&gt; when its min is 0 — the child may be as small as its natural size,
up to the max (“be as big as you need, no larger”). &lt;code&gt;Constraints.loose(size)&lt;/code&gt; and the
&lt;code&gt;.loosen&lt;/code&gt; method drop the minimums to zero.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That single distinction is what later expresses “fill the leftover space” versus “shrink to
fit” — the basis of spacers and flexible children.&lt;/p&gt;
&lt;p&gt;Useful operations:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;c&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;constrain&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;size&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;             &lt;span class=&quot;hl-comment&quot;&gt;// clamp a size into the range&lt;/span&gt;
c&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;tighten&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;width&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; height&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;      &lt;span class=&quot;hl-comment&quot;&gt;// force one/both axes to an explicit extent (clamped)&lt;/span&gt;
c&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;deflate&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;insets&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;             &lt;span class=&quot;hl-comment&quot;&gt;// shrink the room by padding on each side&lt;/span&gt;
c&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;loosen                      &lt;span class=&quot;hl-comment&quot;&gt;// min → 0, keep max (turn &amp;quot;fill&amp;quot; into &amp;quot;at most&amp;quot;)&lt;/span&gt;
c&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;biggest &lt;span class=&quot;hl-keyword&quot;&gt;/&lt;/span&gt; c&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;smallest        &lt;span class=&quot;hl-comment&quot;&gt;// the largest / smallest allowed size&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;how-each-primitive-sizes-itself&quot;&gt;How each primitive sizes itself&lt;/h2&gt;
&lt;p&gt;Every render object applies the one rule its own way. Knowing each makes layouts
predictable.&lt;/p&gt;
&lt;h3 id=&quot;box&quot;&gt;box&lt;/h3&gt;
&lt;p&gt;A styled container. An axis with an explicit &lt;code&gt;width&lt;/code&gt;/&lt;code&gt;height&lt;/code&gt; takes that value; otherwise
the axis &lt;strong&gt;fills&lt;/strong&gt; the space offered under a tight constraint and &lt;strong&gt;shrinks to its content&lt;/strong&gt;
under a loose one.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A &lt;code&gt;box&lt;/code&gt; at the root fills the window; the same &lt;code&gt;box&lt;/code&gt; inside a &lt;code&gt;row&lt;/code&gt; wraps its contents —
the difference is entirely the constraint its parent handed it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It lays its single child in the room left after &lt;code&gt;padding&lt;/code&gt; (loosened, so the child sizes to
content), places it inside the padding, and reports the child’s size grown by the insets.&lt;/p&gt;
&lt;h3 id=&quot;row-col-renderflex&quot;&gt;row / col (RenderFlex)&lt;/h3&gt;
&lt;p&gt;Children are laid end to end along the &lt;strong&gt;main axis&lt;/strong&gt; (horizontal for &lt;code&gt;row&lt;/code&gt;, vertical for
&lt;code&gt;col&lt;/code&gt;) and sized across the &lt;strong&gt;cross axis&lt;/strong&gt;, in two passes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Inflexible children&lt;/strong&gt; (&lt;code&gt;flex = 0&lt;/code&gt;) are laid out at their natural main size.&lt;/li&gt;
&lt;li&gt;The leftover main space, after the inflexible children and the fixed &lt;code&gt;spacing&lt;/code&gt; gaps, is
divided among the &lt;strong&gt;flexible children&lt;/strong&gt; in proportion to their &lt;code&gt;flex&lt;/code&gt;; each gets a tight
main constraint equal to its share.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Then:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mainAxisSize&lt;/code&gt; — &lt;code&gt;Max&lt;/code&gt; fills the parent along the main axis, &lt;code&gt;Min&lt;/code&gt; wraps the children.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mainAxisAlignment&lt;/code&gt; — distributes any remaining slack: &lt;code&gt;Start&lt;/code&gt; / &lt;code&gt;End&lt;/code&gt; / &lt;code&gt;Center&lt;/code&gt; clump
the children; &lt;code&gt;SpaceBetween&lt;/code&gt; / &lt;code&gt;SpaceAround&lt;/code&gt; / &lt;code&gt;SpaceEvenly&lt;/code&gt; spread the gap.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;crossAxisAlignment&lt;/code&gt; — places each child across: &lt;code&gt;Start&lt;/code&gt; / &lt;code&gt;End&lt;/code&gt; / &lt;code&gt;Center&lt;/code&gt;, or &lt;code&gt;Stretch&lt;/code&gt;
to force every child to the full cross extent.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When the main axis is &lt;em&gt;unbounded&lt;/em&gt; (e.g. a row inside a horizontally-loose parent) there is
nothing to distribute, so flexible children are treated as inflexible.&lt;/p&gt;
&lt;h3 id=&quot;spacer&quot;&gt;spacer&lt;/h3&gt;
&lt;p&gt;A flexible empty gap — the replacement for flex-grow. Inside a row or column it eats
leftover space in proportion to its &lt;code&gt;flex&lt;/code&gt;, pushing its siblings apart. It is just a &lt;code&gt;box&lt;/code&gt;
with no appearance and a flex factor.&lt;/p&gt;
&lt;h3 id=&quot;sizedbox-renderconstrained&quot;&gt;sizedBox (RenderConstrained)&lt;/h3&gt;
&lt;p&gt;Forces a fixed size onto its child (SwiftUI’s &lt;code&gt;.frame&lt;/code&gt;, Flutter’s &lt;code&gt;SizedBox&lt;/code&gt;). Each given
axis becomes a tight constraint on the child; an unset axis passes the parent’s constraint
straight through. It has no appearance — pure layout.&lt;/p&gt;
&lt;h3 id=&quot;padding&quot;&gt;padding&lt;/h3&gt;
&lt;p&gt;Insets its child by &lt;code&gt;padding&lt;/code&gt; on each side: the child is laid out in the parent’s
constraints deflated by the insets, placed at the top-left inside the padding, and this
object reports the child’s size grown by the insets.&lt;/p&gt;
&lt;h3 id=&quot;stack-align-center&quot;&gt;stack / align / center&lt;/h3&gt;
&lt;p&gt;A z-ordered overlay: children stack back-to-front in declaration order, each positioned by
an &lt;code&gt;Alignment&lt;/code&gt;. The stack fills bounded space, otherwise wraps its largest child. &lt;code&gt;align&lt;/code&gt;
is a one-child stack at a chosen alignment; &lt;code&gt;center&lt;/code&gt; is &lt;code&gt;align(Alignment.center)&lt;/code&gt;. This is
the basis for modals, tooltips, and badges once portals layer on top.&lt;/p&gt;
&lt;h3 id=&quot;text&quot;&gt;text&lt;/h3&gt;
&lt;p&gt;Sizes itself by asking the installed &lt;code&gt;TextMeasurer&lt;/code&gt; how big its string is in its
&lt;code&gt;TextStyle&lt;/code&gt;, clamped into the parent’s constraints. Layout is single-line for now.&lt;/p&gt;
&lt;h2 id=&quot;alignment&quot;&gt;Alignment&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Alignment(x, y)&lt;/code&gt; is a fractional coordinate independent of box size: &lt;code&gt;(-1, -1)&lt;/code&gt; is the
top-left corner, &lt;code&gt;(0, 0)&lt;/code&gt; the centre, &lt;code&gt;(1, 1)&lt;/code&gt; the bottom-right. The same value positions a
child in a container of any size — exactly what stacks, centring, and a slider thumb need.
Named constants cover the nine compass points (&lt;code&gt;Alignment.topLeft&lt;/code&gt;, &lt;code&gt;.center&lt;/code&gt;,
&lt;code&gt;.bottomRight&lt;/code&gt;, …).&lt;/p&gt;
&lt;h2 id=&quot;a-worked-example&quot;&gt;A worked example&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;col&lt;span class=&quot;hl-punctuation&quot;&gt;()(&lt;/span&gt;                                            &lt;span class=&quot;hl-comment&quot;&gt;// fills the window (tight from root)&lt;/span&gt;
  box&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;bg &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; header&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; height &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;48&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;                  &lt;span class=&quot;hl-comment&quot;&gt;// fixed-height bar&lt;/span&gt;
    text&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; 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;white&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  row&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;flex &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-comment&quot;&gt;// body claims the leftover height&lt;/span&gt;
    box&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;width &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; bg &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; sidebar&lt;span class=&quot;hl-punctuation&quot;&gt;)(),&lt;/span&gt;             &lt;span class=&quot;hl-comment&quot;&gt;// fixed-width sidebar&lt;/span&gt;
    box&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;flex &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; bg &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; content&lt;span class=&quot;hl-punctuation&quot;&gt;)(),&lt;/span&gt;                &lt;span class=&quot;hl-comment&quot;&gt;// content fills the rest of the width&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 column gets a tight constraint from the root, so it fills the window. The header takes
its fixed 48px; the row, with &lt;code&gt;flex = 1&lt;/code&gt;, claims the remaining height. Inside the row the
sidebar takes its fixed 200px and the content box, with &lt;code&gt;flex = 1&lt;/code&gt;, fills what’s left.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Installation</title>
    <link href="https://suit.edadma.dev/getting-started/installation/"/>
    <id>https://suit.edadma.dev/getting-started/installation/</id>
    <updated>2026-06-07T03:28:13.464209126Z</updated>
    <summary>suit targets Scala Native and renders through SDL3, so there are three things to have in place: the Scala Native toolchain, the native SDL3 libraries, and the suit sources themselves.</summary>
    <content type="html">&lt;p&gt;suit targets Scala Native and renders through SDL3, so there are three things to have in
place: the &lt;strong&gt;Scala Native toolchain&lt;/strong&gt;, the &lt;strong&gt;native SDL3 libraries&lt;/strong&gt;, and the &lt;strong&gt;suit
sources&lt;/strong&gt; themselves.&lt;/p&gt;
&lt;div class=&quot;juicer-callout juicer-callout-note&quot;&gt;
  &lt;strong&gt;Note&lt;/strong&gt;
  &lt;div class=&quot;juicer-callout-body&quot;&gt;&lt;p&gt;suit is in active development and is &lt;strong&gt;not yet published to Maven Central&lt;/strong&gt;. You build and
run it from the repository checkout. The sections below describe that workflow; published
artifacts and a one-line dependency will come once the API stabilises.&lt;/p&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id=&quot;requirements&quot;&gt;Requirements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Scala 3 with sbt&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;sbt-scala-native&lt;/code&gt; and &lt;code&gt;sbt-scala-native-crossproject&lt;/code&gt; plugins&lt;/li&gt;
&lt;li&gt;LLVM/Clang (the Scala Native toolchain)&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;SDL3&lt;/strong&gt;, &lt;strong&gt;Cairo&lt;/strong&gt;, and &lt;strong&gt;FreeType&lt;/strong&gt; shared libraries on your system&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;install-the-native-libraries&quot;&gt;Install the native libraries&lt;/h2&gt;
&lt;p&gt;suit’s runtime links against system libraries via &lt;code&gt;@link&lt;/code&gt;: &lt;strong&gt;SDL3&lt;/strong&gt; (window, input, present,
through the &lt;a href=&quot;https://sdl3.edadma.dev/&quot;&gt;sdl3 bindings&lt;/a&gt;), &lt;strong&gt;Cairo&lt;/strong&gt; (the drawing engine), and
&lt;strong&gt;FreeType&lt;/strong&gt; (font loading). On macOS with Homebrew:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;hl-function&quot;&gt;brew&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;sdl3&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;cairo&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cairo depends on FreeType, so Homebrew pulls it in alongside. On Linux, install the SDL3,
Cairo, and FreeType development packages from your distribution (or build them from source).&lt;/p&gt;
&lt;h2 id=&quot;get-the-sources&quot;&gt;Get the sources&lt;/h2&gt;
&lt;p&gt;suit depends on two sibling repositories, both checked out next to it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/edadma/riposte&quot;&gt;riposte&lt;/a&gt;&lt;/strong&gt; — supplies the &lt;code&gt;vdom&lt;/code&gt; core. suit
consumes its JVM and Native cross-targets as a &lt;strong&gt;source dependency&lt;/strong&gt; (&lt;code&gt;vdomJVM&lt;/code&gt; /
&lt;code&gt;vdomNative&lt;/code&gt;), because those targets are not published; only &lt;code&gt;vdom.js&lt;/code&gt; is on Central.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/edadma/sdl3&quot;&gt;sdl3&lt;/a&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;a href=&quot;https://github.com/edadma/libcairo&quot;&gt;libcairo&lt;/a&gt;&lt;/strong&gt;,
and &lt;strong&gt;&lt;a href=&quot;https://github.com/edadma/freetype&quot;&gt;freetype&lt;/a&gt;&lt;/strong&gt; — the SDL3, Cairo, and FreeType
bindings, pulled from Maven Central.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;hl-function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;clone&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;https://github.com/edadma/riposte.git&lt;/span&gt;
&lt;span class=&quot;hl-function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;clone&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;https://github.com/edadma/suit.git&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The expected layout is the two repos side by side:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dev/
├── riposte/      # provides vdomJVM / vdomNative (source dependency)
└── suit/         # this repo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;suit’s &lt;code&gt;build.sbt&lt;/code&gt; references riposte by relative path:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;jvmConfigure&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;_&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;dependsOn&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;ProjectRef&lt;/span&gt;&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;../riposte&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;vdomJVM&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;nativeConfigure&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;_&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;dependsOn&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;ProjectRef&lt;/span&gt;&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;../riposte&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;vdomNative&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;and pulls SDL3 from Central:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;nativeSettings&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-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;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;sdl3&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.2.2&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;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;libcairo&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.4&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;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;freetype&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.4&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;verify-the-setup&quot;&gt;Verify the setup&lt;/h2&gt;
&lt;p&gt;Run the headless layout suite on the JVM — it needs no window, no device, and no native
toolchain, because the layout engine is pure Scala:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;hl-function&quot;&gt;sbt&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;suitJVM/test&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then build the native demo and run it (see the &lt;a href=&quot;/getting-started/quick-start/&quot;&gt;quick start&lt;/a&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;hl-function&quot;&gt;sbt&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;suitNative/run&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If a window opens showing the widget demo, the toolchain and SDL3 are wired correctly.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Input</title>
    <link href="https://suit.edadma.dev/guide/input/"/>
    <id>https://suit.edadma.dev/guide/input/</id>
    <updated>2026-06-07T03:28:13.464209126Z</updated>
    <summary>Input is the bridge from raw device events to the render tree’s handlers. The runtime polls SDL for mouse, wheel, and keyboard events and feeds them to a set of…</summary>
    <content type="html">&lt;p&gt;Input is the bridge from raw device events to the render tree’s handlers. The runtime polls
SDL for mouse, wheel, and keyboard events and feeds them to a set of &lt;strong&gt;routers&lt;/strong&gt;; the
routers hit-test the tree (for pointer events) or consult the focus owner (for key events)
and fire the matching handler. Like layout, all of this is pure Scala — it needs only
&lt;code&gt;hitTest&lt;/code&gt;, the parent chain, and the handler maps — so the dispatch model is unit-tested on
the JVM.&lt;/p&gt;
&lt;h2 id=&quot;bubbling&quot;&gt;Bubbling&lt;/h2&gt;
&lt;p&gt;Dispatch bubbles. A hit-test lands on the &lt;em&gt;deepest&lt;/em&gt; object under the cursor, but handlers
are usually registered on a composite’s &lt;strong&gt;outer&lt;/strong&gt; object — a button’s frame, a slider’s
track — while the cursor actually sits over an inner decoration with no handler of its own.&lt;/p&gt;
&lt;p&gt;So an event walks up the parent chain from the hit to the &lt;strong&gt;nearest ancestor with a handler
for that event&lt;/strong&gt;, and fires there. This is the analogue of DOM event bubbling, in the
single-listener-per-object form suit uses.&lt;/p&gt;
&lt;p&gt;The event’s coordinates are resolved against the &lt;strong&gt;receiving&lt;/strong&gt; object, not the raw hit:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;PointerEvent&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;position&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Offset&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; local&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Offset&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; size&lt;span class=&quot;hl-punctuation&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; button&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-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-keyword&quot;&gt;def&lt;/span&gt; x&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;def&lt;/span&gt; y&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-comment&quot;&gt;// absolute (window) coordinates&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; localX&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;def&lt;/span&gt; localY&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-comment&quot;&gt;// relative to the receiving object&apos;s top-left&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;position&lt;/code&gt; is the absolute window coordinate.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;local&lt;/code&gt; is that point relative to the receiving object’s top-left, and &lt;code&gt;size&lt;/code&gt; is that
object’s size.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Together they let a handler work in its own coordinate space — a slider maps
&lt;code&gt;local.x / size.width&lt;/code&gt; to a fraction, measured against its &lt;em&gt;whole track&lt;/em&gt; no matter which
inner pixel was hit.&lt;/p&gt;
&lt;h2 id=&quot;pointer-capture-drag&quot;&gt;Pointer capture (drag)&lt;/h2&gt;
&lt;p&gt;A press &lt;strong&gt;captures&lt;/strong&gt; the pointer at the hit object. While captured, every subsequent move
goes to the capturing object — even after the cursor leaves its bounds — until the button
comes up. This is what makes dragging work: a slider keeps receiving &lt;code&gt;mousemove&lt;/code&gt; while you
drag past its edge. During a capture drag, &lt;code&gt;button&lt;/code&gt; carries the held button so a handler
can tell a drag from a plain hover (&lt;code&gt;if e.button != 0 then …&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;click&lt;/code&gt; fires on button-up &lt;strong&gt;only if the press and the release resolve to the same click
handler&lt;/strong&gt; — i.e. the release landed on the widget the press started on. Pressing a button
and releasing off it does not click.&lt;/p&gt;
&lt;h2 id=&quot;hover&quot;&gt;Hover&lt;/h2&gt;
&lt;p&gt;Hover is tracked per &lt;strong&gt;owner&lt;/strong&gt;, not per pixel. The router finds the nearest ancestor that
takes part in hover (has a &lt;code&gt;mouseenter&lt;/code&gt; or &lt;code&gt;mouseleave&lt;/code&gt; handler) and fires &lt;code&gt;mouseleave&lt;/code&gt; /
&lt;code&gt;mouseenter&lt;/code&gt; only when that owner &lt;em&gt;changes&lt;/em&gt; — so moving the cursor across a button’s inner
label does not spam enter/leave, and a widget styles itself on hover cleanly.&lt;/p&gt;
&lt;h2 id=&quot;keyboard-focus&quot;&gt;Keyboard focus&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;FocusManager&lt;/code&gt; owns which object currently has keyboard focus:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;press&lt;/strong&gt; routes focus to the nearest &lt;strong&gt;focusable&lt;/strong&gt; ancestor of the hit object (the
target itself if it is focusable), or clears focus if the press landed on nothing
focusable. Clicking a widget takes focus; clicking empty space drops it.&lt;/li&gt;
&lt;li&gt;Focus changes fire &lt;code&gt;focus&lt;/code&gt; / &lt;code&gt;blur&lt;/code&gt; handlers (no payload) so a widget can re-style itself.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;KeyRouter&lt;/code&gt; delivers &lt;code&gt;keydown&lt;/code&gt; / &lt;code&gt;keyup&lt;/code&gt; only to the focused object.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Mark an object focusable in the DSL with &lt;code&gt;focusable = true&lt;/code&gt;; the built-in widgets set it on
their outer object.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;KeyEvent&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;scancode&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; repeat&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;hl-keyword&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;scancode&lt;/code&gt; is the &lt;strong&gt;physical&lt;/strong&gt; key as a standard USB-HID usage code — a stable
cross-platform numbering, not an SDL detail. The &lt;code&gt;Key&lt;/code&gt; object names the common ones:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-type&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Enter&lt;/span&gt;  &lt;span class=&quot;hl-type&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Escape&lt;/span&gt;  &lt;span class=&quot;hl-type&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Backspace&lt;/span&gt;  &lt;span class=&quot;hl-type&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Tab&lt;/span&gt;  &lt;span class=&quot;hl-type&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Space&lt;/span&gt;
&lt;span class=&quot;hl-type&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;   &lt;span class=&quot;hl-type&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Right&lt;/span&gt;   &lt;span class=&quot;hl-type&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Up&lt;/span&gt;         &lt;span class=&quot;hl-type&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Down&lt;/span&gt;
&lt;span class=&quot;hl-type&quot;&gt;Key&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-type&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;End&lt;/span&gt;     &lt;span class=&quot;hl-type&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Delete&lt;/span&gt;     &lt;span class=&quot;hl-type&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;PageUp&lt;/span&gt;  &lt;span class=&quot;hl-type&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;PageDown&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;repeat&lt;/code&gt; is true for the auto-repeat events a held key produces. Text entry — the Unicode a
key press produces under the active layout — is a separate concern (text-input events), not
derived from scancodes here.&lt;/p&gt;
&lt;h2 id=&quot;wheel&quot;&gt;Wheel&lt;/h2&gt;
&lt;p&gt;A wheel turn bubbles a &lt;code&gt;ScrollEvent&lt;/code&gt; to the nearest &lt;code&gt;wheel&lt;/code&gt; handler at the cursor.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;ScrollEvent&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;position&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Offset&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; deltaX&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; deltaY&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Positive &lt;code&gt;deltaY&lt;/code&gt; is a downward/away scroll, matching SDL’s convention.&lt;/p&gt;
&lt;h2 id=&quot;handlers-on-box&quot;&gt;Handlers on box&lt;/h2&gt;
&lt;p&gt;All of this is reached through the typed handlers on the &lt;code&gt;box&lt;/code&gt; builder:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;box&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  focusable    &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;
  onClick      &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-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;PointerEvent&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;
  onMouseDown  &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-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;PointerEvent&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;
  onMouseUp    &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-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;PointerEvent&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;
  onMouseMove  &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-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;PointerEvent&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;
  onMouseEnter &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-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;PointerEvent&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;
  onMouseLeave &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-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;PointerEvent&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;
  onWheel      &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-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;ScrollEvent&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;
  onKeyDown    &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-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;KeyEvent&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;
  onKeyUp      &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-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;KeyEvent&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;
  onFocus      &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-punctuation&quot;&gt;...,&lt;/span&gt;
  onBlur       &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-punctuation&quot;&gt;...,&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;children&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;The &lt;a href=&quot;/reference/widgets/&quot;&gt;widget library&lt;/a&gt; is built entirely on these — reading the
&lt;code&gt;Button&lt;/code&gt;, &lt;code&gt;Checkbox&lt;/code&gt;, and &lt;code&gt;Slider&lt;/code&gt; sources is the best way to see the model in practice.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>DSL</title>
    <link href="https://suit.edadma.dev/reference/dsl/"/>
    <id>https://suit.edadma.dev/reference/dsl/</id>
    <updated>2026-06-07T03:28:13.464209126Z</updated>
    <summary>The declarative surface — the builders that produce vdom VNodes, the render-tree equivalent of riposte’s HTML DSL. Each builder emits an element whose tag selects a RenderObject kind and whose…</summary>
    <content type="html">&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;suit&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;dsl&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;p&gt;The declarative surface — the builders that produce vdom &lt;code&gt;VNode&lt;/code&gt;s, the render-tree
equivalent of riposte’s HTML DSL. Each builder emits an element whose tag selects a
RenderObject kind and whose props are &lt;strong&gt;typed values&lt;/strong&gt; (a &lt;code&gt;Color&lt;/code&gt;, an &lt;code&gt;EdgeInsets&lt;/code&gt;, an
&lt;code&gt;Alignment&lt;/code&gt;, a layout enum), never strings.&lt;/p&gt;
&lt;p&gt;Configuration goes in the first parameter list and children in the second, so nesting reads
cleanly:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;col&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;spacing &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;
  box&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;bg &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;rgb&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;0x222831&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;))(&lt;/span&gt;text&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;hi&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)),&lt;/span&gt;
  spacer&lt;span class=&quot;hl-punctuation&quot;&gt;(),&lt;/span&gt;
  box&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;bg &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;rgb&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;0x222831&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;))(&lt;/span&gt;text&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;bye&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;Sizes are plain &lt;code&gt;Double&lt;/code&gt;s; &lt;code&gt;Double.NaN&lt;/code&gt; means “unset” (left to the layout), so call sites
stay free of &lt;code&gt;Some(...)&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;box&quot;&gt;box&lt;/h2&gt;
&lt;p&gt;The styled container — suit’s workhorse rectangle.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; box&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
    bg&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;          &lt;span class=&quot;hl-type&quot;&gt;Color&lt;/span&gt; | &lt;span class=&quot;hl-type&quot;&gt;Null&lt;/span&gt;     &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-punctuation&quot;&gt;,&lt;/span&gt;
    border&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;      &lt;span class=&quot;hl-type&quot;&gt;Color&lt;/span&gt; | &lt;span class=&quot;hl-type&quot;&gt;Null&lt;/span&gt;     &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-punctuation&quot;&gt;,&lt;/span&gt;
    borderWidth&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-keyword&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;
    width&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-keyword&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-type&quot;&gt;NaN&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
    height&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-keyword&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-type&quot;&gt;NaN&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
    padding&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;     &lt;span class=&quot;hl-type&quot;&gt;EdgeInsets&lt;/span&gt; | &lt;span class=&quot;hl-type&quot;&gt;Null&lt;/span&gt; &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-punctuation&quot;&gt;,&lt;/span&gt;
    flex&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-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;
    focusable&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;   &lt;span class=&quot;hl-type&quot;&gt;Boolean&lt;/span&gt;          &lt;span class=&quot;hl-keyword&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-comment&quot;&gt;// pointer / wheel / key / focus handlers — see the Input guide&lt;/span&gt;
    onClick&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;PointerEvent&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;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-type&quot;&gt;Null&lt;/span&gt; &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-punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;hl-comment&quot;&gt;/* onMouseDown, onMouseUp, onMouseMove, onMouseEnter, onMouseLeave,&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;       onWheel, onKeyDown, onKeyUp, onFocus, onBlur */&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-type&quot;&gt;VNode&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-type&quot;&gt;VNode&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;bg&lt;/code&gt; / &lt;code&gt;border&lt;/code&gt; / &lt;code&gt;borderWidth&lt;/code&gt; give it appearance; &lt;code&gt;width&lt;/code&gt; / &lt;code&gt;height&lt;/code&gt; fix its size (omit
to fill a tight parent or wrap a loose one); &lt;code&gt;padding&lt;/code&gt; insets its child; &lt;code&gt;flex&lt;/code&gt; makes it
expand inside a row/column. See the &lt;a href=&quot;/guide/input/&quot;&gt;input guide&lt;/a&gt; for the handlers.&lt;/p&gt;
&lt;h2 id=&quot;text&quot;&gt;text&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; text&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;content&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; size&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-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;16.0&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; color&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Color&lt;/span&gt; &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-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-number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)):&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;VNode&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A single line of text in &lt;code&gt;color&lt;/code&gt; at point &lt;code&gt;size&lt;/code&gt;. It measures and paints as one line
through the installed &lt;code&gt;TextMeasurer&lt;/code&gt; and the canvas.&lt;/p&gt;
&lt;h2 id=&quot;row-col&quot;&gt;row / col&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; row&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
    mainAxisAlignment&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;hl-type&quot;&gt;MainAxisAlignment&lt;/span&gt;  &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;MainAxisAlignment&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
    crossAxisAlignment&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;CrossAxisAlignment&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;CrossAxisAlignment&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
    mainAxisSize&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;       &lt;span class=&quot;hl-type&quot;&gt;MainAxisSize&lt;/span&gt;       &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;MainAxisSize&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Max&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
    spacing&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-keyword&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;
    flex&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-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-punctuation&quot;&gt;)(&lt;/span&gt;children&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;VNode&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-type&quot;&gt;VNode&lt;/span&gt;

&lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; col&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-comment&quot;&gt;/* same parameters */&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-type&quot;&gt;VNode&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-type&quot;&gt;VNode&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A horizontal (&lt;code&gt;row&lt;/code&gt;) or vertical (&lt;code&gt;col&lt;/code&gt;) stack. Children are laid along the main axis;
flexible children share the leftover space. The enums:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MainAxisAlignment&lt;/code&gt; — &lt;code&gt;Start&lt;/code&gt;, &lt;code&gt;End&lt;/code&gt;, &lt;code&gt;Center&lt;/code&gt;, &lt;code&gt;SpaceBetween&lt;/code&gt;, &lt;code&gt;SpaceAround&lt;/code&gt;, &lt;code&gt;SpaceEvenly&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CrossAxisAlignment&lt;/code&gt; — &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;/li&gt;
&lt;li&gt;&lt;code&gt;MainAxisSize&lt;/code&gt; — &lt;code&gt;Min&lt;/code&gt; (wrap children), &lt;code&gt;Max&lt;/code&gt; (fill parent)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See the &lt;a href=&quot;/guide/layout/&quot;&gt;layout guide&lt;/a&gt; for how the two passes distribute space.&lt;/p&gt;
&lt;h2 id=&quot;spacer&quot;&gt;spacer&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; spacer&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;flex&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-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-type&quot;&gt;VNode&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A flexible empty gap — the replacement for flex-grow. Inside a row or column it eats
leftover space in proportion to &lt;code&gt;flex&lt;/code&gt;, pushing its siblings apart.&lt;/p&gt;
&lt;h2 id=&quot;padding&quot;&gt;padding&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; padding&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;insets&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;EdgeInsets&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-type&quot;&gt;VNode&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-type&quot;&gt;VNode&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Insets its child by &lt;code&gt;insets&lt;/code&gt; on each side.&lt;/p&gt;
&lt;h2 id=&quot;sizedbox&quot;&gt;sizedBox&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; sizedBox&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;width&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-keyword&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-type&quot;&gt;NaN&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; height&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-keyword&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-type&quot;&gt;NaN&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-type&quot;&gt;VNode&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-type&quot;&gt;VNode&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A fixed-size box with no appearance: forces &lt;code&gt;width&lt;/code&gt;/&lt;code&gt;height&lt;/code&gt; onto its child (or occupies
that size with no child). Omit an axis to leave it to the parent.&lt;/p&gt;
&lt;h2 id=&quot;stack-align-center&quot;&gt;stack / align / center&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; stack&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;alignment&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Alignment&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Alignment&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;topLeft&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-type&quot;&gt;VNode&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-type&quot;&gt;VNode&lt;/span&gt;
&lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; align&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;alignment&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Alignment&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-type&quot;&gt;VNode&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-type&quot;&gt;VNode&lt;/span&gt;
&lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; center&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-type&quot;&gt;VNode&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-type&quot;&gt;VNode&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;stack&lt;/code&gt; is a z-ordered overlay: children stack back-to-front, each positioned by
&lt;code&gt;alignment&lt;/code&gt;. &lt;code&gt;align&lt;/code&gt; positions a single child at an alignment (a one-child stack); &lt;code&gt;center&lt;/code&gt;
is &lt;code&gt;align(Alignment.center)&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;geometry-colour-values&quot;&gt;Geometry &amp;amp; colour values&lt;/h2&gt;
&lt;p&gt;The DSL takes these plain value types (all pure, no SDL dependency):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-type&quot;&gt;Offset&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; y&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;                         &lt;span class=&quot;hl-comment&quot;&gt;// a 2-D position or displacement&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;width&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; height&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-type&quot;&gt;Rect&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; y&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; width&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; height&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;            &lt;span class=&quot;hl-comment&quot;&gt;// .contains(px, py), right/bottom-exclusive&lt;/span&gt;
&lt;span class=&quot;hl-type&quot;&gt;EdgeInsets&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;top&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; right&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; bottom&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; left&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-comment&quot;&gt;// .all(v), .symmetric(horizontal, vertical)&lt;/span&gt;
&lt;span class=&quot;hl-type&quot;&gt;Alignment&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; y&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;                      &lt;span class=&quot;hl-comment&quot;&gt;// fractional: (-1,-1) topLeft … (1,1) bottomRight&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;r&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; g&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; a &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;              &lt;span class=&quot;hl-comment&quot;&gt;// .rgb(0xRRGGBB), .parse(&amp;quot;#rrggbb[aa]&amp;quot;), .toHex, .withAlpha&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Color&lt;/code&gt; provides &lt;code&gt;black&lt;/code&gt;, &lt;code&gt;white&lt;/code&gt;, &lt;code&gt;transparent&lt;/code&gt;, and &lt;code&gt;Color.rgb(hex)&lt;/code&gt; for packed
literals.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Architecture</title>
    <link href="https://suit.edadma.dev/guide/architecture/"/>
    <id>https://suit.edadma.dev/guide/architecture/</id>
    <updated>2026-06-07T03:28:13.464209126Z</updated>
    <summary>suit is one layer of a three-layer system, the same division of labour Flutter draws between its widgets, its elements, and its render objects. Two of those layers already exist…</summary>
    <content type="html">&lt;p&gt;suit is one layer of a three-layer system, the same division of labour Flutter draws
between its &lt;em&gt;widgets&lt;/em&gt;, its &lt;em&gt;elements&lt;/em&gt;, and its &lt;em&gt;render objects&lt;/em&gt;. Two of those layers
already exist in &lt;strong&gt;vdom&lt;/strong&gt;; suit builds the third.&lt;/p&gt;
&lt;h2 id=&quot;three-trees&quot;&gt;Three trees&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt; Widgets / VNodes     ── what you write: col(...)(box(...), text(...))      ← vdom
 Elements             ── the reconciled, stateful instance tree            ← vdom
 RenderObjects        ── layout, paint, hit-test                           ← suit
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The VNode tree&lt;/strong&gt; is the declarative description your component returns. It is cheap and
rebuilt freely. vdom’s DSL produces it; suit’s DSL (&lt;code&gt;box&lt;/code&gt;, &lt;code&gt;row&lt;/code&gt;, &lt;code&gt;col&lt;/code&gt;, …) is just a
vocabulary of VNodes whose tags name render-object kinds.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The element tree&lt;/strong&gt; is vdom’s reconciler at work: it diffs successive VNode trees, holds
hook state (&lt;code&gt;useState&lt;/code&gt;), and decides the minimal set of host mutations. This is entirely
vdom — suit adds nothing here.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The RenderObject tree&lt;/strong&gt; is suit. Each node lays itself out, paints itself, and answers
hit-tests. The reconciler creates and mutates these nodes through a &lt;code&gt;HostConfig&lt;/code&gt;; every
opaque host node it holds is really a &lt;code&gt;RenderObject&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;vdom is &lt;em&gt;host-agnostic&lt;/em&gt;: the same reconciler and hooks drive the browser DOM in
&lt;a href=&quot;https://github.com/edadma/riposte&quot;&gt;riposte&lt;/a&gt; and the screen here. The only thing that
changes per host is the &lt;code&gt;HostConfig&lt;/code&gt; binding and the leaf layer underneath it.&lt;/p&gt;
&lt;p&gt;You never import vdom directly. suit re-exports its consumer-facing surface — &lt;code&gt;view&lt;/code&gt;,
&lt;code&gt;component&lt;/code&gt;, the hooks (&lt;code&gt;useState&lt;/code&gt;, &lt;code&gt;useEffect&lt;/code&gt;, …), and the &lt;code&gt;VNode&lt;/code&gt; model — under
&lt;code&gt;io.github.edadma.suit&lt;/code&gt;, so an application imports only &lt;code&gt;io.github.edadma.suit.*&lt;/code&gt; (plus
&lt;code&gt;suit.dsl.*&lt;/code&gt; / &lt;code&gt;suit.widgets.*&lt;/code&gt;). vdom is an implementation detail, the way the browser’s
DOM engine is to a web page.&lt;/p&gt;
&lt;h2 id=&quot;the-host-binding&quot;&gt;The host binding&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;SuitHostConfig&lt;/code&gt; is the single seam where vdom meets suit’s renderer — the analogue of
riposte’s DOM host config, but creating retained render objects instead of DOM nodes. The
reconciler calls it to create elements, edit the tree, set properties, and register
listeners:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; createElement&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;tag&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; namespace&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-type&quot;&gt;Null&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;AnyRef&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; tag &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-string&quot;&gt;&amp;quot;box&amp;quot;&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;RenderBox&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;row&amp;quot;&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;RenderFlex&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Axis&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Horizontal&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-string&quot;&gt;&amp;quot;col&amp;quot;&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;RenderFlex&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Axis&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Vertical&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-string&quot;&gt;&amp;quot;padding&amp;quot;&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;RenderPadding&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;sizedBox&amp;quot;&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;RenderConstrained&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;stack&amp;quot;&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;RenderStack&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;text&amp;quot;&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;RenderText&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-keyword&quot;&gt;case&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;RenderBox&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Properties travel as &lt;strong&gt;typed values&lt;/strong&gt;, not strings. vdom’s &lt;code&gt;PropValue&lt;/code&gt; channel carries a
&lt;code&gt;Color&lt;/code&gt;, an &lt;code&gt;EdgeInsets&lt;/code&gt;, an &lt;code&gt;Alignment&lt;/code&gt;, or a layout enum with its real type straight to
the matching render-object field; nothing is stringified and re-parsed. DOM-shaped
&lt;code&gt;HostConfig&lt;/code&gt; methods (&lt;code&gt;setStyle&lt;/code&gt; with CSS, &lt;code&gt;setInnerHtml&lt;/code&gt;, namespaces) have no meaning on a
pixel canvas and are no-ops.&lt;/p&gt;
&lt;h2 id=&quot;what-a-renderobject-does&quot;&gt;What a RenderObject does&lt;/h2&gt;
&lt;p&gt;Every render object implements three jobs:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;RenderObject&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; layout&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;constraints&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Constraints&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-comment&quot;&gt;// size self, position children&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; paint&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;canvas&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Canvas&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; origin&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Offset&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-comment&quot;&gt;// draw back-to-front&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; hitTest&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;point&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Offset&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; origin&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Offset&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;RenderObject&lt;/span&gt;|&lt;span class=&quot;hl-type&quot;&gt;Null&lt;/span&gt; &lt;span class=&quot;hl-comment&quot;&gt;// deepest object under a point&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The kinds map one-to-one onto the DSL: &lt;code&gt;RenderBox&lt;/code&gt; (styled container), &lt;code&gt;RenderFlex&lt;/code&gt; (row /
column), &lt;code&gt;RenderPadding&lt;/code&gt;, &lt;code&gt;RenderConstrained&lt;/code&gt; (sized box), &lt;code&gt;RenderStack&lt;/code&gt; (z-stack /
align / center), &lt;code&gt;RenderText&lt;/code&gt;, plus a &lt;code&gt;RenderRoot&lt;/code&gt; at the top and a &lt;code&gt;RenderAnchor&lt;/code&gt; for
vdom’s fragment/portal/empty placeholders.&lt;/p&gt;
&lt;h2 id=&quot;the-paint-seam&quot;&gt;The paint seam&lt;/h2&gt;
&lt;p&gt;Painting goes through a &lt;code&gt;Canvas&lt;/code&gt; trait — the boundary between &lt;em&gt;what&lt;/em&gt; to draw and &lt;em&gt;how&lt;/em&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;trait&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Canvas&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; fillRect&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;rect&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Rect&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; color&lt;span class=&quot;hl-punctuation&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;Unit&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; strokeRect&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;rect&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Rect&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; color&lt;span class=&quot;hl-punctuation&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; width&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-type&quot;&gt;Unit&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; fillCircle&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;center&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Offset&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; radius&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; color&lt;span class=&quot;hl-punctuation&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;Unit&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; line&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Offset&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Offset&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; width&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; color&lt;span class=&quot;hl-punctuation&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;Unit&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; drawText&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;origin&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Offset&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; text&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; style&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;TextStyle&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;On Native, &lt;code&gt;CairoCanvas&lt;/code&gt; draws through &lt;a href=&quot;https://www.cairographics.org/&quot;&gt;Cairo&lt;/a&gt; — a real 2D
vector engine, so every fill, stroke, and glyph is anti-aliased by its coverage rasteriser.
&lt;a href=&quot;https://sdl3.edadma.dev/&quot;&gt;SDL3&lt;/a&gt; only creates the window, reads input, and presents the
finished frame. In tests, &lt;code&gt;RecordingCanvas&lt;/code&gt; captures the same calls so paint output can be
asserted on. The same trait split lets &lt;code&gt;TextMeasurer&lt;/code&gt; size text off-device (a deterministic
fake in tests, a Cairo-backed measurer at runtime), which keeps the whole layout pass
JVM-testable.&lt;/p&gt;
&lt;h2 id=&quot;why-jvm-testable-matters&quot;&gt;Why JVM-testable matters&lt;/h2&gt;
&lt;p&gt;The layout engine, the render tree, and the geometry are &lt;strong&gt;pure Scala with no SDL
dependency&lt;/strong&gt; — they live in &lt;code&gt;shared/&lt;/code&gt; and cross to a JVM target whose only purpose is
tests. So &lt;code&gt;sbt suitJVM/test&lt;/code&gt; exercises real layout, paint (against &lt;code&gt;RecordingCanvas&lt;/code&gt;), and
input routing with no window and no native toolchain, the same way vdom’s reconciler is
tested headlessly. This is the whole reason the layout engine was kept FFI-free: no Yoga,
no C solver, just a recursive constraint negotiation you can step through in a debugger.&lt;/p&gt;
&lt;h2 id=&quot;the-runtime&quot;&gt;The runtime&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Suit.run&lt;/code&gt; is the Native entry point. It owns the SDL window and renderer, installs the
host binding and the two scheduler seams (a microtask queue for re-renders, a macrotask
queue for passive effects), mounts the app, and runs the frame loop. The loop is the bridge
between vdom’s React-style batched updates and a game-style render loop: vdom never paints
on its own — it enqueues work, the loop drains it, the tree is marked dirty, and the loop
repaints &lt;strong&gt;only when something changed&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;One rendering detail is worth knowing: Cairo draws the frame into an in-memory ARGB32 image
surface, which is uploaded to an SDL streaming texture and blitted to the window each
iteration (re-drawn only when the tree is dirty). Cairo’s ARGB32 layout is byte-identical to
SDL’s &lt;code&gt;ARGB8888&lt;/code&gt; on a little-endian host, so the upload is a straight copy with no
conversion. SDL never draws a shape and Cairo never touches the OS — the clean split between
the graphics engine and the platform layer.&lt;/p&gt;</content>
  </entry>
</feed>
