Reference conventions for React client components — Tailwind CSS, UI primitives (Dialog, Tooltip, Menu), constants, HTML sanitization, and component design patterns. Use when building or reviewing UI components in packages/client.
styled, css from @emotion/styled / @emotion/react).lint/nursery/useSortedClasses). After modifying a file, run pnpm exec biome check --write <file> to auto-fix. Do not use pnpm biome check --write — the biome script already includes check, so it would expand to biome check check --write and fail.font-*, text-*, capitalize, leading-*, color) cascade naturally; structural properties (flex, w-*, ) usually stay on the element they control.p-*Use the radix-ui based components in packages/client/ui/:
| Use case | Components |
|---|---|
| Modal / dialog | Dialog, DialogContent, DialogTitle, DialogActions, DialogOverlay, DialogClose from ui/Dialog/ |
| Alert/confirm dialog | AlertDialog and sub-components from ui/AlertDialog/ |
| Tooltip | Tooltip, TooltipTrigger, TooltipContent from ui/Tooltip/ |
| Dropdown menu | Menu, MenuItem, MenuContent etc. from ui/Menu/ |
| Select | Select, SelectContent, SelectItem etc. from ui/Select/ |
Do NOT use DashModal (packages/client/components/Dashboard/DashModal.tsx) — it is deprecated.
Dialog open state can be managed with useDialogState from ui/Dialog/useDialogState.tsx, or passed as isOpen/onClose props to Dialog. Prefer always rendering the Dialog and controlling via isOpen rather than conditionally mounting it.
constEnums.ts (packages/client/types/constEnums.ts) — it is deprecated.packages/client/utils/constants.ts as plain export const values.sanitizeExternalHtml() before dangerouslySetInnerHTML. Content from external sources (Jira, GitHub, GitLab, Azure DevOps, Linear, user reflections) must be sanitized via sanitizeExternalHtml() from packages/client/utils/sanitizeExternalHtml.ts. It uses DOMPurify with a hook that forces links to target="_blank" rel="noopener noreferrer" and blocks <style> tags.// Good
<div dangerouslySetInnerHTML={{__html: sanitizeExternalHtml(descriptionHTML)}} />
// Bad — XSS risk
<div dangerouslySetInnerHTML={{__html: descriptionHTML}} />
onPointerDown instead of onMouseDown + onTouchStart. The unified pointer API handles mouse, touch, and pen.useMemo for expensive computationsuseCallback for event handlers passed to child componentsuseEffect when values haven't changed (e.g. if (cellValue !== value))useState initialization for hot-path components: useState(() => expensiveComputation()) not useState(expensiveComputation()). The thunk runs only on mount, not every render.maxLength on all input fields. Prevent users from pasting megabytes of text into cells.package.json. Don't install the same package in multiple package.json files — this causes version conflicts (e.g. TipTap extensions with different versions on server vs client).data.ts — prefer tableOps.ts, transforms.ts, etc.data-is-database not data-isDatabase.brew install stripe/stripe-cli/stripe and then stripe login to get the port forwarder up and runningstripe listen --forward-to https://localhost:3000/stripe --skip-verify to forward Stripe events to your local server.