|
| 1 | +# AGENTS — Reactist |
| 2 | + |
| 3 | +> **How this file is organized.** Everything above the `shared-config:agents` HTML comment marker is specific to Reactist and is maintained locally in this repo. Everything below the marker is synced from Doist/shared-configs and must not be edited directly, as it will be overwritten on the next sync. |
| 4 | +
|
| 5 | +Reactist is a React component library (`@doist/reactist`) — accessible UI components for Doist products. |
| 6 | + |
| 7 | +## Repository-specific rules |
| 8 | + |
| 9 | +### React Compiler is enabled — read before touching components/hooks |
| 10 | + |
| 11 | +Before adding or editing any React component or hook, consult [docs/react-guidelines/react-compiler.md](docs/react-guidelines/react-compiler.md). Do **not** add `useMemo`, `useCallback`, or `React.memo` by default — the compiler handles memoization. Files the compiler already optimizes are the ones **not** listed in `.react-compiler.rec.json`; check violations with the tracker, not ESLint (`npx @doist/react-compiler-tracker --check-files --show-errors <file>`), and do not skip the pre-commit hook that updates the record file. |
| 12 | + |
| 13 | +## Commands |
| 14 | + |
| 15 | +```bash |
| 16 | +npm test # Run Jest tests |
| 17 | +npm run test:watch # Jest in watch mode |
| 18 | +npm run lint # ESLint |
| 19 | +npm run type-check # TypeScript type checking |
| 20 | +npm run validate # lint + type-check + test (all three) |
| 21 | +npm run build # Rollup build (es/, lib/, dist/) |
| 22 | +npm run storybook # Storybook dev server on :6006 |
| 23 | +npm run plop component # Scaffold a new component |
| 24 | +npm run prettify # Format all files with Prettier |
| 25 | +``` |
| 26 | + |
| 27 | +Run `npm run validate` after meaningful changes to catch issues early. |
| 28 | + |
| 29 | +## Project structure |
| 30 | + |
| 31 | +``` |
| 32 | +src/ |
| 33 | + <component-name>/ # One directory per component |
| 34 | + index.ts # Re-exports from the main file |
| 35 | + component-name.tsx # Implementation |
| 36 | + component-name.module.css # CSS Modules styles |
| 37 | + component-name.test.tsx # Tests |
| 38 | + component-name.stories.mdx # Storybook docs (or .tsx) |
| 39 | + styles/ |
| 40 | + design-tokens.css # Global CSS custom properties (--reactist-*) |
| 41 | + utils/ |
| 42 | + common-types.ts # Shared types (Space, Tone, ObfuscatedClassName, etc.) |
| 43 | + responsive-props.ts # Responsive prop utilities |
| 44 | + polymorphism.ts # Polymorphic component types (deprecated) |
| 45 | + test-helpers.tsx # Test utilities (flushMicrotasks, TestIcon, runSpaceTests) |
| 46 | + hooks/ # Custom hooks (usePrevious) |
| 47 | + index.ts # Root export file - all components exported here |
| 48 | +``` |
| 49 | + |
| 50 | +## Component conventions |
| 51 | + |
| 52 | +### File structure |
| 53 | + |
| 54 | +Every component has `index.ts`, `component.tsx`, `component.module.css`, `component.test.tsx`, and a story file. Use `npm run plop component` to scaffold new components. |
| 55 | + |
| 56 | +### Implementation patterns |
| 57 | + |
| 58 | +For generic JSX conventions, named effect callbacks, and component typing, see [docs/react-guidelines/conventions.md](docs/react-guidelines/conventions.md). Reactist-specific patterns: |
| 59 | + |
| 60 | +- Import React as `import * as React from 'react'` |
| 61 | +- Use `React.forwardRef` with a named function matching the component name: |
| 62 | + ```tsx |
| 63 | + const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(function Button( |
| 64 | + { variant, size = 'normal', ...props }, |
| 65 | + ref, |
| 66 | + ) { ... }) |
| 67 | + ``` |
| 68 | +- Use named exports only - no default exports for design system components |
| 69 | +- Export types explicitly: `export type { ButtonProps }` |
| 70 | +- Add JSDoc comments to every prop |
| 71 | +- Use `exceptionallySetClassName` instead of `className` (via `ObfuscatedClassName` type) to discourage custom styling |
| 72 | +- Use `classNames()` from the `classnames` package for conditional classes |
| 73 | +- Use `aria-disabled` instead of HTML `disabled` for soft disable (keeps element focusable) |
| 74 | + |
| 75 | +### Exports |
| 76 | + |
| 77 | +New components must be added to `src/index.ts`. Components are grouped by category (layout, alerts, typography, links, form fields, other). |
| 78 | + |
| 79 | +### Styling |
| 80 | + |
| 81 | +- CSS Modules with `.module.css` files (plain CSS). Legacy components under `src/components/` use `.less` but new components should use CSS Modules |
| 82 | +- Use CSS custom properties from `src/styles/design-tokens.css` (prefixed `--reactist-*`) |
| 83 | +- Component-specific tokens go in the component's own CSS file under `:root` |
| 84 | +- Class naming: `variant-primary`, `size-small`, `tone-destructive` (dash-separated) |
| 85 | +- Responsive: mobile-first with breakpoints at 768px (tablet) and 992px (desktop) |
| 86 | +- No CSS nesting - keep selectors flat |
| 87 | + |
| 88 | +### Accessibility |
| 89 | + |
| 90 | +- Build on `@ariakit/react` primitives for interactive components |
| 91 | +- Use semantic HTML and ARIA attributes |
| 92 | +- Icon-only buttons require `aria-label` |
| 93 | +- Test with `jest-axe` - every component should have an a11y test |
| 94 | +- Use `react-focus-lock` and `aria-hidden` for modals |
| 95 | + |
| 96 | +## Testing conventions |
| 97 | + |
| 98 | +For generic React Testing Library patterns (component tests, hook tests, provider wrappers, querying by role, `userEvent` over `fireEvent`), see [docs/react-guidelines/testing.md](docs/react-guidelines/testing.md). Reactist-specific helpers: |
| 99 | + |
| 100 | +- Use `flushMicrotasks()` from `src/utils/test-helpers.tsx` after ariakit component interactions |
| 101 | +- Use `jest-axe` for accessibility checks: |
| 102 | + ```tsx |
| 103 | + const { container } = render(<Component />) |
| 104 | + expect(await axe(container)).toHaveNoViolations() |
| 105 | + ``` |
| 106 | +- Use `TestIcon` from test-helpers when a test needs an icon element |
| 107 | +- Use `runSpaceTests()` from test-helpers for components that accept a `space` prop |
| 108 | + |
| 109 | +## Commits and PRs |
| 110 | + |
| 111 | +- PR titles must follow [conventional commits](https://www.conventionalcommits.org/): `feat:`, `fix:`, `chore:`, `refactor:`, `test:`, `docs:`, etc. |
| 112 | +- `feat:` triggers a minor version bump, `fix:` triggers a patch, `feat!:` or `fix!:` triggers a major (breaking) |
| 113 | +- Versioning is automated by release-please based on PR title |
| 114 | +- Pre-commit hooks run Prettier, ESLint, and React Compiler tracking - do not skip them |
| 115 | + |
| 116 | +<!-- shared-config:agents --> |
| 117 | + |
1 | 118 | # AGENTS |
2 | 119 |
|
3 | 120 | > [!WARNING] |
@@ -104,3 +221,5 @@ When asked to create a pull request, follow this mandatory process: |
104 | 221 | ## Enforcement |
105 | 222 |
|
106 | 223 | These guidelines are **non-negotiable**. Deviation from this process constitutes a failure to follow core instructions. When in doubt, ask for clarification. |
| 224 | + |
| 225 | +<!-- /shared-config:agents --> |
0 commit comments