# UI kit & styling

> Style a card any way you like, or look native with the optional ui.css kit on the app's live theme — classes, variables and the sandbox rules for styles, fonts and images.

Source: https://docs.neurosquad.ai/en/card-sdk/styling

Inside its box, a card is an ordinary web page: any framework, any CSS, canvas, WebGL, WebAssembly.
Two ways to style it:

- **Native look** — the optional kit `ui.css`: a small dark set of buttons, fields, lists and badges
on the app's live theme. Cards built with it look like part of NeuroSquad and follow its theme.
- **Your own design** — ignore the kit. The theme variables are still there if you want to borrow
a colour.

## The UI kit

```html
<link rel="stylesheet" href="vendor/ui.css" />   <!-- plain template -->
```

```ts
import '@neurosquad/card-sdk/ui.css'              // with a bundler (React template)
```

Put `class="ns-kit"` on `<body>` for the base typography, background and scrollbars, then use the
classes:

```html
<body class="ns-kit">
  <header class="ns-spread">
    <h1 class="ns-title ns-truncate">Test radar</h1>
    <span class="ns-badge ns-badge--danger">3 failed</span>
  </header>

  <ul class="ns-list ns-scroll">
    <li class="ns-list-item"><span class="ns-dot ns-dot--danger"></span> checkout › pays</li>
    <li class="ns-list-item"><span class="ns-dot ns-dot--success"></span> auth › logs in</li>
  </ul>

  <label class="ns-field">
    <span class="ns-label">Filter</span>
    <input class="ns-input" placeholder="checkout" />
  </label>

  <div class="ns-row">
    <button class="ns-btn ns-btn--primary">Run</button>
    <button class="ns-btn ns-btn--ghost ns-btn--sm">Clear</button>
  </div>
</body>
```

| Group | Classes |
| --- | --- |
| Layout | `ns-kit`, `ns-stack` (vertical), `ns-row` (horizontal), `ns-spread` (space-between), `ns-grow`, `ns-scroll`, `ns-pad`, `ns-divider` |
| Surfaces | `ns-surface`, `ns-surface--inset`, `ns-callout`, `ns-callout--success`, `ns-callout--danger`, `ns-empty` |
| Text | `ns-title`, `ns-subtitle`, `ns-muted`, `ns-small`, `ns-mono`, `ns-truncate`, `ns-kbd`, `ns-help`, `ns-error` |
| Buttons | `ns-btn` + `--primary`, `--secondary`, `--outline`, `--ghost`, `--danger`, `--sm`, `--lg`, `--icon` |
| Fields | `ns-field`, `ns-label`, `ns-input`, `ns-textarea`, `ns-select`, `ns-switch` |
| Lists | `ns-list`, `ns-list-item` |
| Status | `ns-badge` + `--accent`, `--success`, `--warning`, `--danger`; `ns-dot` + the same; `ns-spinner`, `ns-progress` |

Outside the app (a browser preview), the kit falls back to the app's dark theme, so the preview
looks right too.

## Theme variables

`connect()` puts the app's live theme on `<html>` as `--ns-<token>` variables — `--ns-background`,
`--ns-surface`, `--ns-foreground`, `--ns-muted`, `--ns-accent`, `--ns-success`, `--ns-warning`,
`--ns-danger`, their `-foreground` and `-soft` variants, `--ns-border`, `--ns-focus`,
`--ns-field-*`, `--ns-radius`, `--ns-font-sans`, `--ns-font-mono` and more (the full list is on
[Theme](https://docs.neurosquad.ai/en/card-sdk/api/environment#theme)). Use them in your own CSS and your card follows the app:

```css
:root { color-scheme: dark; }
body {
  margin: 0;
  background: var(--ns-background, #0b0b0f);
  color: var(--ns-foreground, #fafafa);
  font: 13px/1.45 var(--ns-font-sans, system-ui, sans-serif);
}
.chip {
  border-radius: calc(var(--ns-radius, 0.5rem) * 2);
  background: var(--ns-accent-soft);
  color: var(--ns-accent-soft-foreground);
}
```

Give the variables fallbacks (as above) if your page must also render outside the app. To keep your
own colours untouched, connect with `connect({ theme: false })`.

## Sandbox rules for pages

Your page is served from its own package with a strict content security policy. In practice:

- **No inline scripts or `onclick="…"` attributes** — put code in `.js` files. `eval` and
`new Function` are blocked too (WebAssembly is allowed).
- **No external files.** Scripts, styles, fonts and images must be inside the package; a
`<link href="https://fonts…">` or `<img src="https://…">` is blocked. `data:` and `blob:` images
work. For remote data, use [`card.net.fetch`](https://docs.neurosquad.ai/en/card-sdk/api/network) and turn images into `blob:`
URLs.
- **Inline styles are fine** (`style="…"` and `<style>`).
- **Relative URLs.** The page is served from `nscard://<id>/<path>`; build with relative asset
paths (Vite: `base: './'`, as in the React template).
- **No `localStorage`, cookies, `alert`, pop-ups, downloads, fullscreen, camera or microphone.**
Use `card.storage`, `card.ui.confirm`, `card.openLink`.
- **No writing to the clipboard from the page.** `copy`/`cut` handlers and
`document.execCommand('copy')` do nothing (otherwise any card could replace the clipboard on a
click). Use [`card.copyText`](https://docs.neurosquad.ai/en/card-sdk/api/files#clipboard); the user's own copy of selected text
still works.
- **No nested frames.** `iframe`, `frame`, `object` and `embed` elements are removed.
- **Only blob workers.** `new Worker('worker.js')` fails in the card's origin; fetch the script,
wrap it in a `Blob` and start `new Worker(URL.createObjectURL(blob))`.
- **Never trust `message` events.** Other cards can `postMessage` your frame.

`npx @neurosquad/card-sdk validate` warns about inline scripts, inline handlers and external files in
your entry page.

> **Forms never navigate.** A `<form>`'s `submit` event fires and your handler runs, but the app
> cancels the submission itself and `form.submit()` does nothing — a card page cannot post anywhere
> or reload. Call `event.preventDefault()` in your handler anyway and do the work in JavaScript.

## Designing for the canvas

- **Small and large.** A card is often 400×300 and sometimes expanded to fill the screen. Make both
look finished: use `card.lifecycle.onExpanded` or `useExpanded()` to switch layouts.
- **Empty, loading, error.** Show something sensible before data arrives, when there is none, and
when a call fails.
- **The overview tile is your card zoomed out.** Keep [`setOverview`](https://docs.neurosquad.ai/en/card-sdk/api/card-ui#overview)
current with the one fact that matters.
- **Quiet when unseen.** Stop animations when `usePaused()` is true — a canvas can hold fifty cards.
- **The header is the app's.** Do not repeat the card's name or a close button inside the body;
the header already has them.

> Anything inside the card body is the card's own, and users are told so. Do not imitate
> NeuroSquad's dialogs or ask for passwords and keys inside your card — use `secret` settings, which
> the app asks for in its own form.
