Skip to content

Commit 7c55747

Browse files
Update Claude context document
1 parent b5ef439 commit 7c55747

2 files changed

Lines changed: 209 additions & 26 deletions

File tree

CLAUDE.md

Lines changed: 208 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,57 +5,240 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
55
## Commands
66

77
```bash
8-
yarn develop # Start development server
9-
yarn build # Production build
10-
yarn serve # Serve built site locally
11-
yarn clean # Clear Gatsby cache (use when builds behave unexpectedly)
8+
yarn develop # Start development server (hot reload)
9+
yarn build # Production build (required before playwright tests)
10+
yarn serve # Serve production build locally on :9000
11+
yarn clean # Clear Gatsby cache — run this when builds behave unexpectedly
1212
yarn type-check # TypeScript type checking without emitting
13-
yarn format # Format code with Prettier
13+
yarn format # Format all files with Prettier
1414
```
1515

1616
### Testing (Playwright visual regression)
1717

18+
Tests run against a served production build — always `yarn build && yarn serve` before running locally.
19+
20+
```bash
21+
yarn playwright test # Run all 30 tests
22+
yarn playwright test visual-regression-dark-theme.spec.ts # Run single spec
23+
yarn playwright test --grep "Home Page" # Run by name
24+
yarn playwright test --update-snapshots # Regenerate baselines
25+
yarn playwright test --debug # Debug mode
26+
```
27+
28+
The local base URL is set via `.env.local`:
29+
```
30+
PLAYWRIGHT_TEST_BASE_URL=http://localhost:9000/
31+
```
32+
33+
In CI, `PLAYWRIGHT_TEST_BASE_URL` is set to the Netlify preview URL for the PR.
34+
35+
### Snapshot baselines
36+
37+
30 tests total: 5 pages × 2 themes (light/dark) × 3 viewports (mobile/tablet/desktop).
38+
39+
Snapshots are stored under `tests/__screenshots__/{platform}/` where platform is auto-detected via `process.platform` in `playwright.config.ts`. No env var needed — macOS and Linux CI each use their own folder automatically.
40+
41+
- **Local (macOS):** `tests/__screenshots__/darwin/`
42+
- **CI (Linux):** `tests/__screenshots__/linux/`
43+
44+
**Updating baselines locally** (after a deliberate visual change):
1845
```bash
19-
yarn playwright test # Run all tests
20-
yarn playwright test visual-regression.spec.ts # Run single test file
21-
yarn playwright test --debug # Debug mode
46+
yarn build && yarn serve
47+
yarn playwright test --update-snapshots
48+
git add tests/__screenshots__/darwin/
2249
```
2350

24-
Tests run against a live URL. Locally defaults to `https://ajeetchaulagain.com`; override with `PLAYWRIGHT_TEST_BASE_URL`. In CI, they run against the Netlify preview deployment on PRs to master.
51+
**Updating CI baselines:** Trigger a CI run with `--update-snapshots` and commit the updated `tests/__screenshots__/linux/` files. The darwin and linux folders are independent — updating one doesn't affect the other.
52+
53+
**If Gatsby caching causes a webpack error** after switching branches or reverting packages, run `yarn clean` before `yarn build`.
2554

2655
## Architecture
2756

28-
**Gatsby static site** — personal portfolio/blog. Content is authored in MDX files and Gatsby dynamically generates pages from them.
57+
**Gatsby 5 static site** — personal portfolio/blog. Content is MDX files; Gatsby generates pages from them at build time.
2958

3059
### Content flow
3160

32-
- `src/posts/` — Blog posts as MDX files → rendered via `src/templates/BlogPage/`
33-
- `src/projects/` — Project entries as MDX files → rendered via `src/templates/ProjectPage/`
34-
- `gatsby-node.ts`Queries GraphQL for all posts/projects and programmatically creates pages
35-
- `gatsby-config.ts` — Plugin setup: MDX processing, Prism syntax highlighting, image optimization, sitemap, analytics
61+
- `src/posts/` — Blog posts as MDX → rendered via `src/templates/BlogPage/`
62+
- `src/projects/` — Project entries as MDX → rendered via `src/templates/ProjectPage/`
63+
- `gatsby-node.ts` — GraphQL queries all posts/projects and programmatically creates pages
64+
- `gatsby-config.ts` — Plugin setup: MDX, Prism syntax highlighting, image optimization, sitemap, analytics
3665

