diff --git a/products/desktop/.claude/skills/storybook-stories/SKILL.md b/products/desktop/.claude/skills/storybook-stories/SKILL.md index 9214c9db944d..7cd6d2bd18e2 100644 --- a/products/desktop/.claude/skills/storybook-stories/SKILL.md +++ b/products/desktop/.claude/skills/storybook-stories/SKILL.md @@ -23,7 +23,10 @@ should **not** add its own providers or ``: `ServiceProvider`, and a minimal TanStack Router. So `useHostTRPC()`, `useService()`, `useRouterState()`, etc. render instead of throwing "must be used within a Provider". -- A `` (Radix) bound to the dark/light toolbar global. +- A `` (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). diff --git a/products/desktop/AGENTS.md b/products/desktop/AGENTS.md index 9ec5ba94189a..950c6be10099 100644 --- a/products/desktop/AGENTS.md +++ b/products/desktop/AGENTS.md @@ -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 `/AGENTS.md` with a `/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. @@ -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..")` 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()`, 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`. @@ -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`. @@ -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. + +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 | +| --- | --- | +| `` | `
` | +| `` | `
` | +| `` | `
` | +| `` | `
` | +| `
`, `` | `
` / `
` + Tailwind | +| `` | `
` | +| `` | quill `Heading`, or `

`–`

` + Tailwind | +| ``, `
` | ``, `
` + Tailwind | +| `` | `className="sr-only"` | +| `` | TanStack Router `Link` (in quill, `render={}`) | + +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 `` root stays for now.** The app-level provider (`Providers.tsx`, + `.storybook/preview.tsx`) and the `` wrapper in existing tests supply those + tokens. Leave them alone — removing them is a separate migration. Do not add + `` to new files; new tests should not need it. + ## Code Style - Prefer local code over new dependencies for simple fixes. @@ -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 diff --git a/products/desktop/docs/conventions.md b/products/desktop/docs/conventions.md index d09774ce7854..88f4ca2a1d5d 100644 --- a/products/desktop/docs/conventions.md +++ b/products/desktop/docs/conventions.md @@ -28,13 +28,15 @@ interface AgentMessageProps { export function AgentMessage({ content }: AgentMessageProps) { return ( - +
- +
); } ``` +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: @@ -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: diff --git a/products/desktop/packages/ui/src/features/browser-tabs/AGENTS.md b/products/desktop/packages/ui/src/features/browser-tabs/AGENTS.md index 46e368140134..72d2aaff0139 100644 --- a/products/desktop/packages/ui/src/features/browser-tabs/AGENTS.md +++ b/products/desktop/packages/ui/src/features/browser-tabs/AGENTS.md @@ -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 (`` → `
`, `Text` → quill `Text`). + ## Where the logic lives The feature is deliberately split so the rules are portable and testable: diff --git a/products/desktop/packages/ui/src/features/canvas/AGENTS.md b/products/desktop/packages/ui/src/features/canvas/AGENTS.md index 897be5e30739..0bc5e89dfef0 100644 --- a/products/desktop/packages/ui/src/features/canvas/AGENTS.md +++ b/products/desktop/packages/ui/src/features/canvas/AGENTS.md @@ -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 + (`` → `
`), + 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 diff --git a/products/desktop/packages/ui/src/features/inbox/AGENTS.md b/products/desktop/packages/ui/src/features/inbox/AGENTS.md index 7ac2c6f467b9..5c819bb064c3 100644 --- a/products/desktop/packages/ui/src/features/inbox/AGENTS.md +++ b/products/desktop/packages/ui/src/features/inbox/AGENTS.md @@ -130,8 +130,11 @@ Shared primitives exist to keep the surfaces consistent: When adding or changing UI, reuse those primitives first. Avoid encoding one-off layout systems inside a tab component. +Components come from `@posthog/quill`; layout is `div`s with Tailwind. Radix is banned — see [UI Components](../../../../../AGENTS.md#ui-components) in the root `AGENTS.md`. Inbox files that still import `@radix-ui/themes` are legacy: when you edit one, drop the Radix import rather than extending it (`Flex`/`Box` → `div` + Tailwind, `Text`/`Button`/`Badge`/`Tooltip` → the quill component of the same name). + ## Things to Avoid +- Do not add any `@radix-ui/*` import. Use `@posthog/quill` plus `div` + Tailwind. - Do not reuse the deleted legacy `ReportListRow`, `ReportDetailPane`, or old list/detail stores. - Do not put page-level Inbox title or navigation into the global app header; `InboxView` owns the Inbox page chrome. - Do not add a configure shortcut back into the Inbox header; Responders configuration is a sidebar destination.