From d93c30474f4efcc86636729ad5b1e5a47d0873e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Aubert?= Date: Fri, 12 Jun 2026 17:54:45 +0200 Subject: [PATCH 1/2] Add LLM guidelines and gitar review guidelines --- .claude/skills/echoes-review/SKILL.md | 120 ++++++++++++++++++++++++++ .gitar/review/accessibility.md | 7 ++ .gitar/review/component-patterns.md | 12 +++ .gitar/review/design-tokens.md | 8 ++ .gitar/review/folder-and-exports.md | 6 ++ .gitar/review/general-design.md | 19 ++++ .gitar/review/localization.md | 5 ++ .gitar/review/naming-and-api.md | 11 +++ .gitar/review/radix-ui.md | 4 + .gitar/review/stories-and-figma.md | 5 ++ .gitar/review/testing.md | 8 ++ .gitignore | 8 +- AGENT.md | 62 +++++++++++++ CLAUDE.md | 1 + 14 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 .claude/skills/echoes-review/SKILL.md create mode 100644 .gitar/review/accessibility.md create mode 100644 .gitar/review/component-patterns.md create mode 100644 .gitar/review/design-tokens.md create mode 100644 .gitar/review/folder-and-exports.md create mode 100644 .gitar/review/general-design.md create mode 100644 .gitar/review/localization.md create mode 100644 .gitar/review/naming-and-api.md create mode 100644 .gitar/review/radix-ui.md create mode 100644 .gitar/review/stories-and-figma.md create mode 100644 .gitar/review/testing.md create mode 100644 AGENT.md create mode 120000 CLAUDE.md diff --git a/.claude/skills/echoes-review/SKILL.md b/.claude/skills/echoes-review/SKILL.md new file mode 100644 index 000000000..9fe7d4770 --- /dev/null +++ b/.claude/skills/echoes-review/SKILL.md @@ -0,0 +1,120 @@ +--- +description: Echoes review — analyse your current branch or an existing PR for naming violations, a11y gaps, design-token misuse, missing exports, and architectural issues in the Echoes React component library. +--- + +Run echoes review: $ARGUMENTS + +Parse arguments: + +- `--pr ` — review a specific PR's diff instead of the current branch; accepts a bare number (`123`) or a full GitHub URL — extract the number from the URL path before proceeding; `--base` is ignored when this is set since `gh pr diff` always diffs against the PR's own base branch +- `--base ` — override the base branch when diffing the current branch (default: auto-detected main or master); has no effect when `--pr` is provided + +--- + +## Step 1: Load review guidelines + +Read all files under `.gitar/review/` — they contain the authoritative rules for this review: + +@.gitar/review/general-design.md +@.gitar/review/naming-and-api.md +@.gitar/review/component-patterns.md +@.gitar/review/folder-and-exports.md +@.gitar/review/radix-ui.md +@.gitar/review/design-tokens.md +@.gitar/review/accessibility.md +@.gitar/review/testing.md +@.gitar/review/localization.md +@.gitar/review/stories-and-figma.md + +--- + +## Step 2: Get the diff + +The two modes use **different data sources**. Do not mix them. + +### Mode A: `--pr` + +```bash +git fetch origin pull/{pr}/head:pr-{pr} + +gh pr diff {pr} --name-only +gh pr diff {pr} +``` + +For each changed file that looks architecturally significant (component files, index exports, stories, tests, figma-connect files, design token files), read its full content from the fetched PR ref: + +```bash +git show pr-{pr}: +``` + +### Mode B: Current branch (no `--pr`) + +```bash +git rev-parse --abbrev-ref HEAD +``` + +Determine the merge-base. If `--base` was provided, use it directly; otherwise auto-detect: + +```bash +MERGE_BASE=$(git merge-base HEAD origin/main 2>/dev/null || git merge-base HEAD origin/master) +``` + +Get the diff from the merge-base to the **working tree** (includes committed, staged, and unstaged changes): + +```bash +git diff $MERGE_BASE --name-only +git diff $MERGE_BASE +``` + +For each architecturally significant changed file, read the full file from disk for context beyond the diff. + +--- + +## Step 3: Analyse + +Apply every rule from the loaded `.gitar/review/` files to the diff. For each finding note: file path, approximate line, and whether it's a **must fix** (correctness risk) or **worth addressing** (design smell that will compound). + +Before reporting each finding, assess the tradeoff: + +- **Scope**: one-liner fix, single-file refactor, or multi-file change? +- **Complexity trade**: does the fix remove complexity in one place but add it somewhere else? +- **Risk**: does it touch a public API (breaking change for consumers)? +- **Strategic direction**: is the implementation heading the right way, or is it a local fix on a fundamentally wrong approach? + +Include the tradeoff in every finding. A finding without a tradeoff is just a complaint. + +--- + +## Step 4: Report findings + +--- + +**Must fix:** + +- `{file}:{line}` — {what breaks or violates and why} → fix: {concrete suggestion} | cost: {tradeoff} + +**Worth addressing (will compound if left):** + +- `{file}:{line}` — {the smell or gap} → suggestion: {cleaner approach} | cost: {tradeoff} + +**Looks good:** + +- {something specific that was done well} + +--- + +Keep each finding to 2-3 sentences. If no must-fix items, say so clearly. + +### Closing prompt + +**If `--pr` was used** (reviewing an existing PR): + +Ask: "Want me to address any of these, or post these findings as a comment on the PR?" + +If the answer is to post: use `gh pr comment {pr} --body "{findings}"`. Do **not** offer to create a new PR — one already exists. + +**If reviewing the current branch** (no `--pr`): + +Ask: "Want to address any of these before opening the PR, or shall I go ahead and create it?" + +If the answer is to create it: `gh pr create --title "{title}" --body "{body}"`. Mention any deferred items briefly in the PR body so reviewers are aware. diff --git a/.gitar/review/accessibility.md b/.gitar/review/accessibility.md new file mode 100644 index 000000000..93a6bca27 --- /dev/null +++ b/.gitar/review/accessibility.md @@ -0,0 +1,7 @@ +# Accessibility (a11y) + +- **WCAG 2.2 AA**: Verify that interactive components expose correct roles, labels, and states. Flag missing `aria-label`, `aria-labelledby`, or `role` where semantics require them. +- **Aria attribute naming**: Aria props must match HTML attribute names in camelCase (e.g., `aria-label`, `aria-expanded`). Flag custom prop names that shadow or diverge from ARIA spec names. +- **Keyboard navigation**: All interactive elements must support standard keyboard patterns (Enter/Space to activate, Arrow keys for composite widgets, Escape to dismiss). Flag interactive components with no keyboard handler. +- **`jest-axe` in tests**: Every new component test file must include a `jest-axe` assertion (`expect(await axe(container)).toHaveNoViolations()`). Flag test files missing this check. +- **Storybook a11y**: Confirm that no a11y findings are suppressed in stories without a documented justification. diff --git a/.gitar/review/component-patterns.md b/.gitar/review/component-patterns.md new file mode 100644 index 000000000..4ea0431bf --- /dev/null +++ b/.gitar/review/component-patterns.md @@ -0,0 +1,12 @@ +# Component implementation patterns + +- **No `forwardRef`**: React 19 makes `ref` a regular prop — `forwardRef` is deprecated and must not be used in new components. Accept `ref` directly in the props destructuring (e.g., `function MyComponent({ ref, ...props }: MyProps)`). Flag any newly introduced `forwardRef` wrapper. Existing components that still use `forwardRef` should be flagged as candidates for migration, but are not a hard blocker if the PR is not touching that component's signature. +- **`displayName`**: Every component and every styled component must have `.displayName` set explicitly. Flag any missing `displayName`. +- **Enums for variants**: Varieties, sizes, and other closed sets must be expressed as exported TypeScript enums (e.g., `ButtonVariety`, `BadgeSize`), not as plain string unions. The enum must be exported from the component file and re-exported from the folder's `index.ts`. +- **CSS custom properties pattern**: Multi-variant styled components must use CSS custom properties (e.g., `var(--component-color)`) with a variant style map, not inline conditionals or multiple styled-component variants. +- **Props interface**: Each component must export a `{ComponentName}Props` interface. Flag components that don't export their props type. +- **Rest props forwarding**: Components must spread remaining props (`...otherProps` / `...restProps`) onto the root DOM node, after any internal props are destructured. Flag components that destructure all props without forwarding the rest, as this silently swallows `data-*`, `aria-*`, `className`, and other valid HTML attributes consumers may pass. +- **`className` prop**: Every component's props interface must include an optional `className?: string` field. Flag any component that omits it, even if it otherwise spreads rest props — the explicit declaration is required so consumers know it is supported. +- **`isDefined()` for truthiness checks**: Use the `isDefined()` helper from `~common/helpers/types` for all `!= null` / `!== undefined` / conditional-render guards. Flag raw `=== undefined`, `!= null`, or `Boolean(x)` checks where `isDefined(x)` should be used instead. +- **Type predicate functions**: When a component needs to narrow a prop type (e.g., distinguishing `ButtonAsLinkProps` from `ButtonProps`), write a standalone type predicate function (`function isFoo(props): props is FooProps`) rather than inline `as` casts or `instanceof` checks. +- **`@internal` for non-exported sub-components**: Internal sub-components and helpers that live in the component file but should not be used by consumers must be annotated with `/** @internal */`. Flag internal symbols that are exported without this annotation. diff --git a/.gitar/review/design-tokens.md b/.gitar/review/design-tokens.md new file mode 100644 index 000000000..a744c0bca --- /dev/null +++ b/.gitar/review/design-tokens.md @@ -0,0 +1,8 @@ +# Design tokens & styling + +- **`cssVar` only**: All CSS values — colors, spacing, typography, border radii, shadows — must come from design tokens via the `cssVar` helper. Flag any raw hex codes, pixel values, or hardcoded font sizes. +- **No Tailwind / utility classes**: Echoes components must not use Tailwind or arbitrary CSS utility classes. Styling must go through Emotion styled components with design tokens. +- **Semantic token selection**: Tokens must carry semantic meaning (e.g., `color-text-danger`, not `color-red-500`). Flag usage of primitive/non-semantic tokens where a semantic equivalent exists. +- **New tokens**: Any new design token introduced in the diff should be flagged as requiring review by both a designer and a developer — it's not a blocker, but a checkpoint. +- **No arbitrary colors or typography values**: Flag any hardcoded color value (hex, `rgb()`, `hsl()`), font family string, font size, font weight, or line height that is not routed through `cssVar()`. This applies inside `styled` components, inline `style` props, and CSS-in-JS objects alike. +- **Layer 3 token opportunity**: When a component uses the same layer 1/2 token (e.g. `color-background-accent-default`) in multiple places to express a single component-level concept, flag it as a candidate for a new layer 3 component token (e.g. `--echoes-mycomponent-background`). Layer 3 tokens are the right tool when: (a) the same value is repeated many times within one component, (b) the value may need to be overridden independently from the global token it maps to, or (c) the concept has a clear semantic name at the component level. Note this as a suggestion requiring designer + developer alignment, not a hard blocker. diff --git a/.gitar/review/folder-and-exports.md b/.gitar/review/folder-and-exports.md new file mode 100644 index 000000000..cff9f5fb7 --- /dev/null +++ b/.gitar/review/folder-and-exports.md @@ -0,0 +1,6 @@ +# Folder & export architecture + +- **Folder placement**: New components must live in `src/components/{kebab-case-name}/`. Flag components landing directly in `src/components/` without their own folder. +- **Named exports only**: Default exports must be rejected. Every symbol — component, enum, props type — must use a named export. +- **`index.ts` wiring**: The component's folder `index.ts` must export: the component itself, all enums, and the props type — all as named exports. The top-level `src/components/index.ts` must also be updated. Flag any component that is implemented but not re-exported at both levels, and flag any props interface that is exported from the component file but missing from the public barrel. +- **Logic separation**: Complex components should be split into internal sub-components within the same folder. Flag monolithic components that exceed ~150 lines and handle multiple distinct concerns inline. diff --git a/.gitar/review/general-design.md b/.gitar/review/general-design.md new file mode 100644 index 000000000..d0004265c --- /dev/null +++ b/.gitar/review/general-design.md @@ -0,0 +1,19 @@ +# General design issues + +## Duplication (DRY) + +- Same 3+ lines of logic appearing in 3 or more files? Flag it. Two-file duplication is often coincidental — don't extract unless the intent is clearly the same. +- Two components or hooks structurally identical (same props, same state, same render logic)? +- A constant defined in one place but hardcoded as a literal elsewhere? + +## Missing or better abstractions + +- A `switch` / `if` chain on the same discriminator repeated across multiple components? Consider a lookup map or config object. +- Multiple styled components sharing identical CSS blocks that could be expressed as a shared base or CSS custom-property pattern. +- Trace "what files need to change to add the next variant or size". More than two is a signal to redesign. + +## SOLID + +- **S**: A component mixing unrelated concerns (data, presentation, styling logic all in one)? +- **O**: Adding a new variety or size — does it require modifying core component logic or just extending a map/enum? +- **D**: Logic tightly coupled to a specific variant rather than driven by props or config objects? diff --git a/.gitar/review/localization.md b/.gitar/review/localization.md new file mode 100644 index 000000000..20c8d2e42 --- /dev/null +++ b/.gitar/review/localization.md @@ -0,0 +1,5 @@ +# Localization (l10n) + +- **English fallback**: Any user-visible string must have a default English value. Flag hardcoded non-English strings or strings with no default. +- **`label`-prefixed overrideable props**: All user-visible labels must be overrideable via props prefixed with `label` (e.g., `labelClose`, `labelConfirm`). Flag components with hardcoded non-overrideable strings. +- **No translation helpers**: Echoes is a library — it must not import or depend on app-level translation systems (`translate`). Flag any such imports. diff --git a/.gitar/review/naming-and-api.md b/.gitar/review/naming-and-api.md new file mode 100644 index 000000000..23ca7d31c --- /dev/null +++ b/.gitar/review/naming-and-api.md @@ -0,0 +1,11 @@ +# Naming & API conventions + +- **Component files**: Component names and their `.tsx` files must use PascalCase. Flag any snake_case or kebab-case component file names. +- **Folder names**: Must be kebab-case. Flag any PascalCase or camelCase folder names under `src/components/`. +- **Boolean props**: Must be prefixed with `is`, `has`, `enable`, or `disable`, and must default to `false`. Flag booleans like `disabled={true}` as default, or props named `active` / `open` without the prefix. +- **Event handler props**: Must start with `on` (e.g., `onClick`, `onValueChange`). Flag handlers named `handle*`, `click*`, or similar. +- **Internal sub-components**: Sub-components within a folder must be prefixed with the parent component name (e.g., `SelectOption`, not just `Option`) to avoid collisions and keep them grouped in search results. +- **Predictability**: Prop names should be obvious and consistent with existing Echoes components. Flag novel naming when a standard equivalent exists in the library (e.g., using `type` where similar components use `variety`). +- **Props interface ordering**: Props must follow this order — required/semantic props first, then boolean flags (`is*`/`has*`/`enable*`/`disable*`), then callbacks (`on*`), then layout/style props (`className` last). Flag interfaces that mix callbacks before flags or put semantic props at the end. +- **TSDoc on every public prop**: Every field in a public props interface must have a `/** */` doc comment. Optional props that have a default value must document it with `@defaultValue`. Enum members must also be individually documented. Flag any undocumented public prop or enum member. +- **Union types for mutually exclusive props**: Props that are mutually exclusive (e.g., `ariaLabel` vs `ariaLabelledBy`, controlled vs uncontrolled open state) must be expressed as a TypeScript union type (`PropsA | PropsB`) rather than making both optional and relying on runtime checks. Flag components where two props are documented as "use one or the other" but the interface permits both simultaneously. diff --git a/.gitar/review/radix-ui.md b/.gitar/review/radix-ui.md new file mode 100644 index 000000000..7e05d50ba --- /dev/null +++ b/.gitar/review/radix-ui.md @@ -0,0 +1,4 @@ +# Radix UI + +- **Namespace imports**: Radix primitives must always be imported as a namespace (`import * as RadixDialog from '@radix-ui/react-dialog'`), never as named imports. This makes Radix usage visually distinct from internal components throughout the file. Flag any `import { Root, Trigger } from '@radix-ui/react-*'` style imports. +- **`style*` factory pattern for Radix composition**: When a Radix primitive needs styling, define a `styleXxx()` factory function that returns a styled component, then call it with the Radix component as the argument (e.g., `const ModalOverlay = styleModalOverlay(RadixDialog.Overlay)`). Do not wrap Radix components directly with `styled()` inline — this avoids double-wrapping and keeps style definitions composable. Flag direct `styled(RadixDialog.SomeComponent)` calls that bypass the factory pattern. diff --git a/.gitar/review/stories-and-figma.md b/.gitar/review/stories-and-figma.md new file mode 100644 index 000000000..e290708af --- /dev/null +++ b/.gitar/review/stories-and-figma.md @@ -0,0 +1,5 @@ +# Stories & Figma Connect + +- **Stories file**: Every new component must have a corresponding `stories/{category}/{ComponentName}-stories.tsx` file. Flag missing story files. When an existing component gains a new prop, variety, or behaviour, the matching story must be updated or a new story added to cover it — flag changes to component files with no corresponding change to the stories file. +- **Controls coverage**: Stories must expose all meaningful props as Storybook controls. Flag stories that omit enum props from `argTypes`. +- **Figma Connect file**: If a Figma design exists for the component, a `.figma.tsx` file should be present in `figma-connect/`. Flag missing Figma Connect files when the component clearly maps to a Figma component (this is a soft flag — confirm with the author). diff --git a/.gitar/review/testing.md b/.gitar/review/testing.md new file mode 100644 index 000000000..d96b4757e --- /dev/null +++ b/.gitar/review/testing.md @@ -0,0 +1,8 @@ +# Testing + +- **No snapshot tests**: Snapshot testing must be avoided. Flag any `toMatchSnapshot()` or `toMatchInlineSnapshot()` usage in component tests. +- **Behavioral testing with RTL**: Tests must assert on user-visible behavior, not implementation details. Flag tests that query by component internals, CSS class names, or implementation-specific attributes. +- **Keyboard navigation coverage**: For every interactive element introduced, verify that at least one test explicitly exercises keyboard interaction (e.g., `userEvent.keyboard`, `fireEvent.keyDown`). +- **Coverage gate**: Low coverage on new code is a merge blocker. Flag new components that have no test file at all. +- **Test file location**: Tests must live in `src/components/{component-folder}/__tests__/{ComponentName}-test.tsx`. +- **Inline `setup*`/`render*` helpers**: Test files must define a local helper function (`setupFoo()` or `renderFoo()`) that wraps `render()` and returns `{ user, ...rest }`. Raw `render()` calls scattered across individual test cases are not permitted — they make tests harder to refactor and bypass the consistent `userEvent.setup()` wiring. Flag test files that call `render()` directly in `it()` blocks without a shared helper. diff --git a/.gitignore b/.gitignore index d4cacce1c..bb157cc5e 100644 --- a/.gitignore +++ b/.gitignore @@ -57,6 +57,12 @@ build-reports/ dist/ .env/ -# SonarQube LLM code context files +# ---- LLM code context files .sonar-code-context/ .serena/ +.claude/hooks/ +.mcp.json +CLAUDE-personal.md +CLAUDE.local.md +AGENT-personal.md +AGENT.local.md diff --git a/AGENT.md b/AGENT.md new file mode 100644 index 000000000..ed58cea2d --- /dev/null +++ b/AGENT.md @@ -0,0 +1,62 @@ +- Read @README.md every time you start. +- Read @package.json every time you start. + +# About this project + +Echoes React is a React component library implementing the Echoes design system for Sonar products. It is published as `@sonarsource/echoes-react` and consumed by downstream apps (SonarQube, SonarCloud). Changes here are public API changes — treat breaking changes with care. + +# Design philosophy + +- **Developer experience first**: Product developers must be able to rely on this library fully. APIs should be straightforward and easy to configure — if a component is hard to use, that is a design flaw. +- **One component, one use case**: Each component must serve a single, well-defined purpose. Avoid multi-purpose components with diverging code paths. +- **Design fidelity is non-negotiable**: Components must not visually or behaviourally differ from their Figma counterparts. Keep components in sync with the Design System — both UX designers and developers share responsibility for this. +- **Favour existing standards over custom solutions**: When adding interactive behaviour, prefer established external libraries that follow industry standards over bespoke implementations. Any new dependency must have a compatible license and allow sufficient customisation to meet the agreed-upon design. +- **Delivery over perfection**: The vision is a direction, not a gate. Incremental improvements are better than blocking delivery waiting for the ideal solution. + +# Important skills + +- `echoes-review` — review changes on the current branch or a PR for naming violations, a11y gaps, design-token misuse, missing exports, and architectural issues. Run before opening a PR. + +Load skills only when the task actually requires them — do not load them at startup. + +# Testing, linting, and running tools + +```sh +yarn jest # run all unit tests +yarn jest src/components/Foo # run tests for a specific component +yarn test-ci # full test run with coverage +yarn build # compile the library (dist/) +yarn ts-check # type check the library +yarn lint # lint the library +yarn format # auto-format with Prettier +``` + +- Never attempt to fix linting issues until you believe the implementation is correct. +- When adding new dependencies to `package.json`, always add a corresponding entry in `package.json.md`. +- **MANDATORY**: Run `yarn prettier --write ` after finishing editing files, do it in bulk. +- **MANDATORY**: Fix all TypeScript errors before declaring a task done — `yarn ts-check` will surface them. + +# Writing code + +- Keep things DRY. Reorganize if necessary before duplicating logic. +- Always prefer `export { ComponentName }` over `export default ComponentName` — prevents renaming at import. +- Use function declarations for components, not `const`. +- All new components must be functional components — no class components. + +@.gitar/review/naming-and-api.md + +@.gitar/review/component-patterns.md + +@.gitar/review/folder-and-exports.md + +@.gitar/review/radix-ui.md + +@.gitar/review/design-tokens.md + +@.gitar/review/accessibility.md + +@.gitar/review/testing.md + +@.gitar/review/localization.md + +@.gitar/review/stories-and-figma.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 000000000..ac534a310 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENT.md \ No newline at end of file From 65d7f33247fdb6db68946c927ac81dd48a1d952f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Aubert?= Date: Tue, 16 Jun 2026 10:41:59 +0200 Subject: [PATCH 2/2] Fix following review --- {.claude => .agents}/skills/echoes-review/SKILL.md | 1 + .claude/skills | 1 + .gitar/review/accessibility.md | 10 ++++++++-- .gitar/review/folder-and-exports.md | 2 +- AGENT.md => AGENTS.md | 0 CLAUDE.md | 2 +- 6 files changed, 12 insertions(+), 4 deletions(-) rename {.claude => .agents}/skills/echoes-review/SKILL.md (99%) create mode 120000 .claude/skills rename AGENT.md => AGENTS.md (100%) diff --git a/.claude/skills/echoes-review/SKILL.md b/.agents/skills/echoes-review/SKILL.md similarity index 99% rename from .claude/skills/echoes-review/SKILL.md rename to .agents/skills/echoes-review/SKILL.md index 9fe7d4770..c429818c4 100644 --- a/.claude/skills/echoes-review/SKILL.md +++ b/.agents/skills/echoes-review/SKILL.md @@ -1,4 +1,5 @@ --- +name: echoes-review description: Echoes review — analyse your current branch or an existing PR for naming violations, a11y gaps, design-token misuse, missing exports, and architectural issues in the Echoes React component library. --- diff --git a/.claude/skills b/.claude/skills new file mode 120000 index 000000000..2b7a412b8 --- /dev/null +++ b/.claude/skills @@ -0,0 +1 @@ +../.agents/skills \ No newline at end of file diff --git a/.gitar/review/accessibility.md b/.gitar/review/accessibility.md index 93a6bca27..df43312fc 100644 --- a/.gitar/review/accessibility.md +++ b/.gitar/review/accessibility.md @@ -1,7 +1,13 @@ # Accessibility (a11y) - **WCAG 2.2 AA**: Verify that interactive components expose correct roles, labels, and states. Flag missing `aria-label`, `aria-labelledby`, or `role` where semantics require them. -- **Aria attribute naming**: Aria props must match HTML attribute names in camelCase (e.g., `aria-label`, `aria-expanded`). Flag custom prop names that shadow or diverge from ARIA spec names. -- **Keyboard navigation**: All interactive elements must support standard keyboard patterns (Enter/Space to activate, Arrow keys for composite widgets, Escape to dismiss). Flag interactive components with no keyboard handler. +- **Aria attribute naming**: + - rendered DOM nodes must use spec ARIA attributes like aria-label + - public component props must use repo conventions like ariaLabel / ariaLabelledBy + - Flag any props that use the wrong casing or naming convention. +- **Keyboard navigation**: + - All interactive elements must support standard keyboard patterns (Enter/Space to activate, Arrow keys for composite widgets, Escape to dismiss). + - Require explicit keyboard handlers for custom non-semantic widgets or custom composite behavior + - Do not require local handlers when native elements or Radix already provide the behavior - **`jest-axe` in tests**: Every new component test file must include a `jest-axe` assertion (`expect(await axe(container)).toHaveNoViolations()`). Flag test files missing this check. - **Storybook a11y**: Confirm that no a11y findings are suppressed in stories without a documented justification. diff --git a/.gitar/review/folder-and-exports.md b/.gitar/review/folder-and-exports.md index cff9f5fb7..6a98e630e 100644 --- a/.gitar/review/folder-and-exports.md +++ b/.gitar/review/folder-and-exports.md @@ -1,6 +1,6 @@ # Folder & export architecture -- **Folder placement**: New components must live in `src/components/{kebab-case-name}/`. Flag components landing directly in `src/components/` without their own folder. +- **Folder placement**: New components must live in a new `src/components/{kebab-case-name}/` folder or an existing matching feature/family folder. Flag components landing directly in `src/components/` without their own folder. - **Named exports only**: Default exports must be rejected. Every symbol — component, enum, props type — must use a named export. - **`index.ts` wiring**: The component's folder `index.ts` must export: the component itself, all enums, and the props type — all as named exports. The top-level `src/components/index.ts` must also be updated. Flag any component that is implemented but not re-exported at both levels, and flag any props interface that is exported from the component file but missing from the public barrel. - **Logic separation**: Complex components should be split into internal sub-components within the same folder. Flag monolithic components that exceed ~150 lines and handle multiple distinct concerns inline. diff --git a/AGENT.md b/AGENTS.md similarity index 100% rename from AGENT.md rename to AGENTS.md diff --git a/CLAUDE.md b/CLAUDE.md index ac534a310..47dc3e3d8 120000 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1 +1 @@ -AGENT.md \ No newline at end of file +AGENTS.md \ No newline at end of file