3766
### Key patterns
3867

39-
- **Markdown decorators** (`src/markdown-decorators/`) — Custom React components injected into MDX rendering (e.g., `ProjectCardDecorator`, `BlogPostCardDecorator`, `ButtonLinkDecorator`). These are passed as components to the MDX provider so MDX authors can use them directly.
40-
- **Global context** (`src/contexts/GlobalContextProvider.tsx`) — App-wide state (e.g., dark/light theme). Wrapped in `gatsby-browser.js` and `gatsby-ssr.js` to apply to all pages.
41-
- **Styled components** throughout — use `styled-components-breakpoint` for responsive styles and `styled-components-spacing` for spacing utilities.
42-
- **TypeScript path alias**`baseUrl: ./src/` in `tsconfig.json`, so imports can use `components/...`, `hooks/...`, etc. directly.
68+
- **Markdown decorators** (`src/markdown-decorators/`) — Custom React components injected into MDX (e.g. `BlogPostCardDecorator`, `ButtonLinkDecorator`). Registered in the MDX provider so MDX authors can use them as JSX tags directly in content.
69+
- **MarkdownRenderer** (`src/components/markdown-renderer/`) — Wraps `MDXProvider`, maps HTML elements (h1–h4, ul, ol, a, p, etc.) to styled components. Add new MDX-injectable components here.
70+
- **Global context** (`src/contexts/GlobalContextProvider.tsx`) — App-wide state (dark/light theme toggle). Wrapped in both `gatsby-browser.js` and `gatsby-ssr.js`.
71+
- **Theme system**`src/components/theme/colors.ts` defines light/dark color tokens; `src/components/theme/brand.ts` defines brand colors (primary, accent). Theme is injected via `ThemeProvider` from styled-components.
72+
- **TypeScript path alias**`baseUrl: ./src/` in `tsconfig.json`. Pages and templates import from `components/...`, `hooks/...` etc. (absolute). Components import siblings with relative paths (`../heading/Heading`).
73+
74+
### Import conventions
75+
76+
- `src/pages/` and `src/templates/` — use absolute barrel imports: `import { Foo } from 'components/foo/Foo'`
77+
- `src/components/` — use relative imports for siblings: `import { Bar } from '../bar/Bar'`
78+
- Do not re-export internal styled components from `src/components/index.ts` — only export public-facing React components
4379

4480
### Styling
4581

