Skip to content

Commit 1f0280e

Browse files
committed
docs(site): document critical frontend pattern migrations
Add comprehensive documentation for active frontend pattern migrations discovered through analysis of 200+ git commits touching site/ files: Core Migrations: - Emotion → Tailwind CSS (strict 'no new emotion' policy) - MUI Components → Custom/Radix/shadcn (Tooltips, Tables, Buttons) - MUI Icons → lucide-react with specific icon mappings - spyOn → queries parameter for GET endpoint mocks in Storybook - localStorage → user_configs table for user preferences New Documentation: - Icon migration mappings (BusinessIcon→Building2Icon, etc.) - Radix component prop naming conventions (placement→side) - cn() utility usage for conditional className merging - Chromatic testing best practices (prefer snapshots over assertions) Includes concrete before/after examples and migration patterns to guide developers away from deprecated approaches toward current best practices. Analysis based on PRs: #20948, #20946, #20938, #20905, #20900, #20869, #20849, #20808, #20530, #20479, #20261, #20201, #20200, #20193, #20318 --- 🤖 This change was written by Claude Sonnet 4.5 Thinking using mux and reviewed by a human 🏂
1 parent b7d8918 commit 1f0280e

File tree

1 file changed

+156
-5
lines changed

1 file changed

+156
-5
lines changed

site/CLAUDE.md

Lines changed: 156 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# Frontend Development Guidelines
22

3+
## 🚨 Critical Pattern Migrations (MUST FOLLOW)
4+
5+
The following patterns are actively being migrated and have **STRICT policies**:
6+
7+
1. **Emotion → Tailwind**: "No new emotion styles, full stop" - Always use Tailwind CSS
8+
2. **MUI Components → Custom/Radix Components**: Replace MUI components (Tooltips, Tables, Buttons) with custom/shadcn equivalents
9+
3. **MUI Icons → lucide-react**: All icons must use lucide-react, never MUI icons
10+
4. **spyOn → queries parameter**: Use `queries` in story parameters for GET endpoint mocks
11+
5. **localStorage → user_configs**: Store user preferences in backend, not browser storage
12+
13+
When touching existing code, **"leave the campsite better than you found it"** - refactor old patterns to new ones even if not directly related to your changes.
14+
315
## TypeScript LSP Navigation (USE FIRST)
416

517
When investigating or editing TypeScript/React code, always use the TypeScript language server tools for accurate navigation:
@@ -26,25 +38,104 @@ When investigating or editing TypeScript/React code, always use the TypeScript l
2638

2739
## Components
2840

29-
- MUI components are deprecated - migrate away from these when encountered
30-
- Use shadcn/ui components first - check `site/src/components` for existing implementations.
41+
- **MUI components are deprecated** - migrate away from these when encountered
42+
- Replace `@mui/material/Tooltip` with custom `Tooltip` component (Radix-based)
43+
- Default 100ms delay via global tooltip provider
44+
- Use `delayDuration={0}` when immediate tooltip needed
45+
- Replace MUI Tables with custom table components
46+
- Replace MUI Buttons with shadcn Button components
47+
- Systematically replace MUI components with custom/shadcn equivalents
48+
- Use shadcn/ui components first - check `site/src/components` for existing implementations
3149
- Do not use shadcn CLI - manually add components to maintain consistency
32-
- The modules folder should contain components with business logic specific to the codebase.
50+
- The modules folder should contain components with business logic specific to the codebase
3351
- Create custom components only when shadcn alternatives don't exist
3452

53+
### Icon Migration: MUI Icons → lucide-react
54+
55+
**STRICT POLICY**: All icons must use `lucide-react`, not MUI icons.
56+
57+
```tsx
58+
// OLD - MUI Icons (DO NOT USE)
59+
import BusinessIcon from "@mui/icons-material/Business";
60+
import GroupOutlinedIcon from "@mui/icons-material/GroupOutlined";
61+
import PublicOutlinedIcon from "@mui/icons-material/PublicOutlined";
62+
63+
// NEW - lucide-react
64+
import {
65+
Building2Icon,
66+
UsersIcon,
67+
GlobeIcon,
68+
} from "lucide-react";
69+
```
70+
71+
**Common icon mappings:**
72+
- `BusinessIcon``Building2Icon`
73+
- `GroupOutlinedIcon` / `GroupIcon``UsersIcon`
74+
- `PublicOutlinedIcon` / `PublicIcon``GlobeIcon`
75+
- `PersonIcon``UserIcon`
76+
- Always use descriptive lucide-react icons over generic MUI icons
77+
78+
### MUI → Radix Component Prop Naming
79+
80+
When migrating from MUI to Radix components, prop names change:
81+
82+
```tsx
83+
// MUI Tooltip props
84+
<Tooltip placement="top" PopperProps={...}>
85+
86+
// Radix Tooltip props
87+
<Tooltip side="top"> // placement → side
88+
// PopperProps is removed (internal implementation detail)
89+
```
90+
91+
**Common prop name changes:**
92+
- `placement` → `side` (for positioning)
93+
- Remove `PopperProps` (internal implementation, not needed)
94+
- MUI's `title` prop → Radix uses children pattern with `TooltipContent`
95+
3596
## Styling
3697

