Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .claude/skills/storybook-stories/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ should **not** add its own providers or `<Theme>`:
`ServiceProvider`, and a minimal TanStack Router. So `useHostTRPC()`,
`useService()`, `useRouterState()`, etc. render instead of throwing
"must be used within a Provider".
- A `<Theme>` (Radix) bound to the dark/light toolbar global.
- A `<Theme>` (Radix) bound to the dark/light toolbar global. This root provider is
the one sanctioned Radix usage — it supplies the CSS tokens. Radix *components*
are banned: build stories and components from `@posthog/quill` plus `div` +
Tailwind (see [UI Components](../../../AGENTS.md#ui-components)).

Add a per-story decorator only to constrain layout (e.g. wrap in a
`maxWidth` div so a full-width component sizes realistically).
Expand Down
70 changes: 68 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

`AGENTS.md` is the source of truth for architecture and development rules. `CLAUDE.md` is a symlink to this file. Edit this file only.

Feature-level guides follow the same convention: the content lives in `<dir>/AGENTS.md` with a `<dir>/CLAUDE.md` symlink beside it. When you add a new guide, add the symlink too (`ln -s AGENTS.md CLAUDE.md`).

## Architecture

PostHog uses a layered architecture. Business logic and UI live in shared `packages/*`. Each `apps/*` host boots those packages and binds host-specific implementations. `@posthog/core` and `@posthog/ui` must run unchanged on desktop, web, and mobile.
Expand Down Expand Up @@ -53,10 +55,12 @@ Hosts:
10. Boot side effects are `Contribution`s bound in feature modules and started by `boot()`.
11. tRPC routers are one-line forwards over services. No inline business logic.
12. Use Inversify with `@inversifyjs/strongly-typed`. Define each token as a standalone `export const TOKEN = Symbol.for("posthog.<area>.<thing>")` beside its `interface`/service — never an object-literal token bag (`TOKENS = { X: Symbol.for(...) }`), because object properties are not `unique symbol` and cannot key a binding map. Every composition root declares a `BindingMap` interface (token → bound type) and constructs `new TypedContainer<BindingMap>()`, so a mistyped bind or a resolve of an unbound token fails at compile time. Bind in the feature module. Do not use `@provide` or `*Port` naming.
13. Use `@posthog/quill` for rendering-layer primitives when available. Routing is TanStack Router contributed per feature.
13. Use `@posthog/quill` for rendering-layer primitives. Never import Radix — see [UI Components](#ui-components). Routing is TanStack Router contributed per feature.

Hard boundary: `@posthog/core` and `@posthog/ui` never import host transports. No `trpcClient`, `electron`, or `node:*`.

Hard boundary: no new `@radix-ui/*` imports anywhere in the repo.

## Import Direction

Enforced by Biome `noRestrictedImports`.
Expand Down Expand Up @@ -99,6 +103,8 @@ For each new file or meaningful change:

## Forbidden Patterns

- Any new `@radix-ui/*` import (`@radix-ui/themes`, `@radix-ui/react-*`). Use `@posthog/quill` plus `div` + Tailwind.
- Radix layout primitives (`Box`, `Flex`, `Grid`, `Section`, `Container`) in code you touch; replace them with `div`s.
- Business logic in store actions.
- Domain stores in `@posthog/ui`.
- `trpcClient` imports in `@posthog/core` or `@posthog/ui`.
Expand Down Expand Up @@ -222,6 +228,66 @@ All merges into `main` go through the Trunk merge queue. Never run `gh pr merge`
- Queue progress is the `Trunk Merge Queue (main)` check run on the PR's head commit. On failure the Trunk bot comments with links to the failing workflows; fix, push, and re-enqueue.
- Never force-push a branch while it is in the queue -- it removes the PR from the queue.

## UI Components

**Radix is banned. Do not add a `@radix-ui/*` import to any file, ever.** That covers
`@radix-ui/themes` and every `@radix-ui/react-*` package. There is no "just this once"
exception for matching surrounding code: the ~500 files that still import Radix are
legacy awaiting migration, not a pattern to copy. If a file you are editing already
imports Radix, that is a reason to remove those imports, not to add more.
Comment on lines +233 to +237

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Radix ban contradicts carve-outs

The unconditional ban on every @radix-ui/* import conflicts with the later instructions to retain the Radix Theme roots and token stylesheet. Qualify the ban wherever it is stated so agents do not remove sanctioned imports from ThemeWrapper.tsx, Storybook, existing themed tests, or globals.css, causing avoidable visual regressions and corrective work.

Context Used: Do not attempt to comment on incorrect alphabetica... (source)

Knowledge Base Used: @posthog/ui shared UI package

Prompt To Fix With AI
This is a comment left during a code review.
Path: AGENTS.md
Line: 233-237

Comment:
**Radix ban contradicts carve-outs**

The unconditional ban on every `@radix-ui/*` import conflicts with the later instructions to retain the Radix Theme roots and token stylesheet. Qualify the ban wherever it is stated so agents do not remove sanctioned imports from `ThemeWrapper.tsx`, Storybook, existing themed tests, or `globals.css`, causing avoidable visual regressions and corrective work.

**Context Used:** Do not attempt to comment on incorrect alphabetica... ([source](https://app.greptile.com/review/custom-context?memory=instruction-0))

**Knowledge Base Used:** [`@posthog/ui` shared UI package](https://app.greptile.com/posthog-org-19734/-/custom-context/knowledge-base/posthog/code/-/docs/ui-package.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!


Rendering primitives come from `@posthog/quill`. Layout comes from plain HTML elements
plus Tailwind.

### Replace Radix as you go

When you touch a component that imports Radix, migrate the parts you are working in.
Swap the whole file when the file is small or the change is broad; otherwise migrate at
least the elements your diff touches, and leave the file no more Radix-dependent than
you found it. Don't open a drive-by migration of an untouched file in an unrelated PR.

Layout primitives become `div`s (or the right semantic element) with Tailwind classes:

| Radix Themes | Replace with |
| --- | --- |
| `<Box p="2">` | `<div className="p-2">` |
| `<Flex align="center" gap="2">` | `<div className="flex items-center gap-2">` |
| `<Flex direction="column">` | `<div className="flex flex-col">` |
| `<Grid columns="2" gap="3">` | `<div className="grid grid-cols-2 gap-3">` |
| `<Section>`, `<Container>` | `<div>` / `<section>` + Tailwind |
| `<ScrollArea>` | `<div className="overflow-y-auto">` |
| `<Heading>` | quill `Heading`, or `<h1>`–`<h6>` + Tailwind |
| `<Code>`, `<Blockquote>` | `<code>`, `<blockquote>` + Tailwind |
| `<VisuallyHidden>` | `className="sr-only"` |
| `<Link>` | TanStack Router `Link` (in quill, `render={<Link … />}`) |

Everything else has a quill equivalent — import it from `@posthog/quill`:

| Radix Themes | quill |
| --- | --- |
| `Text` | `Text` |
| `Button`, `IconButton` | `Button` (`variant`/`size` props) |
| `TextField`, `TextArea` | `Input`, `Textarea`, `InputGroup*` |
| `Select`, `Checkbox`, `Switch`, `Slider`, `Progress` | same names |
| `SegmentedControl`, `RadioGroup` | `ToggleGroup` + `ToggleGroupItem`, or `Tabs` |
| `Badge`, `Avatar`, `Separator`, `Skeleton`, `Spinner`, `Kbd`, `Card`, `Table*`, `Tabs*` | same names |
| `Tooltip`, `Popover`, `Dialog`, `AlertDialog`, `DropdownMenu`, `ContextMenu` | same names, as `*Trigger` / `*Content` compounds |
| `Callout` | compose a `div` with Tailwind, or quill `Item*` |

No quill equivalent and no plain-HTML answer? Build it from HTML + Tailwind in the
feature, or add it to quill (see [`.claude/skills/quill-code/SKILL.md`](./.claude/skills/quill-code/SKILL.md)).
Pulling in a Radix package is not the fallback.

### The two carve-outs

- **CSS variables are not components.** Tailwind classes like `text-(--gray-12)`,
`bg-(--gray-2)`, and `rounded-(--radius-2)` come from Radix's *CSS token* layer and
stay. Keep using them; do not "de-Radix" a stylesheet.
- **The `<Theme>` root stays for now.** The app-level provider (`Providers.tsx`,
`.storybook/preview.tsx`) and the `<Theme>` wrapper in existing tests supply those
tokens. Leave them alone — removing them is a separate migration. Do not add
`<Theme>` to new files; new tests should not need it.

## Code Style

- Prefer local code over new dependencies for simple fixes.
Expand All @@ -245,7 +311,7 @@ See [docs/conventions.md](./docs/conventions.md).

## Key Libraries

- React 19, Radix UI Themes, Tailwind CSS, `@posthog/quill`
- React 19, Tailwind CSS, `@posthog/quill` (the component library — Radix is banned in new code, see [UI Components](#ui-components); Radix Themes remains only as the CSS-token layer and legacy imports pending migration)
- TanStack Query, TanStack Router
- Zustand, InversifyJS (with `@inversifyjs/strongly-typed`), Zod
- xterm.js, CodeMirror, Tiptap
Expand Down
8 changes: 5 additions & 3 deletions docs/conventions.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@ interface AgentMessageProps {

export function AgentMessage({ content }: AgentMessageProps) {
return (
<Box className="py-1 pl-3">
<div className="py-1 pl-3">
<MarkdownRenderer content={content} />
</Box>
</div>
);
}
```

Components come from `@posthog/quill`; layout is plain elements plus Tailwind. **Never import `@radix-ui/themes` or `@radix-ui/react-*`** — see [UI Components](../AGENTS.md#ui-components) for the full replacement mapping, including the layout primitives (`Box`, `Flex`, `Grid`) that become `div`s.

Keep render functions short. Extract named components for distinct UI concerns instead of long inline conditionals.

Group component hooks by concern:
Expand Down Expand Up @@ -134,7 +136,7 @@ Barrel files hide dependency edges, increase circular import risk, and make refa

## Styling

Use Tailwind first. The project uses Tailwind v4 with Radix CSS variables.
Use Tailwind first. The project uses Tailwind v4 with Radix CSS variables. Those *variables* are fine and stay; Radix *components* are banned (see [UI Components](../AGENTS.md#ui-components)).

Examples:

Expand Down
9 changes: 9 additions & 0 deletions packages/ui/src/features/browser-tabs/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ within it — because the identity differs only by `channelSection`.
Dedup/identity keys on all four fields, so two channels' inboxes (or one
channel's inbox vs artifacts) are distinct tabs.

## Components & styling

Components come from `@posthog/quill`; layout is `div`s with Tailwind. **Radix is
banned** — do not add `@radix-ui/themes` or `@radix-ui/react-*` to any file here. See
[UI Components](../../../../../AGENTS.md#ui-components) in the root `AGENTS.md` for the
mapping. The tab strip's remaining `Flex`/`Box`/`Text` imports are legacy; when your
change lands in one of those files, convert what you touch (`<Flex align="center"
gap="2">` → `<div className="flex items-center gap-2">`, `Text` → quill `Text`).

## Where the logic lives

The feature is deliberately split so the rules are portable and testable:
Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/features/browser-tabs/CLAUDE.md
13 changes: 8 additions & 5 deletions packages/ui/src/features/canvas/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ The root `AGENTS.md` architecture rules still apply.

## Components & styling

- **Use `@posthog/quill`, not Radix.** New UI in this space pulls components from
- **Use `@posthog/quill`, never Radix.** All UI in this space pulls components from
`@posthog/quill` (`Button`, `Dialog*`, `AlertDialog*`, `DropdownMenu*`,
`ContextMenu*`, `Tooltip*`, `Collapsible*`, …). Do **not** reach for
`@radix-ui/themes` or `@radix-ui/react-*`. Some older code here still imports
`ContextMenu*`, `Tooltip*`, `Collapsible*`, …). Adding a `@radix-ui/themes` or
`@radix-ui/react-*` import is banned — see [UI Components](../../../../../AGENTS.md#ui-components)
in the root `AGENTS.md` for the full mapping. Some older code here still imports
`@radix-ui/themes` (`Box`, `Flex`, `Text`, `AlertDialog`) — that's legacy to be
migrated, not a pattern to copy. When you touch such code, prefer swapping to
the Quill equivalent.
migrated, not a pattern to copy. When you touch such code, replace it: layout
primitives (`Box`, `Flex`, `Grid`) become `div`s with Tailwind classes
(`<Flex align="center" gap="2">` → `<div className="flex items-center gap-2">`),
and everything else becomes its Quill equivalent.
- **Don't restyle Quill internals.** Quill components are already themed —
spacing, typography, and especially **color** are baked in. Do not add
`text-gray-*` / `text-muted-foreground` / `font-*` or other color/typography
Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/features/canvas/CLAUDE.md
Loading