agentleFS
BlockKnown publisherblock/berd/src/shared/ui/AGENTS.md

berd / ui

AGENTS.md1k starsChanged 3 months agoApache-2.0View on GitHub

Use the primitives in src/shared/ui/ as the default way to build interface elements. Prefer extending these components over recreating their styling in feature code. All buttons within a single surface zone must use the same size. Footer buttons in dialogs are the default Button size. Toolbar buttons are xs. Page-level actions are default. Never mix sizes within a footer, toolbar, or inline button group — if one button needs to be smaller, the whole group steps down together. The button…

We split this AGENTS.md into the rules it gives an agent

19 rules. About 0 are common, 9 are distinctive, and 10 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈9

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 10

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Use the primitives in src/shared/ui/ as the default way to build interface elements.

    line 3
  2. Distinctive:

    Prefer shared UI primitives over custom markup plus Tailwind classes.

    line 7
  3. Distinctive:

    Prefer adding a variant or prop to a shared component over one-off styling in feature code.

    line 8
  4. Very specific:

    Keep feature code focused on composition, state, and content.

    line 9
  5. Distinctive:

    If a pattern appears in more than one place, extract or extend a shared primitive instead of copying class strings.

    line 10
  6. Very specific:

    Use Button for clickable controls unless there is a strong reason not to.

    line 84
  7. Very specific:

    Use variant and size before adding custom classes.

    line 85
  8. Very specific:

    Use leftIcon and rightIcon for leading and trailing icons instead of manually placing icon children.

    line 86
  9. Distinctive:

    Do not add spacing classes only to position button icons unless the design system cannot express the pattern yet.

    line 87
  10. Very specific:

    Use Button for async action feedback too.

    line 91
  11. Very specific:

    When combining async feedback with asChild, keep the visible label inside the slotted child so Button can swap it with loading, success, or …

    line 95
  12. Very specific:

    Only give an icon its own explicit size-* class when intentionally overriding the design-system default.

    line 110
  13. Very specific:

    Do not create wrapper buttons for back/save/delete.

    line 127
  14. Very specific:

    When a trigger behaves like a button, use Button as the trigger surface.

    line 145
  15. Distinctive:

    Avoid introducing custom colors, spacing, radii, or typography in feature code when existing tokens and shared variants cover the need.

    line 150
  16. Distinctive:

    If a control needs a new visual treatment, add it to the shared component API first.

    line 151
  17. Distinctive:

    Keep accessibility built in: semantic elements, labels for icon-only buttons, and consistent focus states.

    line 152
  18. Distinctive:

    Before writing custom classes in a feature, ask:

    line 156
  19. Distinctive:

    Should this become a shared variant or prop?

    line 159

≈ “Common” is an estimate: at least 1 in 5 of the 36,368 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
AGENTS.md

Shared UI Design System

Use the primitives in src/shared/ui/ as the default way to build interface elements. Prefer extending these components over recreating their styling in feature code.

Core Rules

  • Prefer shared UI primitives over custom markup plus Tailwind classes.
  • Prefer adding a variant or prop to a shared component over one-off styling in feature code.
  • Keep feature code focused on composition, state, and content. Visual treatment should usually live in src/shared/ui/.
  • If a pattern appears in more than one place, extract or extend a shared primitive instead of copying class strings.

Button sizing rule

All buttons within a single surface zone must use the same size. Footer buttons in dialogs are the default Button size. Toolbar buttons are xs. Page-level actions are default. Never mix sizes within a footer, toolbar, or inline button group — if one button needs to be smaller, the whole group steps down together.

The button system is a closed menu. Every button in the app is one of these named things — never a custom-styled Button.

The semantic menu (Button variants)

VariantUse when
primaryThe one main action (save, create, submit, confirm). Filled.
outlineThe paired secondary action next to a primary. Bordered.
subtleSoft-fill mid-weight action on background-colored surfaces.
ghostQuiet action in a row or toolbar. Invisible until hover.
alertAction inside a colored alert/banner; inherits currentColor.
linkInline text link that behaves as a button. Collapses to text.

Two flags modify variants:

  • destructive — danger intent. Works on primary (red fill), outline (red border/text), subtle (red tinted fill), and ghost (red text + tint hover). Other variants ignore it and warn in dev.
  • flush — ghost only. Inline geometry: no padding pill; rests at muted-foreground and raises the label on hover. For quiet actions that sit flush with surrounding content (list section actions, "show more").

Pick the emphasis first; if the action is dangerous, add destructive.

Chrome buttons (named wrappers)

Surface-specific recipes live in named components that compose Button. Use these instead of restyling Button for app chrome:

ComponentBaseUse when
TopBarIconButtonghostIcon actions in the app top bar / window chrome.
ComposerActionButtonsubtleControls on the chat composer surface.
PageHeaderButtonsubtleView-header actions in the app top strip.
AgentTileButtonsubtleActions floating over agent/persona tiles.
GlassButtonsubtleControls floating over media, canvases, artwork.
JumpToLatestButtonprimaryFloating back-to-live-edge pills over streams.
DisclosureButtonghost"View more" / "View less" / "View all" affordances. Pick a surface.