46-
Global styles live in `src/styles/` (CSS reset, base styles, Prism themes for light/dark mode). Component styles are co-located with components using styled-components.
82+
Component styles are co-located in a `styles.tsx` file next to each component. Global styles live in `src/styles/` and are composed in `GlobalStyle` (CSS reset, base styles, Prism themes, scrollbar, code titles).
83+
84+
#### Styled-components utilities
85+
86+
```ts
87+
import { mt, mb, p, mr } from 'styled-components-spacing'; // spacing scale 0–12
88+
import breakpoint from 'styled-components-breakpoint'; // responsive helpers
89+
90+
const Card = styled.div`
91+
${p(5)}; // padding: 1.5rem
92+
${breakpoint('xs', 'lg')` // applies between xs and lg
93+
${p(4)};
94+
`}
95+
`;
96+
```
97+
98+
#### Accessing theme tokens
99+
100+
```ts
101+
const El = styled.div`
102+
color: ${({ theme }) => theme.colors.primaryText};
103+
font-size: ${({ theme }) => theme.fontSizes.medium};
104+
font-family: ${({ theme }) => theme.fonts.body};
105+
border-radius: ${({ theme }) => theme.borderRadius.base};
106+
font-weight: ${({ theme }) => theme.fontWeights[5]}; // 500
107+
`;
108+
```
109+
110+
## Design system
111+
112+
### Breakpoints
113+
114+
| Name | px | Viewport |
115+
|------|----|---------|
116+
| `xs` | 0 | mobile |
117+
| `xsm` | 370 | small mobile |
118+
| `sm` | 480 | |
119+
| `md` | 640 | tablet |
120+
| `lg` | 800 | |
121+
| `xl` | 992 | desktop |
122+
| `xxl` | 1120 | |
123+
| `xxxl` | 1200 | large desktop |
124+
125+
### Spacing scale
126+
127+
`styled-components-spacing` utilities (`mt`, `mb`, `p`, `mr`, etc.) take a scale key 0–12:
128+
129+
| Key | Value |
130+
|-----|-------|
131+
| 0 | 0 |
132+
| 1 | 0.25rem |
133+
| 2 | 0.5rem |
134+
| 3 | 0.75rem |
135+
| 4 | 1rem |
136+
| 5 | 1.5rem |
137+
| 6 | 2rem |
138+
| 7 | 2.5rem |
139+
| 8 | 3rem |
140+
| 9 | 4rem |
141+
| 10 | 5rem |
142+
143+
### Typography
144+
145+
**Fonts** (`theme.fonts`):
146+
| Key | Family |
147+
|-----|--------|
148+
| `body` | Source Sans Pro, system fallbacks |
149+
| `primaryHeading` | Domine (serif) |
150+
| `secondaryHeading` | Roboto |
151+
152+
**Font sizes** (`theme.fontSizes`):
153+
| Key | Value |
154+
|-----|-------|
155+
| `xsmall` | 0.975rem |
156+
| `small` | 1.05rem |
157+
| `xmedium` | 1.125rem |
158+
| `medium` | 1.2rem |
159+
| `large` | 1.3rem |
160+
| `xlarge` | 1.4rem |
161+
162+
**Heading sizes** (`theme.headingFontSizes`): `small` 1.2rem → `medium` 1.5rem → `large` 1.8rem → `xlarge` 2.1rem
163+
164+
**Font weights** (`theme.fontWeights[n]`): index 0–9 maps to 0, 100, 200 … 900 — e.g. `fontWeights[4]` = 400, `fontWeights[7]` = 700
165+
166+
**Line heights** (`theme.lineHeights`): `regular` 1.8 · `condensed` 1.5 · `extraCondensed` 1.4
167+
168+
### Theming (light / dark)
169+
170+
Source of truth is `src/components/theme/brand.ts` — edit brand values there to shift the whole theme:
171+
172+
```ts
173+
// light
174+
lightBrand = { base: '#81A6C6', accent: '#3B6E9A', heroText: '#0f2035', heroParagraph: '#2a4a68' }
175+
// dark
176+
darkBrand = { base: '#1f1f1f', accent: '#81A6C6', heroText: '#e7e7e7', heroParagraph: '#C8C8C8' }
177+
```
178+
179+
Color tokens (`theme.colors`) derived from brand values:
180+
181+
| Token | Purpose |
182+
|-------|---------|
183+
| `primaryBackground` | Page background |
184+
| `secondaryBackground` | Section / card background |
185+
| `tertiaryBackground` | Code title bar, subtle fills |
186+
| `quaternaryBackground` | Hover states |
187+
| `primaryText` | Body text |
188+
| `brandPrimary` | Links, accents, primary CTA — equals `accent` |
189+
| `heroBackground` | Header / hero area background |
190+
| `heroText` / `heroParagraph` | Text rendered on brand background |
191+
| `cardBorder` | Card / code block borders |
192+
| `codeHighlightBackground` | Highlighted line fill |
193+
| `codeHighlightBorderColor` | Highlighted line left-border accent |
194+
195+
Theme is toggled via `useThemeToggle` hook → `GlobalContextProvider``GlobalStateContext`. Default is `lightTheme`. Wrapped in `gatsby-browser.js` and `gatsby-ssr.js` so it applies to all pages including SSR.
196+
197+
### Code syntax highlighting
198+
199+
Two Prism themes co-located in `src/styles/`:
200+
- **Dark:** Laserwave — colourful on dark background
201+
- **Light:** One Light (Atom) — subtle on white background
202+
203+
Theme is applied conditionally in `CodeBlockStyles.tsx`:
204+
```ts
205+
${({ theme }) => theme.name === 'lightTheme' ? PrismOneLightTheme : PrismDarkTheme}
206+
```
207+
208+
Highlighted lines use `theme.colors.codeHighlightBackground` + `codeHighlightBorderColor` for the left accent border.
209+
210+
## Deployment
211+
212+
- **Host:** Netlify
213+
- **Branch deploys:** Every PR to `master` gets a Netlify preview URL; CI runs Playwright against it
214+
- **Production:** Merges to `master` auto-deploy to `https://ajeetchaulagain.com`
215+
216+
## Package upgrade constraints
217+
218+
These packages must **not** be upgraded to the next major version without careful testing:
219+
220+
| Package | Current | Reason to hold |
221+
|---------|---------|----------------|
222+
| `styled-components` | 5 | v6 may change CSS output and break visual regression baselines |
223+
| `react` / `react-dom` | 18 | React 19 has breaking changes; Gatsby 5 compatibility unverified |
224+
| `@mdx-js/react` | 2 | v3 breaks MDXProvider API used in MarkdownRenderer |
225+
| `react-icons` | 4 | v5 restructured FA packages; some icons renamed/moved |
226+
| `prism-react-renderer` | 1 | v2 has breaking API changes; used for code block rendering |
227+
| `eslint` | 7 | v9 requires flat config migration |
228+
| `@fontsource/*` | 4 | v5 changes font metrics causing pixel-level visual regression failures |
47229

