--- title: "Ui: bestPractices" description: "smabar plugin reference: Ui: bestPractices" canonical: "https://smabar.com/docs/plugin-ref-ui-best-practices/" lang: en --- # Ui: bestPractices > smabar plugin reference: Ui: bestPractices Canonical HTML version: https://smabar.com/docs/plugin-ref-ui-best-practices/ · Language: en · Publisher: smabar (https://smabar.com) --- [Build my plugin](https://smabar.com/build-my-plugin.md) · [All references](https://smabar.com/docs/plugin-ref-index.md) · [Install the ZIP](https://smabar.com/docs/install-plugin-zip.md) Source: smabar 1.5.0, revision `a019f2563965edc37a5e8d84742ea1ff2fa6f824`. [Export metadata and checksums](https://smabar.com/plugin-reference/export.json). This is an offline reference. MCP calls quoted below describe the connected workflow; use the linked documents and create local files instead. Live state, installed plugins, available providers and the user's active theme are unknown. Never claim a live check. Read with [styling](https://smabar.com/docs/plugin-ref-styling.md) and [examples](https://smabar.com/docs/plugin-ref-examples.md). ## workflow ```json [ "Make follow-up calls when the tile needs them: ui_kit(sections=[\"coverLayouts\"]) for the nine tile recipes, [\"behaviour\"] for a data-sb-* hook, [\"sanitizer\"] for the allowlist and native interaction, [\"conventions\"] for tabs, menus, tooltips and render targets, [\"charts\"] for charts, [\"icons\"] for icon names, [\"media\"] for images and players.", "Compare all nine cover recipes by whenToUse and choose exactly one before writing the tile. Do not invent a tenth layout from loose classes.", "When hasFlyout is true, build the compact tile and detail flyout as one design: the tile earns the click, the flyout answers it. A tile-only tile keeps its complete state glanceable without inventing an empty flyout.", "Read the complete component anatomy in the relevant categories before composing a surface: forms for editors, content for cards/empty states, actions for navigation, feedback for validation. Use these components to give the user's idea a clear visual hierarchy; a row of generic controls is only a draft." ] ``` ## hierarchy ```json [ "The tile communicates one glanceable primary signal. Keep labels secondary and move history, controls and explanations into the flyout.", "Give each flyout one clear title and primary action, then group the rest into short vertical sections. Avoid nested cards when spacing alone separates the content.", "Use the smallest component that expresses the meaning: semantic text before a badge, a list before a table, and a native control before a custom interaction." ] ``` ## states ```json [ "Design loading, empty, error, stale and ready states. Keep the tile structure and footprint stable between them; use a placeholder such as – where the value belongs.", "Keep the last useful data visible during refresh or a recoverable network error, and explain the stale/error state without turning the tile into an alert panel.", "Disable actions that cannot run and explain why in nearby text or a themed title tooltip; never make a dead control look active." ] ``` ## accessibility ```json [ "Use semantic buttons, links, headings, lists and form labels. Icon-only controls need an aria-label AND a title with the same text — the shell turns the title into its themed tooltip; decorative icons use aria-hidden=\"true\".", "Every meaningful image needs useful alt text, audio/video needs an accessible fallback, and spoken video needs captions when available.", "Never communicate status by color alone. Pair sb-ok/sb-warn/sb-crit with text, an icon or another explicit cue and keep every interaction keyboard reachable." ] ``` ## themeAndMotion ```json [ "Use sb-* classes and var(--sb-*) tokens so every bundled and drop-in theme remains legible; do not hardcode surface, text or status colors.", "Animate a meaningful state change, not decoration. Keep markup stable so media and CSS motion do not restart on each render; the shell honors reduced motion.", "Use data-sb-key when one surface has several remembered values, and push new markup only when visible data or state actually changed." ] ``` ## media ```json [ "Local images, audio and video belong in app.data_dir and render through sb-asset:. Copy a bundled file from app.plugin_dir once before rendering; never write runtime data into the code folder.", "Keep media at max-width 100%, provide poster/alt/captions where applicable, and use the media classes instead of fixed dimensions.", "For a remote player use its official HTTPS embed in the flyout, a lightweight thumbnail in tile/hover, stable HTML while the item is unchanged, and an ordinary external link as fallback.", "For background playback use app.audio, which reads local files directly from the plugin or data folder, supports playback controls and does not require an HTML audio element. HTML autoplay stays muted." ] ``` ## verification ```json [ "Read plugin_logs(pluginId) after rendering; sanitizer removals and unknown sb-* classes are design failures, not cosmetic warnings.", "Photograph every surface after the last write: the tile with bar_screenshot(target=the full tile id from bar_get_state, scale=3); for every tile with hasFlyout, bar_ui_state(action=\"open_flyout\", tileId=the full id) and then bar_screenshot(target=\"flyout\"); hover and popup when the plugin renders them.", "Open and INSPECT the returned PNGs. A successful screenshot call only proves a capture exists. Check hierarchy, spacing, readable contrast, wrapping of long realistic content, clipping, touch targets and the primary action; revise and capture again until they pass.", "Exercise the actual UI in empty, populated, create/edit and error states wherever applicable. For forms, verify labels, localized placeholders, optional-field hints, validation, keyboard input, submit and cancel. For actionable toasts, verify buttons and dismissal.", "Check another contrasting theme and long localized text where possible. Use temporary sample records only with authorization or isolated data; remove only records you created and restore any settings changed for the review.", "Before reporting completion, name the surfaces/states inspected and any remaining visual limitation. If smabar is not running or capture fails, state that visual review is pending; successful startup and passing tests alone do not establish design quality." ] ``` ## forms ```json [ "For every form, fetch ui_kit sections forms and snippets. Use form.sb-stack to separate fields, and sb-field-stack around each label/control/hint/error. Automatic root spacing does not space children inside a plain form.", "Every text input and textarea needs a visible associated label and a localized, useful placeholder: show an example value or what to write, rather than repeating the label. Placeholders supplement labels; they never replace them.", "Mark optional fields, explain formats/units/time zones and the effect of leaving a field empty with sb-field__hint and aria-describedby. Native date/time controls often ignore placeholder: provide a visible hint beside them.", "Prefer input[data-sb-temporal] for a themed date, time or datetime-local field with an explicit completion path; read the behaviour hook and snippets.temporal. Its calendar closes on a day choice, its time editor has Apply time, and its clear action makes optional fields obvious. Use native inputs for sub-minute precision.", "Use required, min/max/maxlength and an owned sb-field__error where applicable. Put actionable save failures near the form, preserve entered values and keep the correction path obvious.", "Give the submit action a specific verb, distinguish create from edit, and keep cancellation and destructive actions visually secondary. Use a collapsed native details section for optional long editors so existing content stays easy to scan." ] ```