|
| 1 | +#Frontend Development Guidelines |
| 2 | + |
| 3 | +##Bash commands |
| 4 | + |
| 5 | +-`pnpm dev` - Start Vite development server |
| 6 | +-`pnpm storybook --no-open` - Run storybook tests |
| 7 | +-`pnpm test` - Run jest unit tests |
| 8 | +-`pnpm test -- path/to/specific.test.ts` - Run a single test file |
| 9 | +-`pnpm lint` - Run complete linting suite (Biome + TypeScript + circular deps + knip) |
| 10 | +-`pnpm lint:fix` - Auto-fix linting issues where possible |
| 11 | +-`pnpm playwright:test` - Run playwright e2e tests. When running e2e tests, remind the user that a license is required to run all the tests |
| 12 | +-`pnpm format` - Format frontend code. Always run before creating a PR |
| 13 | + |
| 14 | +##Components |
| 15 | + |
| 16 | +- MUI components are deprecated - migrate away from these when encountered |
| 17 | +- Use shadcn/ui components first - check`site/src/components` for existing implementations. |
| 18 | +- Do not use shadcn CLI - manually add components to maintain consistency |
| 19 | +- The modules folder should contain components with business logic specific to the codebase. |
| 20 | +- Create custom components only when shadcn alternatives don't exist |
| 21 | + |
| 22 | +##Styling |
| 23 | + |
| 24 | +- Emotion CSS is deprecated. Use Tailwind CSS instead. |
| 25 | +- Use custom Tailwind classes in tailwind.config.js. |
| 26 | +- Tailwind CSS reset is currently not used to maintain compatibility with MUI |
| 27 | +- Responsive design - use Tailwind's responsive prefixes (sm:, md:, lg:, xl:) |
| 28 | +- Do not use`dark:` prefix for dark mode |
| 29 | + |
| 30 | +##Tailwind Best Practices |
| 31 | + |
| 32 | +- Group related classes |
| 33 | +- Use semantic color names from the theme inside`tailwind.config.js` including`content`,`surface`,`border`,`highlight` semantic tokens |
| 34 | +- Prefer Tailwind utilities over custom CSS when possible |
| 35 | + |
| 36 | +##General Code style |
| 37 | + |
| 38 | +- Use ES modules (import/export) syntax, not CommonJS (require) |
| 39 | +- Destructure imports when possible (eg. import { foo } from 'bar') |
| 40 | +- Prefer`for...of` over`forEach` for iteration |
| 41 | +-**Biome** handles both linting and formatting (not ESLint/Prettier) |
| 42 | + |
| 43 | +##Workflow |
| 44 | + |
| 45 | +- Be sure to typecheck when you’re done making a series of code changes |
| 46 | +- Prefer running single tests, and not the whole test suite, for performance |
| 47 | +- Some e2e tests require a license from the user to execute |
| 48 | +- Use pnpm format before creating a PR |
| 49 | + |
| 50 | +##Pre-PR Checklist |
| 51 | + |
| 52 | +1.`pnpm check` - Ensure no TypeScript errors |
| 53 | +2.`pnpm lint` - Fix linting issues |
| 54 | +3.`pnpm format` - Format code consistently |
| 55 | +4.`pnpm test` - Run affected unit tests |
| 56 | +5. Visual check in Storybook if component changes |
| 57 | + |
| 58 | +##Migration (MUI → shadcn) (Emotion → Tailwind) |
| 59 | + |
| 60 | +###Migration Strategy |
| 61 | + |
| 62 | +- Identify MUI components in current feature |
| 63 | +- Find shadcn equivalent in existing components |
| 64 | +- Create wrapper if needed for missing functionality |
| 65 | +- Update tests to reflect new component structure |
| 66 | +- Remove MUI imports once migration complete |
| 67 | + |
| 68 | +###Migration Guidelines |
| 69 | + |
| 70 | +- Use Tailwind classes for all new styling |
| 71 | +- Replace Emotion`css` prop with Tailwind classes |
| 72 | +- Leverage custom color tokens:`content-primary`,`surface-secondary`, etc. |
| 73 | +- Use`className` with`clsx` for conditional styling |
| 74 | + |
| 75 | +##React Rules |
| 76 | + |
| 77 | +###1. Purity & Immutability |
| 78 | + |
| 79 | +-**Components and custom Hooks must be pure and idempotent**—same inputs → same output; move side-effects to event handlers or Effects. |
| 80 | +-**Never mutate props, state, or values returned by Hooks.** Always create new objects or use the setter from useState. |
| 81 | + |
| 82 | +###2. Rules of Hooks |
| 83 | + |
| 84 | +-**Only call Hooks at the top level** of a function component or another custom Hook—never in loops, conditions, nested functions, or try / catch. |
| 85 | +-**Only call Hooks from React functions.** Regular JS functions, classes, event handlers, useMemo, etc. are off-limits. |
| 86 | + |
| 87 | +###3. React orchestrates execution |
| 88 | + |
| 89 | +-**Don’t call component functions directly; render them via JSX.** This keeps Hook rules intact and lets React optimize reconciliation. |
| 90 | +-**Never pass Hooks around as values or mutate them dynamically.** Keep Hook usage static and local to each component. |
| 91 | + |
| 92 | +###4. State Management |
| 93 | + |
| 94 | +- After calling a setter you’ll still read the**previous** state during the same event; updates are queued and batched. |
| 95 | +- Use**functional updates** (setX(prev ⇒ …)) whenever next state depends on previous state. |
| 96 | +- Pass a function to useState(initialFn) for**lazy initialization**—it runs only on the first render. |
| 97 | +- If the next state is Object.is-equal to the current one, React skips the re-render. |
| 98 | + |
| 99 | +###5. Effects |
| 100 | + |
| 101 | +- An Effect takes a**setup** function and optional**cleanup**; React runs setup after commit, cleanup before the next setup or on unmount. |
| 102 | +- The**dependency array must list every reactive value** referenced inside the Effect, and its length must stay constant. |
| 103 | +- Effects run**only on the client**, never during server rendering. |
| 104 | +- Use Effects solely to**synchronize with external systems**; if you’re not “escaping React,” you probably don’t need one. |
| 105 | + |
| 106 | +###6. Lists & Keys |
| 107 | + |
| 108 | +- Every sibling element in a list**needs a stable, unique key prop**. Never use array indexes or Math.random(); prefer data-driven IDs. |
| 109 | +- Keys aren’t passed to children and**must not change between renders**; if you return multiple nodes per item, use`<Fragment key={id}>` |
| 110 | + |
| 111 | +###7. Refs & DOM Access |
| 112 | + |
| 113 | +- useRef stores a mutable .current**without causing re-renders**. |
| 114 | +-**Don’t call Hooks (including useRef) inside loops, conditions, or map().** Extract a child component instead. |
| 115 | +-**Avoid reading or mutating refs during render;** access them in event handlers or Effects after commit. |