Each wrapper's recipe owns all of its interactive states; see the doc comment in its source for the full contract.

DisclosureButton takes a surface (default | sidebar | sidebarRow) because how quiet the rest state can afford to be depends on what the button sits on. default inherits the system ghost + flush states and is correct on tinted raised surfaces like the chat user bubble; the sidebar* surfaces dim to muted-foreground/75, which is only legible against the page background. Do not use a sidebar* surface on a tinted surface.

The rule (audited in CI)

Feature code never puts color or interactive-state classes (bg-*, text-foreground/text-muted-foreground/..., hover:*, active:*, shadow-*, opacity-*) on a <Button>. Layout-only classes (ml-auto, w-full, justify-start, shrink-0, truncation) are fine. If no variant, flag, or wrapper fits, that is a design-system conversation — extend the menu, don't restyle locally. pnpm design-system:audit enforces this and fails on new violations.

General guidance

  • Use Button for clickable controls unless there is a strong reason not to.
  • Use variant and size before adding custom classes.
  • Use leftIcon and rightIcon for leading and trailing icons instead of manually placing icon children.
  • Do not add spacing classes only to position button icons unless the design system cannot express the pattern yet.
  • For icon-only actions, use the icon-* sizes instead of text button sizes.
  • For active icon-only buttons, prefer native hover via title plus aria-label.
  • Reserve custom Tooltip for disabled controls or richer explanatory content.
  • Use Button for async action feedback too. Prefer its feedbackState, loadingLabel, successLabel, errorLabel, loadingVisual, and preserveWidth props over creating a separate async button wrapper or hand-placing spinners in feature code.
  • When combining async feedback with asChild, keep the visible label inside the slotted child so Button can swap it with loading, success, or error feedback.

Ghost icon buttons

variant="ghost" has compound variants for all icon-* sizes that set hover:bg-transparent hover:text-foreground. This means ghost icon buttons have no background fill on hover — only a color change. Do not add hover:bg-accent/50 or similar hover background classes to ghost icon buttons; the compound variant already provides the correct behavior. Layout classes like mr-1, size-6, flex-shrink-0 are fine to add.

Icon Sizing

  • Let Button size button icons by default.
  • Current button icon defaults are tied to button size:
  • xs and sm buttons use size-3 icons.
  • default buttons use size-3.5 icons.
  • lg buttons use size-4 icons.
  • Only give an icon its own explicit size-* class when intentionally overriding the design-system default.
  • Match icon visual weight to the text and control size. Small toolbar controls should not use oversized icons.

Dialogs

There is one modal surface: Dialog. Do not create new dialog chrome components; compose the anatomy the use case needs.

  • DialogContent owns the glass surface, close button, and width via the size prop (md | lg | xl). Do not pass ad-hoc max-w-* classes.
  • Simple dialogs: DialogHeader + content + DialogFooter. Everything scrolls together.
  • Content-heavy dialogs (forms, pickers, lists): wrap the middle in DialogBody. Its presence automatically pins the header and footer, moves padding to the zones, and gives the footer its top divider. Use DialogBody asChild with a <form> child when the body is a single form.
  • All dialog actions live in DialogFooter and use Button directly. Do not create wrapper buttons for back/save/delete. Back is just <Button variant="ghost" flush className="sm:mr-auto" />. Submit/save buttons use default Button size in the footer; if the body is a form, submit with <Button form={formId} type="submit">.
  • Semantic variants are the only other dialog components: AlertDialog (interruptive, no light-dismiss), ConfirmDialog (yes/no preset), and CommandDialog (search-first palette). Never fork a new one for content density — density is size + DialogBody, not a new component.
  • Name feature dialogs *Dialog, not *Modal.
  • List rows inside dialogs and settings use RowButton (menu variant for borderless option rows, field for bordered input-like triggers) instead of raw <button> + hand-rolled classes. Hover states come from the primitive (bg-muted); do not use washed-out one-offs like hover:bg-accent/30.

Menus And Selectors

  • Compose menus from DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, and related shared primitives.
  • When a trigger behaves like a button, use Button as the trigger surface.
  • Repeated trigger patterns like icon + label + chevron should be expressed through shared props or shared wrapper components.

Styling Boundaries

  • Avoid introducing custom colors, spacing, radii, or typography in feature code when existing tokens and shared variants cover the need.
  • If a control needs a new visual treatment, add it to the shared component API first.
  • Keep accessibility built in: semantic elements, labels for icon-only buttons, and consistent focus states.

Good Heuristic

Before writing custom classes in a feature, ask:

  1. Can an existing shared component already do this?
  2. Should this become a shared variant or prop?
  3. Will another screen likely need the same pattern?

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.