Three files you can copy into a project as-is.
| File | What it is |
|---|---|
| starter.html | A working page with a dashboard, list, form, states, pagination and a drawer. Every SUI contract in one place |
| sui-kit.js | Helper functions that encode each markup contract exactly once |
| sui-patch.css | Local additions filling documented gaps: alert severity, focus rings, tabular numerals, overflow guards |
SUI’s stylesheet references its assets from the web root — url("/img/sui/…") and
url('/fonts/Structicons.woff'). Opening starter.html over file:// will render an
unstyled page with invisible icons and no error. You need the SUI assets served at /.
The simplest path is to serve a directory that has css/, fonts/, img/ and js/
copied out of a structs-webapp checkout:
WEBAPP=../path/to/structs-webapp
mkdir -p /tmp/sui-demo && cd /tmp/sui-demo
cp -R "$WEBAPP"/src/public/{css,fonts,img} .
mkdir -p js && cp -R "$WEBAPP"/src/js/sui js/
cp /path/to/structs-ai/develop/ui/examples/* .
python3 -m http.server 8000
Then open http://localhost:8000/starter.html.
If icons are missing, the page logs a specific error to the console — that check is at
the bottom of starter.html and is worth keeping in your own app.
Serving from a subpath instead? You have to rewrite the url() targets in sui.css.
Structs Desktop does this to serve the Team Ops board under /board/; see
rebase_css_urls in src-tauri/src/mcp/web_board.rs for a reference implementation
that prefixes root-absolute targets and leaves relative, data: and scheme URLs alone.
starter.html is annotated with the reason behind each decision, not just the
markup. The scaling overrides at the top, the direct SUI imports rather than SUI.js,
the drawer’s classList.remove before setPlacement, and the font self-check are all
things that go wrong quietly if you skip them.
sui-kit.js exists because SUI’s contracts are strict enough that repeating them by
hand is how they drift. Its stepper() deliberately wires its own handlers rather than
relying on SUIInputStepper, which binds once at init and so never sees controls created
from data. paginationSlots() is exported separately so the five-slot logic can be
unit-tested without a DOM.
sui-patch.css only fills gaps — it never overrides a deliberate SUI choice, and it
uses real tokens throughout. Each block cites the gotcha it addresses. The alert severity
rules are the ones you will miss immediately if you drop them: without them all four
states in starter.html render with identical body-coloured text.
sui-patch.css existsstarter.html avoids SUI.js