Skip to content

Commit 28732d1

Browse files
authored
chore: consolidate AI agent configuration with AGENTS.md as canonical source (#4889)
1 parent d095239 commit 28732d1

39 files changed

Lines changed: 2296 additions & 2622 deletions

.claude/CLAUDE.md

Lines changed: 30 additions & 296 deletions
Original file line numberDiff line numberDiff line change
@@ -1,313 +1,47 @@
1-
# Equinor Design System (EDS)
1+
# Claude Code — Equinor Design System
22

3-
This file provides guidance for Claude Code working in this repository.
3+
> **The canonical conventions for this repository live in [`AGENTS.md`](../AGENTS.md).**
4+
> Read that file for component structure, code style, CSS patterns, testing, accessibility, and conventional commits.
5+
>
6+
> This file holds only Claude-Code-specific configuration that doesn't belong in `AGENTS.md`.
47
5-
## Overview
8+
## Path-Scoped Rules
69

7-
Equinor Design System (EDS) is a pnpm monorepo containing React component libraries and design tokens. **New components are developed in `/next`** (`packages/eds-core-react/src/components/next/`).
10+
Claude Code automatically loads rules in `.claude/rules/` when working on matching files:
811

9-
### Key Packages
12+
| Rule | Scope | Purpose |
13+
| -------------------- | --------------------------------------------------------------- | ----------------------------------------------- |
14+
| `eds-component.md` | `packages/eds-core-react/src/components/next/**/*.{tsx,ts,css}` | EDS 2.0 component conventions (short reference) |
15+
| `figma-component.md` | `packages/eds-core-react/src/components/next/**/*.figma.tsx` | Figma-to-code workflow with MCP tools |
16+
| `advisor.md` | General | Read-only code review guidelines |
1017

11-
- `@equinor/eds-core-react` - Main React component library
12-
- `@equinor/eds-core-react/next` - New EDS 2.0 components (active development)
13-
- `@equinor/eds-tokens` - Design tokens, CSS variables, and theming
14-
- `@equinor/eds-icons` - Icon library
18+
The path-scoped rules are intentionally short — they reference [`AGENTS.md`](../AGENTS.md) rather than duplicating it.
1519

16-
## Build/Lint/Test Commands
20+
## Slash Commands
1721

18-
Package manager: `pnpm@10.15.0`
22+
User-invokable prompts triggered with `/command-name`.
1923

20-
```bash
21-
pnpm run build # Build all packages
22-
pnpm run build:core-react # Build eds-core-react only
23-
pnpm run lint:all # Lint entire codebase
24-
pnpm run lint ./path/to/file.tsx # Lint specific file
24+
| Command | Usage | Description |
25+
| ----------------------- | ------------------------------------- | -------------------------------------------------------- |
26+
| `/new-component` | `/new-component Button` | Scaffold a new EDS 2.0 component with all required files |
27+
| `/create-component-doc` | `/create-component-doc <raw content>` | Restructure raw content into component documentation |
2528

26-
pnpm run test:core-react # Run eds-core-react tests
27-
pnpm run test:watch:core-react # Watch mode
29+
## Hooks
2830

29-
# Run a single test file (from package directory)
30-
cd packages/eds-core-react
31-
pnpm test -- --testPathPattern="Icon"
31+
Configured in `.claude/settings.json`. Scripts live in `.claude/hooks/`.
3232

33-
pnpm run storybook # Start Storybook
34-
```
33+
| Hook | Event | Matcher | Purpose |
34+
| ---------------- | ------------- | --------------------------------------------------- | ---------------------------------------------- |
35+
| `read_hook.js` | `PreToolUse` | `Read\|Grep\|Glob\|Bash\|Edit\|Write\|NotebookEdit` | Blocks access to `.env` and other secret files |
36+
| `format_hook.js` | `PostToolUse` | `Edit\|Write` | Runs ESLint+Prettier auto-fix on edited files |
3537

36-
## Component File Structure (EDS 2.0)
38+
See [`.claude/README.md`](./README.md) for hook authoring details.
3739

38-
New components go in `packages/eds-core-react/src/components/next/`:
40+
## Settings
3941

40-
```
41-
ComponentName/
42-
index.ts # Named exports only
43-
ComponentName.tsx # Main component with forwardRef
44-
ComponentName.types.ts # TypeScript types with JSDoc
45-
componentname.css # Vanilla CSS with design tokens
46-
componentName.figma.tsx # Figma Code Connect file for mapping code to Figma props
47-
ComponentName.test.tsx # Jest + Testing Library + jest-axe
48-
ComponentName.stories.tsx
49-
```
50-
51-
## Code Style
52-
53-
### Formatting (Prettier)
54-
55-
- 2 spaces, no semicolons, single quotes, trailing commas, LF line endings
56-
57-
### Imports
58-
59-
```typescript
60-
// 1. React
61-
import { forwardRef, useId } from 'react'
62-
// 2. Types (use `import type`)
63-
import type { ComponentProps } from './Component.types'
64-
// 3. Styles last
65-
import './component.css'
66-
```
67-
68-
**No default exports** (except Storybook files). Always use named exports.
69-
70-
### TypeScript
71-
72-
```typescript
73-
export type IconSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'
74-
75-
export type IconProps = {
76-
/** Icon data from @equinor/eds-icons */
77-
data: IconData
78-
/** Title for accessibility - makes icon semantic with role="img" */
79-
title?: string
80-
/** Explicit size override */
81-
size?: IconSize
82-
} & Omit<SVGProps<SVGSVGElement>, 'color'>
83-
```
84-
85-
### React Components
86-
87-
```typescript
88-
export const Icon = forwardRef<SVGSVGElement, IconProps>(function Icon(
89-
{ data, title, color = 'currentColor', size, className, ...rest },
90-
ref,
91-
) {
92-
const titleId = useId()
93-
94-
if (!data) {
95-
console.error('Icon: data prop is required')
96-
return null
97-
}
98-
99-
const classes = ['icon', className].filter(Boolean).join(' ')
100-
101-
return (
102-
<svg ref={ref} className={classes} data-icon-size={size} {...rest}>
103-
{title && <title id={titleId}>{title}</title>}
104-
<path d={data.svgPathData} />
105-
</svg>
106-
)
107-
})
108-
```
109-
110-
### CSS (Vanilla + Tokens + Nesting)
111-
112-
One `eds-`-prefixed root class per component. Internal elements use simple class names scoped by CSS nesting. Variants and state use data attributes.
113-
114-
```css
115-
@layer eds-components {
116-
.eds-icon {
117-
font-size: var(--eds-typography-icon-size, 1.5em);
118-
width: 1em;
119-
height: 1em;
120-
flex-shrink: 0;
121-
122-
&[data-icon-size='lg'] {
123-
--_explicit-size: var(--eds-sizing-icon-lg);
124-
width: var(--_explicit-size);
125-
height: var(--_explicit-size);
126-
}
127-
}
128-
}
129-
```
130-
131-
#### Pseudo-private custom properties
132-
133-
Define component-scoped variables with a `--_` prefix at the component root. Use these variables for all properties. In variants and states, **override only the variable — never the property directly**.
134-
135-
```css
136-
/* CORRECT */
137-
.eds-button {
138-
--_color: var(--eds-color-text-strong-on-emphasis);
139-
--_bg-color: var(--eds-color-bg-fill-emphasis-default);
140-
color: var(--_color);
141-
background-color: var(--_bg-color);
142-
}
143-
.eds-button[data-variant='ghost']:disabled {
144-
--_color: var(--eds-color-text-disabled); /* override the variable */
145-
}
146-
147-
/* WRONG */
148-
.eds-button[data-variant='ghost']:disabled {
149-
color: var(--eds-color-text-disabled); /* never override the property directly */
150-
}
151-
```
152-
153-
#### CSS layers
154-
155-
Wrap all component styles in `@layer eds-components { }`. Rules outside the layer (e.g. display overrides) must be placed after the layer block with a comment explaining why they are outside.
156-
157-
#### Data attributes for variants and states
158-
159-
Use `data-*` attributes for all variants, sizes, and boolean states — not modifier classes.
160-
161-
```css
162-
.eds-button[data-variant='primary'] { }
163-
.eds-button[data-selectable-space='lg'] { }
164-
.eds-button[data-icon-only] { }
165-
.eds-button[data-round] { }
166-
.eds-button[data-multiline] { }
167-
```
168-
169-
#### Density via ancestor attribute
170-
171-
Density variants are applied by setting `data-density` on an ancestor element. Component CSS selects against this ancestor:
172-
173-
```css
174-
[data-density='comfortable'] .eds-button[data-selectable-space='md'] {
175-
--_min-height: 1.5rem;
176-
}
177-
```
178-
179-
#### Modular type scale
180-
181-
Font sizes follow a mathematical scale based on a `--_base` value:
182-
183-
```css
184-
:root, [data-density='spacious'] {
185-
--_base: 16px;
186-
--font-size-md: round(calc(var(--_base) * pow(2, -1/5)), 0.5px);
187-
}
188-
[data-density='comfortable'] {
189-
--_base: 14px; /* only the base changes; all derived values update automatically */
190-
}
191-
```
192-
193-
#### Progressive enhancement with `@supports`
194-
195-
Use `@supports` to layer in advanced CSS features. The base styles work everywhere; the `@supports` block adds what only supported browsers can handle:
196-
197-
```css
198-
/* Base — all browsers: symmetric padding keeps text vertically centred */
199-
padding-block: var(--eds-selectable-space-vertical);
200-
201-
/* Enhancement — trims whitespace above/below the cap-height */
202-
@supports (text-box: trim-both ex alphabetic) {
203-
padding-top: var(--padding-top-baseline);
204-
padding-bottom: 0;
205-
text-box: trim-both ex alphabetic;
206-
}
207-
```
208-
209-
CSS `@function` (Chrome/Edge 128+) is a future enhancement — define it and comment it in once Safari ships support.
210-
211-
### Testing
212-
213-
Jest + Testing Library. Organize tests by category with `describe` blocks:
214-
215-
```typescript
216-
import { render, screen } from '@testing-library/react'
217-
import { axe } from 'jest-axe'
218-
import { Icon } from '.'
219-
220-
describe('Icon (next)', () => {
221-
describe('Rendering', () => {
222-
it('renders with data prop', () => {
223-
render(<Icon data={save} />)
224-
expect(screen.getByTestId('eds-icon')).toBeInTheDocument()
225-
})
226-
})
227-
228-
describe('Accessibility', () => {
229-
it('is decorative (aria-hidden) when no title', () => {
230-
render(<Icon data={save} />)
231-
expect(screen.getByTestId('eds-icon')).toHaveAttribute('aria-hidden', 'true')
232-
})
233-
234-
it('passes axe accessibility test', async () => {
235-
const { container } = render(<Icon data={save} title="Save" />)
236-
expect(await axe(container)).toHaveNoViolations()
237-
})
238-
})
239-
})
240-
```
241-
242-
Query priority: `getByRole` > `getByLabelText` > `getByText` > `getByTestId`
243-
244-
### Naming Conventions
245-
246-
- **Components/Types**: PascalCase (`Button`, `ButtonProps`)
247-
- **Variables/Functions**: camelCase (`isDisabled`, `useToken`)
248-
- **CSS classes**: `eds-` prefix on root class (`eds-button`, `eds-text-area`); simple nested names for internal elements (`.label-row`, `.icon`); variants via data attributes
249-
- **Files**: Match export (`Icon.tsx`, `Icon.types.ts`, `icon.css`)
250-
251-
## Polymorphism (`asChild` + `Slot`)
252-
253-
EDS 2.0 uses the `asChild` pattern for components that need polymorphic rendering (e.g. Link, Button). This lets consumers swap the underlying element for router links, custom components, etc.
254-
255-
- **`Slot`** utility in `packages/eds-core-react/src/components/next/Slot/` merges parent props onto the child element
256-
- Add `asChild?: boolean` to the component's props type
257-
- When `asChild` is true, render `<Slot>` instead of the default element
258-
- Extract shared props into a `sharedProps` object to avoid duplication
259-
- See `Slot/README.md` for merge behavior details and usage examples
260-
261-
Components that should support `asChild`: **Link**, **Button**, and any component rendering an interactive element that consumers may want to swap.
262-
263-
## Accessibility
264-
265-
- WCAG 2.1 AA compliance required
266-
- Decorative elements: `aria-hidden="true"`
267-
- Semantic elements: `role="img"` with `aria-labelledby`
268-
- Test with `jest-axe` in every component
269-
270-
## Conventional Commits
271-
272-
```
273-
type(scope): description
274-
```
275-
276-
**Types**: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`
277-
278-
**Scopes (packages)**: `eds-core-react`, `eds-data-grid-react`, `eds-icons`, `eds-lab-react`, `eds-tailwind`, `eds-tokens`, `eds-tokens-build`, `eds-tokens-sync`, `eds-utils`, `design-system-docs`, `eds-color-palette-generator`, `eds-demo`, `figma-broker`
279-
280-
**Scopes (infrastructure)**: `config`, `github`, `build`, `deps`, `docs`, `devcontainer`
281-
282-
**Breaking**: `feat(eds-core-react)!: remove deprecated prop`
283-
284-
**Scope and release-please interaction**: Release-please detects packages from file paths — you don't always need a package scope. Using a package scope with a visible type forces a bump regardless of which files changed. For non-publishable changes (config, Storybook, tests, README, docs), use hidden types: `chore`, `build`, `ci`, `docs`, or `test`.
285-
286-
**PR titles** must also follow the conventional commits format — they appear in changelogs and merge history.
287-
288-
See `documentation/how-to/CONVENTIONAL_COMMITS.md` for full guidelines.
42+
- `.claude/settings.json` — shared, checked into the repo (permission denies, hooks)
43+
- `.claude/settings.local.json` — personal, gitignored (per-developer overrides)
28944

29045
## Git Workflow
29146

292-
⚠️ **CRITICAL: Always ask the user for permission before:**
293-
294-
- Creating commits
295-
- Pushing to remote
296-
- Creating branches
297-
- Creating PRs with `gh`
298-
299-
**Never assume these actions are okay.** Even for small changes, always confirm with the user first. Example: "Ready to commit. Should I proceed?"
300-
301-
NEVER attribute AI, or add to the commit message "Co-authored by Claude" or similar.
302-
303-
## Additional Guidelines
304-
305-
See `.github/copilot-instructions.md` and `.github/instructions/` for detailed guidelines.
306-
307-
## Specialized Rules
308-
309-
Path-specific rules are available in `.claude/rules/`:
310-
311-
- `eds-component.md` - EDS 2.0 component building conventions
312-
- `figma-component.md` - Figma-to-code workflow with MCP tools
313-
- `advisor.md` - Read-only code review guidelines
47+
See the **Git Workflow** section in [`AGENTS.md`](../AGENTS.md) — the rules apply to Claude Code as well.

0 commit comments

Comments
 (0)