---
title: 'Charming design guide: read before you write the UI'
sidebar:
  hidden: true
---

The `ui` field is one JavaScript program. Charming provides `<div id="app"></div>`, a Tailwind v3-compatible runtime, and a default theme. Your code fills `#app`.

## Sandbox

- Use inline UI instead of `alert`, `confirm`, or `prompt`. Those calls do nothing.
- Prevent native form submission and handle it in JavaScript.
- Do not add external scripts or CDN imports.
- Call the app backend through `window.charming`.
- Store lasting data through backend operations that use `env.storage`. Do not use browser storage for data that must work in chat clients or across devices.

Read https://charm.ing/docs/llms-full.txt before using browser capabilities, network access, or secrets.

## Layout

- Fill the viewport. Do not wrap the whole app in a narrow `max-w-*` container.
- Limit line length only for long text.
- Make the main action clear on first load.
- Use a fitting layout and palette. Do not default every app to a heading, input, button, and empty list.
- Avoid empty settings pages, generic "My App" headings, and fake account fields.

Use the vanilla DOM and Tailwind classes unless the task needs a small library. If you need one, inline its UMD or IIFE build.

## Backend calls

`window.charming.api(id)` returns the operation value. It throws `CharmingOperationError` when an operation fails.

```text
const api = window.charming.api("todo"); // pass manifest.id, not the UUID
const todos = await api.list();
await api.add({ text: "..." });
```

Credentials attach automatically. Do not manage tokens in the UI.

## Access

Use `window.charming.viewer.can(op)` to hide or disable actions a read-only viewer cannot run.

```text
const { viewer } = window.charming;
if (!viewer.can("addTodo")) {
  addButton.disabled = true;
}
```

Treat `viewer.can(op)` as a UI hint, not an auth check. The server enforces access. Catch `forbidden` and `forbidden_write` errors and show a read-only state.

Use `window.charming.user` for optional display data. Never use it to authorize an action. Read https://charm.ing/docs/technical-reference/authentication.md for roles and sign-in behavior.