37-
- Emotion CSS is deprecated. Use Tailwind CSS instead.
38-
- Use custom Tailwind classes in tailwind.config.js.
98+
- **Emotion CSS is STRICTLY DEPRECATED: "no new emotion styles, full stop"**
99+
- Never use `@emotion/react`, `css` prop, `useTheme()`, or emotion styled components
100+
- Always use Tailwind CSS utility classes instead
101+
- When touching code with emotion styles, refactor to Tailwind ("leave the campsite better than you found it")
102+
- Use custom Tailwind classes in tailwind.config.js
39103
- Tailwind CSS reset is currently not used to maintain compatibility with MUI
40104
- Responsive design - use Tailwind's responsive prefixes (sm:, md:, lg:, xl:)
41105
- Do not use `dark:` prefix for dark mode
42106

107+
### Common Emotion → Tailwind Migrations
108+
109+
```tsx
110+
// OLD - Emotion (DO NOT USE)
111+
import { type Interpolation, type Theme, useTheme } from "@emotion/react";
112+
<div css={styles.container}>
113+
<Stack direction="row" spacing={3}>
114+
<span css={{ fontWeight: 500, color: theme.experimental.l1.text }}>
115+
116+
// NEW - Tailwind
117+
<div className="flex flex-col gap-2">
118+
<div className="flex items-center gap-6">
119+
<span className="font-medium text-content-primary">
120+
```
121+
122+
**Common replacements:**
123+
- `css={visuallyHidden}` → `className="sr-only"`
124+
- `Stack` component → flex with Tailwind classes (`flex`, `flex-col`, `flex-row`, `gap-*`)
125+
- Theme colors → Tailwind semantic tokens (`text-content-primary`, `bg-surface-secondary`, `border-border-default`)
126+
- Icons: use lucide-react with `size-icon-sm`, `size-icon-xs` classes
127+
43128
## Tailwind Best Practices
44129

45130
- Group related classes
46131
- Use semantic color names from the theme inside `tailwind.config.js` including `content`, `surface`, `border`, `highlight` semantic tokens
47132
- Prefer Tailwind utilities over custom CSS when possible
133+
- For conditional classes, use the `cn()` utility (from `utils/cn`) which combines `clsx` and `tailwind-merge`
134+
```tsx
135+
import { cn } from "utils/cn";
136+
137+
<div className={cn("base-classes", condition && "conditional-classes", className)} />
138+
```
48139

49140
## General Code style
50141

@@ -53,6 +144,66 @@ When investigating or editing TypeScript/React code, always use the TypeScript l
53144
- Prefer `for...of` over `forEach` for iteration
54145
- **Biome** handles both linting and formatting (not ESLint/Prettier)
55146

147+
## Testing Patterns
148+
149+
### Storybook: spyOn → queries parameter (for GET endpoint mocks)
150+
151+
**PREFERRED PATTERN**: Use `queries` parameter in story parameters instead of `spyOn` for GET endpoint mocks.
152+
153+
```tsx
154+
// OLD - spyOn pattern (AVOID for GET mocks)
155+
beforeEach: () => {
156+
spyOn(API, "getUsers").mockResolvedValue({
157+
users: MockUsers,
158+
count: MockUsers.length,
159+
});
160+
spyOn(API, "getTemplates").mockResolvedValue([MockTemplate]);
161+
}
162+
163+
// NEW - queries parameter pattern (PREFERRED)
164+
parameters: {
165+
queries: [
166+
{
167+
key: usersKey({ q: "" }),
168+
data: {
169+
users: MockUsers,
170+
count: MockUsers.length,
171+
},
172+
},
173+
{
174+
key: getTemplatesQueryKey({ q: "has-ai-task:true" }),
175+
data: [MockTemplate],
176+
},
177+
],
178+
}
179+
```
180+
181+
**Important notes:**
182+
- This applies specifically to GET endpoint mocks in Storybook stories
183+
- `spyOn` is still used for other mock types (POST, PUT, DELETE, non-GET endpoints)
184+
- Must import the correct query key functions (e.g., `usersKey`, `getTemplatesQueryKey`)
185+
186+
### Chromatic/Storybook Testing Best Practices
187+
188+
- **Prefer visual validation through snapshots** over programmatic assertions
189+
- Chromatic snapshots catch visual changes during review
190+
- Avoid programmatic assertions in stories that duplicate what snapshots show
191+
- Programmatic assertions can introduce flakiness - remove when redundant
192+
- Stories are snapshot tests - rely on the screenshot to verify correctness
193+
194+
## State Storage
195+
196+
### localStorage vs user_configs table
197+
198+
**IMPORTANT**: For user preferences that should persist across devices and browsers, use the `user_configs` table in the backend, NOT `localStorage`.
199+
200+
- **localStorage is browser-specific**, not user-specific
201+
- **User preferences should persist** across devices/browsers
202+
- Follow the plumbing for `theme_preference` as a reference example
203+
- localStorage may be acceptable only for truly transient UI state that doesn't need to follow the user
204+
205+
**Key principle**: If a user dismisses something or sets a preference, it should be tied to their account, not their browser.
206+
56207
## Workflow
57208

58209
- Be sure to typecheck when you're done making a series of code changes

0 commit comments

Comments
 (0)