This document provides AI agents with essential context about the SWS NextJS Static Website project, including architecture, conventions, workflows, and best practices.
SWS NextJS Static Website is a static site built with Next.js 15 (App Router) and Stanford's design system, providing a fast, accessible, and maintainable web experience for Stanford Web Services projects.
- Deliver fast, reliable, accessible static web experiences
- Provide reusable components following Stanford's design system
- Maintain clean, type-safe code with TypeScript
- Support long-term maintainability and collaboration
- Enable easy content updates through static data files
| Layer | Technology | Notes |
|---|---|---|
| Framework | Next.js 15 | App Router, Static Export, TypeScript strict mode |
| Styling | TailwindCSS v4 | Utility-first, Stanford design tokens |
| Components | React 19 | Server components by default, client when needed |
| Fonts | Stanford Fonts | Source Sans 3, custom font loading |
| Type System | TypeScript | Strict mode enabled |
| Testing | Jest + Cypress | Unit and component testing |
| Linting | ESLint + Prettier | Code quality and formatting |
Node Version: >=22.0.0
next- Next.js frameworkreact- React librarytailwindcss- Utility-first CSS frameworkclsx- Utility for conditionally building class names@heroicons/react- Icon library
- Static Export: Fully static HTML generated at build time
- Server Components: Default rendering strategy for better performance
- Client Components: Used only when needed for interactivity
- Component Library: Reusable UI components following Stanford design patterns
- Type Safety: Full TypeScript coverage for components and utilities
├── src/
│ ├── app/ # Next.js app directory (App Router)
│ │ ├── layout.tsx # Root layout
│ │ ├── page.tsx # Homepage
│ │ └── not-found.tsx # 404 page
│ ├── components/ # React components
│ │ ├── elements/ # Base UI elements (Text, Button, Link, etc.)
│ │ ├── paragraphs/ # Content paragraph components
│ │ └── patterns/ # Composite UI patterns (HeroBanner, ImageCard, etc.)
│ ├── styles/ # Global styles
│ │ └── globals.css # Global CSS imports
│ └── utilities/ # Utility functions and helpers
├── public/ # Static assets
│ ├── fonts/ # Stanford fonts
│ └── images/ # Static images
├── tailwind.config.ts # Tailwind configuration
├── tsconfig.json # TypeScript configuration
└── next.config.ts # Next.js configuration
Components are organized into three main categories:
-
Elements (
src/components/elements/) - Basic UI building blocks- Text, headings, buttons, links, images, etc.
- Highly reusable, minimal dependencies
- Example:
Text.tsx,Button.tsx,Link.tsx
-
Paragraphs (
src/components/paragraphs/) - Content layout components- Row layouts (one-column, two-column, three-column)
- Card components for structured content
- Example:
card-paragraph.tsx,media-caption-paragraph.tsx
-
Patterns (
src/components/patterns/) - Composite UI patterns- Complex compositions of elements and paragraphs
- Page-level components
- Example:
hero-banner.tsx,image-card.tsx
Files:
- Components:
kebab-case.tsx(e.g.,hero-banner.tsx,image-card.tsx) - Types:
PascalCase.types.ts(if needed for complex types) - Tests:
ComponentName.test.tsx(Jest),ComponentName.cy.tsx(Cypress)
Components:
- Component names:
PascalCase(e.g.,HeroBanner,ImageCard,Text) - Props types:
ComponentNamePropsor inline type definitions
Exports:
- Default exports for components
- Named exports for additional utilities or types
Pattern:
type ComponentProps = React.HTMLAttributes<HTMLDivElement> & {
required: string;
optional?: boolean;
children?: React.ReactNode;
};
export const Component = ({ required, optional, children, ...props }: ComponentProps) => (
<div {...props}>{children}</div>
);Common Patterns:
- Extend HTML element attributes for proper DOM prop spreading
- Use optional props with
?for non-required values - Avoid
Maybe<T>wrapper types - use standard TypeScript optionals
Styling Approach:
- Use Tailwind utility classes directly in component files
- Stanford design tokens configured in
tailwind.config.ts - Conditional classes with
clsx()utilities - No separate
.styles.tsfiles unless styles are complex/reusable
Example:
import {clsx} from "clsx";
import twMerge from "@/utilities/utils/twMerge";
export const Card = ({ variant, className, children }: CardProps) => (
<div className={twMerge(
"border border-black-10 bg-white shadow-lg",
clsx({
"p-10": variant === "padded",
"p-0": variant === "flush",
}),
className
)}>
{children}
</div>
);strict: true- Strict type checking enabledbaseUrl: "."- Base path for importspaths: { "@/*": ["./src/*"] }- Path alias for absolute imports
Import paths:
// Use path alias for absolute imports
import { Component } from '@/components/elements/component';
import { utility } from '@/utilities/utility';Formatting:
semi: always- Require semicolonsquotes: single- Use single quotes (except JSX)jsx-quotes: prefer-double- Use double quotes in JSXcomma-dangle: always-multiline- Require trailing commas in multiline
React:
react/jsx-props-no-spreading: off- Allow prop spreadingreact/prop-types: off- Prop types not required (TypeScript)- Server components by default, add
'use client'directive when needed
TypeScript:
@typescript-eslint/no-unused-vars: warn- Warn on unused variables- Explicit return types encouraged for exported functions
npm run lint # Check for errors and warnings
npm run lint:fix # Auto-fix issues
npm run tsc # TypeScript type checkingPrinciples:
- Test critical paths and user flows
- Focus on real-world scenarios, not edge cases
- Catch regressions early
- Balance coverage with effort
- Keep tests maintainable
Jest - Unit tests for pure functions and utilities
- Config:
jest.config.ts - Run:
npm run test
Cypress - Component tests
- Component tests: Test individual React components in isolation
- Run component tests:
npm run cy:component
File location: Same directory as component (component-name.cy.tsx)
Pattern:
import { mount } from 'cypress/react';
import { Component } from './component';
describe('Component', () => {
it('should render with expected content', () => {
mount(<Component prop="value" />);
cy.get('selector').should('contain.text', 'value');
});
});Test: Props, interactions, conditionals, a11y | Skip: Third-party internals, edge cases
Branches: main (production)
Process:
- Create feature branch:
git checkout -b feature/name - Submit PR to
main - Get approvals: tests pass, no lint errors, code review
- Squash and merge to
main
- Developers review AI output before PR
- PRs require peer review
- PRs should be labeled with
AI Developerlabel if AI-assisted
- Use AI to generate components, boilerplate, tests, and docs
- Reference project-specific types and naming conventions
- Favor deterministic code, minimal side effects, and typed interfaces
- Never store personalized AI configs in the repo (
.gitignorethem) - Always validate AI-generated code meets project standards
- Design System: Tailwind CSS 4 config with Stanford design tokens
- Colors: Stanford brand colors (cardinal red, digital blue, etc.)
- Typography: Source Sans 3 font family, semantic heading levels
- Spacing: Consistent spacing scale using Tailwind utilities
- Breakpoints: Mobile-first responsive design
Common design tokens available in Tailwind:
Colors:
cardinal-red- Stanford primary reddigital-blue- Stanford blueblack-true,black-90,black-80, etc. - Black scalewhite- Pure whitefoggy-light,cool-grey,stone-dark- Neutral grays
Typography:
- Font family: Source Sans 3 (default)
- Heading components:
H1,H2,H3,H4 - Text component:
Textwith size/weight variants
Spacing:
- Use standard Tailwind spacing scale
- Margin utilities:
mb-{size}for consistent vertical rhythm
import { H2 } from '@/components/elements/headers';
import { Text } from '@/components/elements/text';
<H2>Section Heading</H2>
<Text size="lg" weight="semibold">Body text with custom styling</Text>import OneColumn from '@/components/paragraphs/one-column';
import TwoColumn from '@/components/paragraphs/two-column';
<OneColumn bg_color="f4f4f4" top_padding="more">
<Text>Content in a single column with background</Text>
</OneColumn>
<TwoColumn column_widths="33-67" vertical_dividers>
<div>Left column content</div>
<div>Right column content</div>
</TwoColumn>import CardParagraph from '@/components/paragraphs/card-paragraph';
<CardParagraph
header="Card Title"
superHeader="Category"
body="<p>Card body content as HTML</p>"
link={{ url: "/path", title: "Learn More" }}
imageUrl="/images/photo.jpg"
imageAlt="Description"
headingLevel="h3"
linkStyle="action"
/>import HeroBanner from '@/components/patterns/hero-banner';
import ImageCard from '@/components/patterns/image-card';
<HeroBanner
imageUrl="/images/hero.jpg"
imageAlt="Hero image"
overlayPosition="center"
overlayColor="#000000"
>
<H1>Page Title</H1>
<Text>Hero description</Text>
</HeroBanner>
<ImageCard
imageUrl="/images/card.jpg"
imageAlt="Card image"
isArticle={true}
squareImage={false}
>
<H3>Card heading</H3>
<Text>Card content</Text>
</ImageCard>- All images must have descriptive
alttext - Heading hierarchy must be semantic (don't skip levels)
- Interactive elements must be keyboard accessible
- Color contrast must meet WCAG AA standards
- Form inputs must have associated labels
- Use semantic HTML elements (
<nav>,<main>,<article>, etc.) - Provide ARIA labels for icon-only buttons
- Ensure focus states are visible
- Test with keyboard navigation
- Test with screen readers when possible
-
Create component file in appropriate directory:
src/components/elements/for basic elementssrc/components/paragraphs/for content layoutssrc/components/patterns/for complex patterns
-
Define TypeScript props interface
-
Implement component with proper typing
-
Add Tailwind styling using utility classes
-
Export component (default export)
-
Create test file if needed (
.cy.tsxfor Cypress)
- Create route in
src/app/ - Create
page.tsxfile for the route - Use existing components to build page layout
- Add metadata with
generateMetadataif needed - Test page rendering and navigation
npm run dev # Start development server
npm run build # Build for production
npm run start # Start production server
npm run lint # Check for lint errors
npm run lint:fix # Auto-fix lint errors
npm run tsc # TypeScript type checking
npm run test # Run Jest tests
npm run cy:component # Run Cypress component tests- Code in TypeScript and follow Next.js App Router conventions
- Use existing components from the component library when possible
- Apply Tailwind utilities for styling with Stanford design tokens
- Ensure accessibility (ARIA labels, semantic HTML, keyboard nav)
- Follow naming conventions (kebab-case files, PascalCase components)
- Write tests for new components when appropriate
- Keep it simple - prefer static content over complex state management
- Avoid creating unnecessary README.md files unless prompted
- Reference existing components as examples for consistency
- Favor deterministic code with minimal side effects
- Use TypeScript for type safety
- Follow existing patterns in the codebase
- Keep components focused and single-purpose
Next.js:
- App Router only (not Pages Router)
- Server components by default - add
'use client'directive for client components - Use
generateMetadatafor page metadata - Always use
@/imports for absolute paths
TypeScript:
- Strict mode enabled - watch for null/undefined
- Type props explicitly for all components
- No
anytypes - use proper typing
Styling:
- Use
twMerge()to safely merge Tailwind classes - Use
clsx()for conditional classes - Custom variants:
hocus:(hover + focus)
Components:
- Server components can't use hooks or browser APIs
- Client components need
'use client'directive at top of file - Spread HTML attributes with
...propsfor flexibility
# Development
npm run dev # Start dev server
# Building
npm run build # Build static site
npm run start # Preview production build
# Testing
npm run test # Run Jest tests
npm run cy:component # Run Cypress component tests
# Linting
npm run lint # Check for lint errors
npm run lint:fix # Auto-fix lint errors
npm run tsc # TypeScript type checking- ✅ Use TypeScript with explicit typing
- ✅ Style with Tailwind utilities and Stanford tokens
- ✅ Follow component organization (elements/paragraphs/patterns)
- ✅ Test critical components with Cypress
- ✅ Use
@/imports for absolute paths - ✅ Follow kebab-case for files, PascalCase for components
- ✅ Server components by default, client only when needed
- ✅ Run
lint+tscbefore committing - ✅ Ensure accessibility (alt text, ARIA, semantic HTML)
- ✅ Reference existing components as examples