CLAUDE.md Behavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed.
Tradeoff: These guidelines bias toward caution over speed. For trivial tasks, use judgment.
- Think Before Coding Don't assume. Don't hide confusion. Surface tradeoffs.
Before implementing:
State your assumptions explicitly. If uncertain, ask. If multiple interpretations exist, present them - don't pick silently. If a simpler approach exists, say so. Push back when warranted. If something is unclear, stop. Name what's confusing. Ask.
- Simplicity First
Minimum code that solves the problem. Nothing speculative.
No features beyond what was asked. No abstractions for single-use code. No "flexibility" or "configurability" that wasn't requested. No error handling for impossible scenarios. If you write 200 lines and it could be 50, rewrite it. Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
- Surgical Changes Touch only what you must. Clean up only your own mess.
When editing existing code:
Don't "improve" adjacent code, comments, or formatting. Don't refactor things that aren't broken. Match existing style, even if you'd do it differently. If you notice unrelated dead code, mention it - don't delete it. When your changes create orphans:
Remove imports/variables/functions that YOUR changes made unused. Don't remove pre-existing dead code unless asked. The test: Every changed line should trace directly to the user's request.
- Goal-Driven Execution Define success criteria. Loop until verified.
Transform tasks into verifiable goals:
"Add validation" → "Write tests for invalid inputs, then make them pass" "Fix the bug" → "Write a test that reproduces it, then make it pass" "Refactor X" → "Ensure tests pass before and after" For multi-step tasks, state a brief plan:
- [Step] → verify: [check]
- [Step] → verify: [check]
- [Step] → verify: [check]
Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.
These guidelines are working if: fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes.
Technical conventions: The following section mirrors .cursor/rules/claude-conventions.mdc (Cursor rules). When changing conventions, update both files so they stay identical. Covers .ts/.tsx import order, React/hooks patterns, and SCSS/token rules (previously in separate globs rules).
- Next.js 15, App Router, TypeScript, React 19.
- The
@/alias maps to./src/(seetsconfig.json). - Prefer
@/components/...,@/hooks/...,@/lib/...,@/styles/...when a dedicated path entry exists. - Avoid deep relative imports (
../../../) when the folder has a barrel (index.ts).
'use client'on the first line when the file uses hooks, local state, or client-only APIs (see also Components and"use client").- External packages first (
react,next/*, other libs), then a blank line. @/— always use the alias. Sort@/imports alphabetically by full path (cleaner diffs, fewer visual cycles).- Relative imports last: shared
@/styles/...globals or partials, then./component-name.scssat the end (after all JS/TS). import typefor type-only imports; may sit next to the related module or in the@/group while keeping alphabetical order.
// Order example
'use client';
import { useMemo, useState } from 'react';
import { ProjectCard } from '@/components/shared/ProjectCard';
import { useI18n } from '@/contexts/I18nContext';
import type { Project } from '@/lib/github';
import '@/styles/partials/buttons.scss';
import './projects-page.scss';- App Router:
src/app/(layouts, pages). - UI under
src/components/: feature folders in PascalCase (e.g.ProjectsPage/,ContactPage/). - Feature files in paired kebab-case:
component-name.tsx+component-name.scss. - Subcomponents in PascalCase subfolders (e.g.
ContactTerminal/,HeroScene/). - Shared:
src/components/shared/(e.g.SvgIcons/,ProjectCard/). Do not import feature A inside feature B without moving code toshared/. - Other:
src/contexts/,src/hooks/,src/lib/,src/styles/(tokens, partials, mixins,globals.scss).
- Server Components by default.
"use client"only when needed: local state, event handlers, or browser APIs (effects/layout that depend on the DOM).- Place the directive on the first line of the file that needs it.
- Rules of Hooks: call hooks only at the top level of a function component or custom hook — never inside conditions, loops, or nested plain functions.
- Lists: give each item a stable
key(id/slug). Avoid array index askeywhen order can change or items are inserted/removed. useEffect: keep effects focused; list all reactive values used inside in the dependency array (or justify an intentional omission with a short comment). Return a cleanup for subscriptions, listeners, timers, andrequestAnimationFrameids.- Derived data: prefer computing values during render when they follow from props/state; avoid mirroring the same information in extra state.
useMemo/useCallback: use when they prevent real work (expensive computation, stable reference for a memoized child) — not by default on every handler.useRef: use for DOM nodes and mutable values that should not trigger re-renders when updated.- Custom hooks: follow the
use+ camelCase filename rule (useScrollSpy.ts), export a single hook (or a small cohesive set), and keep side effects and browser APIs inside hooks or client components — see Hooks for placement undersrc/hooks/.
- Use CVA (
class-variance-authority) for component variants. Add the dependency when a component needs CVA and the package is not yet in the project. - For a few conditional classes,
cn()alone is enough; add CVA when several named variants share a stable prop API.
- Merge conditional classes with
cn()from@/lib/utils(built on clsx). If Tailwind is added later, you may extend the helper withtailwind-mergein the same file.
- No external SCSS/CSS utility frameworks (Tailwind, Bootstrap Sass stacks, similar) for component styling: use co-located
*.scss, tokens undersrc/styles/tokens/, and project partials/mixins — not third-party class-based styling layers. - No
@applyexcept insrc/styles/globals.scss. - Mobile-first; breakpoint naming aligned with sm, md, lg, xl (Sass variables, media queries, or tokens under
src/styles/, following existing patterns).
- Use
@use(not@import). Breakpoint tokens:@use '../../styles/tokens/breakpoints' as bp;(adjust../depth to the file location). - Optional mixins:
src/styles/mixins/_responsive.scss—mobile=max-width: $bp-mobile,desktop= above that.
- Preferred: base styles for the narrowest viewport; scale up with
min-widthor@include desktop { … }when using the mixin. - Legacy code that already uses
@media (max-width: bp.$bp-mobile)/bp.$bp-md: keep the same pattern within that file so one component does not mix two models.
- Prefer custom tokens in
src/styles/(variables, partials) instead of repeated raw values. - Colors, text, and surfaces:
var(--…)fromsrc/styles/tokens/_colors.scss(data-themethemes). Do not introduce stray hex/rgb unless briefly documented as an exception. - Spacing:
var(--space-*)fromtokens/_spacing.scss. Radii and borders:var(--border-radius-*),var(--border-width-*). Global typography/layout:var(--font-mono),var(--font-sans), and variables fromtokens/_layout.scsswhere appropriate. - New reusable values → add them to the right token file and use the variable instead of repeated magic numbers.
- Location:
src/hooks/. - File name:
useprefix + camelCase for the rest (e.g.useScrollSpy.ts,useMediaQuery.ts). - Import:
@/hooks/useScrollSpy(or the matching name).
- Feature components and pages: paired kebab-case
.tsx+.scss. - Feature folders: PascalCase.
- Folders with a public API:
index.tswithexport { X } from './x'. - Import from
@/components/FolderNamewhen a barrel exists instead of deep paths.
- Follow repo formatting; use
npm run format/npm run format:checkwhen preparing changes.