This file provides guidance for AI coding agents working in this repository.
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/).
@equinor/eds-core-react- Main React component library@equinor/eds-core-react/next- New EDS 2.0 components (active development)@equinor/eds-tokens- Design tokens, CSS variables, and theming@equinor/eds-icons- Icon library
Package manager: pnpm@10.15.0
pnpm run build # Build all packages
pnpm run build:core-react # Build eds-core-react only
pnpm run lint:all # Lint entire codebase
pnpm run lint ./path/to/file.tsx # Lint specific file
pnpm run test:core-react # Run eds-core-react tests
pnpm run test:watch:core-react # Watch mode
# Run a single test file (from package directory)
cd packages/eds-core-react
pnpm test -- --testPathPattern="Icon"
pnpm run storybook # Start StorybookNew components go in packages/eds-core-react/src/components/next/:
ComponentName/
index.ts # Named exports only
ComponentName.tsx # Main component with forwardRef
ComponentName.types.ts # TypeScript types with JSDoc
componentname.css # Vanilla CSS with design tokens
componentName.figma.tsx # Figma Code Connect file for mapping code to Figma props
ComponentName.test.tsx # Jest + Testing Library + jest-axe
ComponentName.stories.tsx
- 2 spaces, no semicolons, single quotes, trailing commas, LF line endings
// 1. React
import { forwardRef, useId } from 'react'
// 2. Types (use `import type`)
import type { ComponentProps } from './Component.types'
// 3. Styles last
import './component.css'No default exports (except Storybook files). Always use named exports.
export type IconSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'
export type IconProps = {
/** Icon data from @equinor/eds-icons */
data: IconData
/** Title for accessibility - makes icon semantic with role="img" */
title?: string
/** Explicit size override */
size?: IconSize
} & Omit<SVGProps<SVGSVGElement>, 'color'>export const Icon = forwardRef<SVGSVGElement, IconProps>(function Icon(
{ data, title, color = 'currentColor', size, className, ...rest },
ref,
) {
const titleId = useId()
if (!data) {
console.error('Icon: data prop is required')
return null
}
const classes = ['icon', className].filter(Boolean).join(' ')
return (
<svg ref={ref} className={classes} data-icon-size={size} {...rest}>
{title && <title id={titleId}>{title}</title>}
<path d={data.svgPathData} />
</svg>
)
})One eds--prefixed root class per component. Internal elements use simple class names scoped by CSS nesting. Variants and state use data attributes.
@layer eds-components {
.eds-icon {
font-size: var(--eds-typography-icon-size, 1.5em);
width: 1em;
height: 1em;
flex-shrink: 0;
&[data-icon-size='lg'] {
--_explicit-size: var(--eds-sizing-icon-lg);
width: var(--_explicit-size);
height: var(--_explicit-size);
}
}
}Jest + Testing Library. Organize tests by category with describe blocks:
import { render, screen } from '@testing-library/react'
import { axe } from 'jest-axe'
import { Icon } from '.'
describe('Icon (next)', () => {
describe('Rendering', () => {
it('renders with data prop', () => {
render(<Icon data={save} />)
expect(screen.getByTestId('eds-icon')).toBeInTheDocument()
})
})
describe('Accessibility', () => {
it('is decorative (aria-hidden) when no title', () => {
render(<Icon data={save} />)
expect(screen.getByTestId('eds-icon')).toHaveAttribute('aria-hidden', 'true')
})
it('passes axe accessibility test', async () => {
const { container } = render(<Icon data={save} title="Save" />)
expect(await axe(container)).toHaveNoViolations()
})
})
})Query priority: getByRole > getByLabelText > getByText > getByTestId
- Components/Types: PascalCase (
Button,ButtonProps) - Variables/Functions: camelCase (
isDisabled,useToken) - CSS classes:
eds-prefix on root class (eds-button,eds-text-area); simple nested names for internal elements; variants via data attributes - Files: Match export (
Icon.tsx,Icon.types.ts,icon.css)
- WCAG 2.1 AA compliance required
- Decorative elements:
aria-hidden="true" - Semantic elements:
role="img"witharia-labelledby - Test with
jest-axein every component
type(scope): description
Types: feat, fix, docs, refactor, test, chore
Scopes: eds-core-react, eds-tokens, eds-icons
Breaking: feat(eds-core-react)!: remove deprecated prop
See .github/copilot-instructions.md and .github/instructions/ for detailed guidelines.