This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
React Creme is a modern UI toolkit for React featuring 55+ high-quality, accessible, and themeable components. The project is built as a Turborepo monorepo with pnpm workspaces, containing a component library, documentation site, and Storybook for component development.
This project uses cutting-edge tooling and configurations:
- Build: Vite 6.3.5 with LightningCSS for CSS optimization
- Linting: ESLint 9+ flat config + Oxlint (50-100x faster)
- Testing: Vitest 3.1.4 with jsdom
- TypeScript: 5.9.3 with strict mode and modern compiler options
- Package Manager: pnpm 10.14.0
- Monorepo: Turborepo 2.5.8
- Code Quality: Knip for unused code detection
- CI/CD: GitHub Actions with pnpm caching
- Node: v20.18.1 LTS (see
.nvmrc)
react-creme/
├── packages/
│ ├── lib/ # Main component library (react-creme npm package)
│ ├── documentation/ # Documentation website (Vite-based)
│ └── storybook/ # Storybook component development environment
└── .storybook/ # Shared Storybook configuration
- Components: Each component lives in
components/<component-name>/with its TypeScript implementation, SCSS module, and tests - Design System: Global styles, tokens, and mixins are in
design/(theme.scss, tokens.scss, effects.scss, etc.) - Entry Point:
react-creme.tsexports all public components and types - Build System: Uses Vite (migrated from Webpack) with CSS Modules support
This is a Turborepo monorepo. All modern tooling (oxlint, knip, ESLint) is monorepo-aware and works across all packages.
See MONOREPO_TOOLING.md for detailed architecture and workflow.
# Install all dependencies (including oxlint, knip, lightningcss)
pnpm install
# Verify installation
pnpm oxlint --version
pnpm knip --version# Build everything (library, docs, storybook)
pnpm build
# Build only the library
pnpm build:lib
# OR from packages/lib:
pnpm build
# Build documentation
pnpm build:doc
# Build Storybook
pnpm build-story# Run documentation dev server
pnpm dev
# Run Storybook dev server
pnpm story
# Run library in dev mode (from packages/lib)
cd packages/lib && pnpm dev# Run all tests
pnpm test
# Run tests in CI mode (from packages/lib)
cd packages/lib && pnpm test:ci
# Run tests with UI (from packages/lib)
cd packages/lib && pnpm test-ui
# Run a single component test (from packages/lib)
cd packages/lib && vitest --run components/button# Run all linters across all packages (oxlint → ESLint)
pnpm lint
# Run fast linting only (oxlint) - <1 second!
pnpm oxlint
# Format all packages
pnpm format
# Fix JavaScript/TypeScript across all packages
pnpm fix-js
# Fix CSS/SCSS across all packages
pnpm fix-css
# Fix everything
pnpm fix-all
# Run full quality check
pnpm clean
# Check for unused code/dependencies (monorepo-aware)
pnpm knip
pnpm knip:production # Production dependencies only
# Run with Turbo filters (specific packages)
pnpm lint --filter=react-creme # Lib only
pnpm lint --filter=*docu* # Documentation only
pnpm oxlint --filter=react-creme # Fast lint lib onlyHow it works:
- Turbo orchestrates tasks across packages in parallel
- Oxlint runs first (50-100x faster) as a pre-check
- ESLint runs after for comprehensive checking
- Results are cached for faster subsequent runs
- See
MONOREPO_TOOLING.mdfor architecture details
Since this is a Turborepo monorepo, you can run commands in specific packages:
# From root, using turbo filters
turbo run build --filter=react-creme # library only
turbo run dev --filter=*docu* # documentation only
turbo run dev --filter=*story* # storybook only
# Or navigate to the package
cd packages/lib && pnpm build
cd packages/documentation && pnpm dev
cd packages/storybook && pnpm devEach component follows a consistent pattern:
components/<component-name>/
├── <component-name>.tsx # Main component implementation
├── <component-name>-model.ts # TypeScript types/interfaces
├── <component-name>.module.scss # Component styles (CSS Modules)
└── __tests__/
└── <component-name>.test.tsx # Vitest tests
- Naming Convention:
[name]__[local]___[hash:base64:5] - Import Pattern:
import styles from './component.module.scss' - Usage: Classes are camelCased in TypeScript (e.g.,
styles.buttonWrapper) - Global Styles: Design tokens and utilities are in
packages/lib/design/
Components support theming through the ThemeProvider:
- Theme colors, sizes, and design tokens are defined in
design/theme.scssanddesign/tokens.scss - Components use
isDark()utility to detect dark mode - Custom theme properties can be passed via ThemeProvider
- Components use React 19 features and hooks
- No external state management library in the component library
- Documentation site uses Jotai for state management
- All components should be accessible and keyboard navigable
- Tests should include accessibility checks using
jest-axe - Storybook includes
@storybook/addon-a11yfor visual a11y testing
The library (packages/lib) uses Vite with:
- Entry Point:
react-creme.ts - Output Formats: ESM (
.mjs) and CJS (.cjs) - CSS: Single bundled CSS file (
react-creme.css) with CSS Modules - Type Declarations: Generated via
tscwithtsconfig.emit.json - Path Aliases:
@components→./components@design→./design@icons→./icons
The library exports:
"."→ Main entry (types, ESM, CJS)"./css"→ Bundled CSS file- Tree-shakeable with
sideEffects: false
The project is currently undergoing modernization (see MODERNIZATION_PLAN.md):
- ✅ Migrated from Webpack to Vite for library builds
- ✅ Using Vitest for testing
- ✅ Storybook 8.x with Vite builder
- ✅ React 19 support
- 🚧 Ongoing: Further build optimizations and tooling updates
- Framework: Vitest with
jsdomenvironment - Testing Library:
@testing-library/reactfor component testing - User Events:
@testing-library/user-event - Accessibility:
jest-axefor a11y testing - Setup File:
packages/lib/test-setup.ts
Tests are located in __tests__/ directories within each component folder:
// Example test structure
import { render, screen } from '@testing-library/react';
import { Button } from '../button';
describe('Button', () => {
it('renders children correctly', () => {
render(<Button>Click me</Button>);
expect(screen.getByText('Click me')).toBeInTheDocument();
});
});# From packages/lib
vitest --run components/button # Run button tests only
vitest --ui # Run with UI dashboard
vitest --coverage # Run with coverage report- Config File:
eslint.config.mjs(modern flat config format) - Fast Linting: Oxlint runs first in pre-commit hooks (50-100x faster)
- IDE Support: Full TypeScript-aware linting
- Note: The old
.eslintrc.jshas been removed (deprecated in ESLint v10)
- Version: 5.9.3 across all packages
- Target: ES2022 with modern browser support
- Strict mode enabled with additional safety:
exactOptionalPropertyTypes: truenoUncheckedIndexedAccess: truenoPropertyAccessFromIndexSignature: trueverbatimModuleSyntax: true(replaces importsNotUsedAsValues)incremental: truefor faster builds
- All components are typed with explicit props interfaces in
-model.tsfiles - Use
React.forwardReffor components that need ref forwarding - Export types alongside components in
react-creme.ts
- Functional components with hooks (no class components)
- Use React 19 features (latest version)
- Forward refs when components wrap native elements
- Memoization with
React.memo,useMemo,useCallbackwhere appropriate
- Use CSS Modules (
.module.scss) for component-specific styles - Import design tokens from
@designfor consistency - Follow BEM-like naming within modules
- Use Stylelint rules defined in root config
CRITICAL: Modern Sass (1.77.7+) enforces CSS-compliant ordering within selectors to match the CSS specification. Always follow this order:
- Variable declarations (
$var: value;) - @extend directives (
@extend %placeholder;) - Regular CSS properties (
color: red; margin: 0;) - Nested rules (
& {},&.class {},&:hover {}) - @include directives that generate nested rules (e.g.,
@include animate.set-keyframes(...))
Example:
.component {
// 1. Variables first
$settings: (
from: (
...,
),
to: (
...,
),
);
// 2. Extends next
@extend %border-radius;
// 3. Properties before any nested rules
display: block;
color: theme.$primary;
padding: 1rem;
// 4. Nested selectors
&:hover {
opacity: 0.8;
}
// 5. Animation mixins last (they generate @keyframes)
@include animate.set-settings(0.25s);
@include animate.set-keyframes('fade-in', $settings);
}Why? Sass 1.92.0+ enforces CSS-compliant ordering where declarations must appear before nested rules. Violating this causes deprecation warnings and will break in future versions.
Configuration: The project uses Vite with api: 'modern-compiler' for modern Sass behavior. All code follows proper CSS/Sass ordering (declarations before nested rules).
- ESLint configuration is in root
package.json(lint-staged section) - Prettier for code formatting
- Husky pre-commit hook runs
pretty-quick --staged - Components should pass all linting before commits
- Package Manager: pnpm (version 10.14.0)
- Workspace Protocol: Uses pnpm workspaces
- Installing Dependencies: Always use
pnpm add(not npm or yarn) - Updating Dependencies: Run from root to update all workspaces
Husky is configured with a modern pre-commit workflow:
- Oxlint runs first (50-100x faster than ESLint) for quick feedback
- lint-staged then runs comprehensive checks:
- TypeScript/JavaScript: Prettier → ESLint
- SCSS/CSS: Prettier → Stylelint
- JSON/Markdown/YAML: Prettier
Note: No manual git add needed (lint-staged handles it automatically)
- react & react-dom: v19.1.0 (peer dependencies)
- classnames: Utility for conditional CSS classes
- nanoid: Unique ID generation
- use-debounce: Debouncing hooks
- hex-rgb: Color utilities
- fast-deep-equal: Deep equality checks
- vite: v6.3.5 - Modern build tool with HMR
- lightningcss: CSS optimization (faster than cssnano)
- esbuild: JavaScript minification (faster than terser)
- turbo: v2.5.8 - Monorepo task orchestration
- typescript: v5.9.3 - Latest with modern strict options
- vitest: v3.1.4 - Test runner
- @vitejs/plugin-react: Vite React plugin
- oxlint: v1.0+ - Rust-based ultra-fast linter
- knip: Unused code/dependency detection
- eslint: v9+ with flat config format
If builds fail:
- Clear node_modules:
rm -rf node_modules && pnpm install - Clear Turbo cache:
rm -rf .turbo - Clear Vite cache:
rm -rf packages/lib/node_modules/.vite - Clear TypeScript build cache:
find . -name ".tsbuildinfo" -delete - Ensure correct Node version:
nvm use(reads from.nvmrc)
If ESLint shows errors after upgrade:
- The project now uses ESLint flat config (
eslint.config.mjs) - Make sure you're using ESLint v9+
- Run
pnpm oxlintfirst for fast feedback - Check that your IDE ESLint extension supports flat config
CSS Module types are auto-generated by typescript-plugin-css-modules. If types are missing:
- Ensure
typescript-plugin-css-modulesis in devDependencies - Check
tsconfig.jsonincludes the plugin - Restart TypeScript server in your editor
- Ensure you're in the correct package directory
- Check that
test-setup.tsis properly loaded - Use
--uiflag for interactive debugging - Check coverage with
--coverageflag
- eslint.config.mjs: ESLint flat config (modern format)
- .oxlintrc.json: Oxlint configuration for fast linting
- .knip.json: Knip configuration with workspace awareness for monorepo
- .nvmrc: Node.js version specification (20.18.1)
- .browserslistrc: Browser targets for 2025
- turbo.json: Turborepo task pipeline (includes oxlint, knip tasks)
- MONOREPO_TOOLING.md: Detailed guide on how tooling works across packages
- packages/lib/vite.config.ts: Library build with LightningCSS
- packages/lib/vitest.config.ts: Test configuration for CI
- packages/lib/vitest.config.dev.ts: Test configuration for development
- .storybook/main.ts: Storybook configuration
- packages/lib/react-creme.ts: Library entry point with all exports
- packages/lib/design/: Global design system tokens and utilities
→ Replaced with.eslintrc.jseslint.config.mjs(flat config)→ Removed (project uses Vite)webpack.config.mjs→ Removed (no longer needed)tsconfig.webpack.json
When adding or modifying components:
- UX Review: Use the UX Review Checklist for comprehensive audits or Quick Checklist for rapid reviews
- Component Implementation: Create/modify in
components/<name>/ - Export: Add exports to
react-creme.ts - Styles: Use CSS Modules with design tokens from
@design - Types: Define in
<component>-model.tsand export in main file - Tests: Add comprehensive tests in
__tests__/(includingjest-axefor accessibility) - Storybook: Add stories in
packages/storybook/stories/ - Lint & Format: Run
pnpm cleanbefore committing - Documentation: Update documentation site if needed
All components must meet these minimum requirements:
- ✅ WCAG 2.1 Level AA compliance (use
jest-axein tests) - ✅ Full keyboard accessibility (Tab, Enter, Arrows, Escape, etc.)
- ✅ Design tokens instead of hardcoded values
- ✅ Touch-friendly targets (≥ 44×44px)
- ✅ All interactive states (hover, focus, active, disabled)
- ✅ Dark mode support
See Design System documentation for detailed guidelines.
- Bundle Size: ~115KB gzipped (full library with all 57 components and CSS). Individual components with tree-shaking: ~12-20KB gzipped
- Tree-shaking is enabled (
sideEffects: false) - import only what you need - CSS is bundled as a single file (~28KB gzipped) to reduce HTTP requests
- Terser minification with console/debugger removal in production
- Component lazy-loading is supported by consumers
- Run
pnpm sizeinpackages/lib/to measure bundle size with size-limit