t3-code-android-nightly/docs/internals/web-ui.md

2 KiB

Web UI

Primitives

Controls come from components/ui. AGENTS.md says how to use and extend them.

Don't style a raw <button>, <input>, <select>, or <textarea> to look like a control a primitive provides. Raw elements are right when the element isn't that control: semantic rows, tabs, resize handles, swatches, editor surfaces, anything whose behavior or geometry differs by design.

Styles

Style with Tailwind classes in the component that renders the element.

  • Global CSS, in index.css or any other stylesheet, is only for what classes on the owning component can't express: generated Markdown or imperative DOM, custom elements and shadow roots, animations, runtime theme variables, and browser or Electron integration.
  • No style={{ ... }} or inline <style> for static values a class covers. Values computed at runtime are fine.
  • Theme-only rules use @variant dark and @variant light, not .dark selectors.

Composition

The composer banners are the reference.

  • ComposerBanner.tsx exports one object of small slots. Slots own their classes, take className, and spread the rest of their props, merging classes with cn. Variants and density are props on Root. A slot that needs a control renders Button.
  • ComposerBannerStack.tsx composes the slots and owns only stack behavior: priority, expand and collapse, dismiss transitions.
  • Consumers are a few lines of slots, and the slots carry the styling: ComposerActivityStatus.tsx.

Build new UI the same way. Compose or extend a component instead of copying its markup and classes. When a call site repeats a treatment another one already uses, make it a variant or a slot on the shared component. A new composer banner or notice uses the ComposerBanner slots.