This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
OpenStax webview — the main openstax.org website. A Preact/TypeScript SPA that fetches content from an OpenStax CMS API and renders educational resource pages (textbooks, subjects, blog, errata, etc.).
script/build # Dev build (output in dev/)yarn test # Run all tests with coverage
yarn jest layout.test # Run a single test by name pattern
yarn jest test/src/components/shell.test.tsx # Run a specific test fileNote: Tests require the dev build (dev/ directory) to exist.
yarn lint # Run all linters (JS + TS + CSS)
yarn lint:js # ESLint only
yarn lint:ts # TypeScript type-checking only (tsc --noEmit)
yarn lint:css # Stylelint on SCSS files- Preact with
@preact/compataliased asreact/react-dom(in both webpack and jest configs) - React Router DOM v6 for routing
- Emotion (
@emotion/react,@emotion/styled) for CSS-in-JS alongside SCSS files - React Intl for internationalization (babel-plugin-formatjs for message extraction)
src/app/main.js— bootstraps the app: fetches CMS settings, renders<App>into#appsrc/app/components/shell/shell.tsx— top-level component, wraps everything in context providers- Context provider nesting:
SharedData → User → Language → Portal → SubjectCategory → BrowserRouter src/app/components/shell/router.tsx— main route definitions
All state management uses React Context (no Redux). Key contexts in src/app/contexts/:
user.ts— authentication, faculty status, account infolanguage.tsx— locale/language selectionlayout.tsx— layout switching (default vs landing)portal.tsx— portal routing (landing page sub-sites)salesforce.tsx— Salesforce form integrationshared-data.ts— shared data across componentssubject-category.ts— subject filtering
src/app/helpers/cms-fetch.ts— primary data fetching utility, wrapsfetch()with retry logic- CMS API endpoint controlled by
API_ORIGINenv var (defaults tohttps://dev.openstax.org) - Settings loaded from
{API_ORIGIN}/cms/webview-settings - Custom hooks (
usePageData,useDocumentHead, etc.) insrc/app/helpers/
src/app/helpers/jit-load.tsx— lazy loading wrapper usingReact.lazy+Suspense- Webpack splits vendor chunks per-package
Main routes in router.tsx:
/— Home page/errata/*— Errata pages/details/*— Book detail pages/embedded/*— Embeddable pages (e.g., contact form)/:dir/*— Catch-all for CMS-driven pages (subjects, blog, general, flex pages, portals)
~/maps tosrc/app/(configured in webpack, tsconfig, and jest)
- Jest 27 with
@testing-library/preactand@testing-library/user-event - Tests live in
test/src/mirroringsrc/app/structure - Test data fixtures in
test/src/data/ - Test helpers in
test/helpers/(fetch mocker, localStorage mock, etc.) test/setupFile.jssets up global mocks (localStorage, requestAnimationFrame, ReactModal, etc.)- Heavy use of
jest.spyOnto mock imported modules andusePageDatafor CMS data
- Max complexity: 6, max depth: 4, max params: 4, max line length: 120
- Single quotes, semicolons required, no trailing commas
prefer-const,prefer-arrow-callback,prefer-template- React hooks rules enforced (
exhaustive-depsis an error) no-shadowenabled — avoid variable shadowing- Unused vars pattern: prefix with
_ - The
cssprop is allowed on JSX elements (Emotion)
- Single quotes, no trailing commas, no bracket spacing, JSX uses double quotes
- Strict mode enabled, target ES6, module ES2020
- Gradually migrating from JS/JSX to TS/TSX