Skip to content

Latest commit

 

History

History
65 lines (57 loc) · 3.59 KB

AGENTS.md

File metadata and controls

65 lines (57 loc) · 3.59 KB

Building Builder's interface

Builder is one product and every part of it should feel the same. A new setting, button, page, document view or agent interaction looks and behaves like what is already there, and extends it rather than standing beside it. DESIGN.md is the workspace design contract.

Before adding anything

  • Check what the surrounding interface already offers, and extend that. Never add a second way to do something the interface already does.
  • Build with @corbits/react-ui components and the existing design tokens first, so everything keeps the same theme and brand. Add no new colours, spacing, type scales or one-off styles.
  • Build a new component or area that isn't specific to Builder, such as a richer table, here first, matching the rest of the interface. Note on the issue and the pull request that it may fit better in @corbits/react-ui; maintainers decide whether it moves.
  • Wording lives in the interface's config.

Styles

  • A component's styles live in a CSS module beside it (pane-bar.tsx, pane-bar.module.css), imported as styles, with camelCase class names. styles.css holds only what is global: fonts, tokens, element defaults, overrides of the library that apply everywhere, and shared keyframes.
  • Each module has exact types, generated by bun run css-types (the dev script keeps them current and typecheck regenerates them), so styles.pane is a string and a misspelt class is a type error. Join classes with clsx only when one is conditional.
  • A look used in more than one place is a component, such as InlineNote, Field, Fault or Banner, not a class two components share. Shared components live in components.tsx, or in a file of their own when they belong to one area.
  • A shared component owns its whole look and takes no className. Variants are props, rendered as data-* attributes (data-tone="error", or an empty data-pill="" for a flag); the element it renders is an as prop.
  • Every shared component's root carries a data-slot naming it. A parent positions it from its own module through that attribute, such as .notes > [data-slot="inline-note"] for margin, size or placement, never through another module's class, and never by restyling its inside.
  • The library's own classes, such as .dark, go in :global(...), and a global keyframe in global(...): animation: global(fade-in) 200ms.
  • Code and tests find elements by data-* attributes, never by a class name.

Every artifact kind is a first-class document

A new kind (a sheet, a deck, a diagram) is integrated everywhere a document is, not shown as a standalone widget:

  • Tab: it opens as a tab in the document pane. The tab names it, so the view adds no second title and no outer margin; content sits flush.
  • Floating bar: every action on it (download, share, search, filters, copy, quote, versions, compare) goes in the document pane's one floating bar, supplied by the view. Views draw no toolbars, inline links or buttons of their own.
  • Tab menu: per-document commands that aren't everyday actions, such as print, go in the tab's context menu next to the existing items.
  • Quote: whatever the person points at, whether a passage, a cell range or a slide element, can be quoted into their message through the existing quote offer, naming the document, the version and the location.
  • Versions and changes: the version menu and comparison work for it, or say plainly why a version can't be compared.
  • States: loading, empty and error states are the same short status lines every document uses.