48230
## yarn resolutions
49231

50-
The `resolutions` field in `package.json` pins specific transitive dependency versions. Do not remove these without understanding why they were added:
232+
The `resolutions` field in `package.json` pins transitive dependency versions. Do not remove without understanding why:
51233

52234
| Package | Reason |
53235
|---------|--------|
54-
| `@types/react` | `@reach/router` (used internally by Gatsby) bundles its own older `@types/react`, causing duplicate React type conflicts. Pinned to force a single version. |
55-
| `webpack` | CVE-2025-68458 and CVE-2025-68157 (SSRF, LOW) introduced via `gatsby@5`. Fixed in `webpack@5.104.1`. |
56-
| `sharp` | Gatsby image plugins (gatsby-plugin-sharp, gatsby-transformer-sharp, gatsby-plugin-manifest, gatsby-sharp) each install their own nested copy of `sharp`, causing libvips integrity check failures on Netlify's Linux CI environment. Pinned to force a single hoisted installation. |
236+
| `@types/react` | `@reach/router` (Gatsby internal) ships its own older `@types/react`, causing duplicate type conflicts. Pinned to force a single version. |
237+
| `webpack` | CVE-2025-68458 and CVE-2025-68157 (SSRF) via `gatsby@5`. Fixed in `webpack@5.104.1`. |
238+
| `sharp` | Gatsby image plugins each install their own nested `sharp`, causing libvips integrity failures on Netlify CI. Pinned to force a single hoisted copy. |
57239

58240
## Code style
59241

60-
- ESLint with Airbnb config + TypeScript; Prettier with single quotes, 2-space indent, 80-char line width, trailing commas (es5)
61-
- Strict TypeScript — no unused locals/parameters
242+
- ESLint with Airbnb config + TypeScript; Prettier: single quotes, 2-space indent, 80-char line width, trailing commas (es5)
243+
- Strict TypeScript — no unused locals/parameters (`strict: true` in tsconfig)
244+
- No default exports — use named exports throughout

playwright.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export default defineConfig({
4343

4444
expect: {
4545
toHaveScreenshot: {
46-
maxDiffPixelRatio: 0.01, // Allow 1% pixels diff ratio
46+
maxDiffPixelRatio: 0.02, // Allow 2% pixels diff ratio
4747
},
4848
},
4949

0 commit comments

Comments
 (0)