Initial commit: React frontend (SLP Software) + AIDLC workflow docs
Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
+47
@@ -0,0 +1,47 @@
|
||||
# Build and Test Summary
|
||||
|
||||
## Build Status
|
||||
- **Build Tool**: Vite 5 + TypeScript (`tsc -b`)
|
||||
- **Build Status**: Success
|
||||
- **Build Artifacts**: `dist/` (static HTML/JS/CSS/font bundle)
|
||||
- **Build Time**: Not formally measured; completes in a few seconds on a typical dev machine
|
||||
|
||||
## Test Execution Summary
|
||||
|
||||
### Unit Tests
|
||||
- **Total Tests**: 9
|
||||
- **Passed**: 9
|
||||
- **Failed**: 0
|
||||
- **Coverage**: No formal coverage gate configured
|
||||
- **Status**: Pass
|
||||
|
||||
### Static Analysis (ESLint)
|
||||
- **Errors**: 0
|
||||
- **Warnings**: 2 (non-blocking `react-refresh/only-export-components` in `src/routes/index.tsx` and `src/theme/ThemeProvider.tsx`)
|
||||
- **Status**: Pass
|
||||
|
||||
### Integration Tests (Manual Smoke Test)
|
||||
- **Test Scenarios**: 2 (navigation/scroll repeatability, theme toggle consistency)
|
||||
- **Passed**: 2 (manually verified during this session — the previously reported "click again and it jumps back to top" navigation bug is fixed)
|
||||
- **Failed**: 0
|
||||
- **Status**: Pass
|
||||
|
||||
### Performance Tests
|
||||
- **Response Time / LCP**: Not yet formally measured with Lighthouse this session (instructions provided; recommended before first production deployment)
|
||||
- **Throughput**: N/A (static site, no application server)
|
||||
- **Error Rate**: 0% observed during manual testing (no console errors)
|
||||
- **Status**: Not yet run — see `performance-test-instructions.md`
|
||||
|
||||
### Additional Tests
|
||||
- **Contract Tests**: N/A (no service boundaries)
|
||||
- **Security Tests**: N/A (no backend/auth surface for this static unit)
|
||||
- **E2E Tests**: Covered by the manual smoke test in `integration-test-instructions.md`
|
||||
|
||||
## Overall Status
|
||||
- **Build**: Success
|
||||
- **All Automated Tests**: Pass (9/9 unit tests, 0 lint errors)
|
||||
- **Manual Navigation Regression Check**: Pass (fixed and verified)
|
||||
- **Ready for Operations**: Yes
|
||||
|
||||
## Next Steps
|
||||
Ready to proceed to the Operations phase for deployment planning (Deployment Setup), as configured (`Include Operations Phase: Yes`, decided during Requirements Analysis).
|
||||
@@ -0,0 +1,43 @@
|
||||
# Build Instructions
|
||||
|
||||
## Prerequisites
|
||||
- **Build Tool**: Vite 5 + TypeScript (project references / `tsc -b`)
|
||||
- **Package Manager**: pnpm
|
||||
- **Runtime**: Node.js (LTS compatible with Vite 5 / TypeScript 5.6)
|
||||
- **Dependencies**: All packages listed in `package.json` (React 18, TanStack Router/Query, Tailwind CSS, Vitest, ESLint, Prettier)
|
||||
- **Environment Variables**: None required — the site is fully static, no backend/API keys
|
||||
- **System Requirements**: Any OS supported by Node.js; no special memory/disk requirements (small static bundle)
|
||||
|
||||
## Build Steps
|
||||
|
||||
### 1. Install Dependencies
|
||||
```bash
|
||||
pnpm install
|
||||
```
|
||||
|
||||
### 2. Configure Environment
|
||||
No environment configuration is required. The app has no runtime environment variables or external service credentials.
|
||||
|
||||
### 3. Build the Unit (react-frontend-app)
|
||||
```bash
|
||||
pnpm run build
|
||||
```
|
||||
This runs `tsc -b` (type-check via project references) followed by `vite build` (production bundle).
|
||||
|
||||
### 4. Verify Build Success
|
||||
- **Expected Output**: `tsc -b` completes with no type errors; `vite build` reports the generated chunks and finishes with `✓ built in <time>`
|
||||
- **Build Artifacts**: Static assets emitted to `dist/` (HTML, JS, CSS, fonts) — ready to be uploaded as-is to any static/FTP web host
|
||||
- **Common Warnings**: None expected under normal conditions
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Build Fails with Dependency Errors
|
||||
- **Cause**: `node_modules` missing/corrupted, or `pnpm-lock.yaml` out of sync with `package.json`
|
||||
- **Solution**: Delete `node_modules`, re-run `pnpm install`; if the lockfile is out of date, run `pnpm install` again to regenerate it, then rebuild
|
||||
|
||||
### Build Fails with Compilation Errors
|
||||
- **Cause**: TypeScript type errors introduced by recent changes (e.g. incorrect props, missing imports)
|
||||
- **Solution**: Read the `tsc -b` error output (file + line), fix the reported type issue, and re-run `pnpm run build`
|
||||
|
||||
## Verified Result (this session)
|
||||
- `pnpm run build` executed successfully: `tsc -b && vite build` completed without errors, `dist/` was generated correctly.
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
# Integration Test Instructions
|
||||
|
||||
## Purpose
|
||||
This feature consists of a single unit (`react-frontend-app`, a static marketing site with no backend service and no other units to integrate with). There is no cross-service integration to test. Instead, this stage covers **manual end-to-end smoke testing** of the built bundle in a browser, focusing on the areas where components/routing interact (navigation, routing, theming).
|
||||
|
||||
## Test Scenarios
|
||||
|
||||
### Scenario 1: Full Page Render & Navigation Flow
|
||||
- **Description**: Verify that the whole single-page site renders and that in-page navigation (Nav links, Hero CTAs, package CTA) scrolls to the correct sections repeatedly, without ever reverting to the top.
|
||||
- **Setup**: Run `pnpm run build && pnpm run preview` (serves the production `dist/` bundle), open the printed local URL in a browser
|
||||
- **Test Steps**:
|
||||
1. Click each Nav link (`Pakketten`, `Werkwijze`, `Over`, `Start project`) in sequence, multiple times each, including repeated clicks on the same link
|
||||
2. Click the Hero CTAs (`Bekijk pakketten`, `Plan een gesprek`)
|
||||
3. Click a package card's CTA (`Kies ...` / `Vraag offerte aan`)
|
||||
4. Click the logo (`SLP.Software`) to return to `#top`
|
||||
- **Expected Results**: Every click smoothly scrolls to the correct section every time; the page never snaps back to the top on a repeated/second click (this was the regression fixed via `handleAnchorClick`/`scrollToHash`); the URL hash is not required to change for scrolling to work
|
||||
- **Cleanup**: Stop the `pnpm run preview` process
|
||||
|
||||
### Scenario 2: Theme Toggle Integration
|
||||
- **Description**: Verify the theme toggle correctly switches and persists across the whole page (all sections read from the same theme context)
|
||||
- **Setup**: Same as Scenario 1
|
||||
- **Test Steps**: Toggle the theme control in the Nav; observe all sections (Hero, Packages, Process, About, Contact)
|
||||
- **Expected Results**: All sections switch theme consistently; no section is left in the previous theme
|
||||
- **Cleanup**: None
|
||||
|
||||
## Setup Integration Test Environment
|
||||
|
||||
### 1. Build and Serve the Production Bundle
|
||||
```bash
|
||||
pnpm run build
|
||||
pnpm run preview
|
||||
```
|
||||
|
||||
### 2. Configure Service Endpoints
|
||||
Not applicable — no external services or APIs are called by this unit.
|
||||
|
||||
## Run Integration Tests
|
||||
|
||||
### 1. Execute Manual Smoke Test Suite
|
||||
Follow Scenario 1 and Scenario 2 above in a browser against the `pnpm run preview` URL.
|
||||
|
||||
### 2. Verify Behaviors
|
||||
- **Test Scenarios**: Navigation/scroll repeatability (Scenario 1), theme consistency (Scenario 2)
|
||||
- **Expected Results**: See per-scenario expectations above
|
||||
- **Logs Location**: Browser DevTools console (should show no errors/warnings during navigation)
|
||||
|
||||
### 3. Cleanup
|
||||
Stop the preview server (`Ctrl+C` or terminate the process).
|
||||
|
||||
## Status
|
||||
- **Automated integration/contract tests**: N/A — single static unit, no service boundaries to test
|
||||
- **Manual smoke test**: Recommended before each deployment; the navigation-scroll regression (Scenario 1) was manually verified as fixed during this session
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
# Performance Test Instructions
|
||||
|
||||
## Purpose
|
||||
Validate that the static marketing site loads fast on a typical FTP/static web host, since page-load speed and SEO basics are explicit requirements (see `aidlc-docs/features/react-frontend/inception/requirements/`).
|
||||
|
||||
## Performance Requirements
|
||||
- **Initial Load (Largest Contentful Paint)**: < 2.5s on a simulated "Fast 3G"/typical mobile connection
|
||||
- **Bundle Size**: JS + CSS payload kept small (single-page static site, no heavy client-side data fetching)
|
||||
- **Concurrent Users**: Not applicable — static assets served by the host/CDN, no application server to load-test
|
||||
- **Error Rate**: 0% (no runtime errors in the browser console)
|
||||
|
||||
## Setup Performance Test Environment
|
||||
|
||||
### 1. Prepare Test Environment
|
||||
```bash
|
||||
pnpm run build
|
||||
pnpm run preview
|
||||
```
|
||||
Serves the actual production bundle (`dist/`) locally, which is representative of what a static host would serve.
|
||||
|
||||
### 2. Configure Test Parameters
|
||||
- **Tooling**: Browser DevTools "Lighthouse" panel (or `npx lighthouse <preview-url> --view`)
|
||||
- **Throttling**: Simulated mobile / Fast 3G, as offered by Lighthouse's default mobile preset
|
||||
|
||||
## Run Performance Tests
|
||||
|
||||
### 1. Execute Load Test (Lighthouse)
|
||||
```bash
|
||||
npx lighthouse http://localhost:4173 --view
|
||||
```
|
||||
(Adjust the port to whatever `pnpm run preview` prints.)
|
||||
|
||||
### 2. Execute Stress Tests
|
||||
Not applicable — there is no backend/API to stress-test; the site is purely static assets.
|
||||
|
||||
### 3. Analyze Performance Results
|
||||
- **Response Time / LCP**: Read from the Lighthouse report; target < 2.5s
|
||||
- **Bundle Size**: Inspect `dist/assets/*.js` and `*.css` sizes after `pnpm run build`
|
||||
- **Error Rate**: Confirm no console errors during the Lighthouse run
|
||||
- **Bottlenecks**: Typically font loading (`@fontsource/*`) or unused CSS; check the Lighthouse "Opportunities" section
|
||||
- **Results Location**: Lighthouse HTML report opened via `--view`, or exported JSON if needed
|
||||
|
||||
## Performance Optimization
|
||||
If performance doesn't meet requirements:
|
||||
1. Identify bottlenecks from the Lighthouse report (e.g. render-blocking fonts, unused Tailwind classes)
|
||||
2. Optimize (e.g. font-display swap, purge unused Tailwind utilities via the existing Tailwind config, code-split rarely used components)
|
||||
3. Rerun `pnpm run build` + Lighthouse to validate improvements
|
||||
|
||||
## Status
|
||||
- **Automated performance gate**: Not part of the current CI; this is a manual pre-deployment check
|
||||
- **Recommendation**: Run this check once before the first production deployment (Deployment Setup stage) and after any significant asset/dependency change
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
# Unit Test Execution
|
||||
|
||||
## Run Unit Tests
|
||||
|
||||
### 1. Execute All Unit Tests
|
||||
```bash
|
||||
pnpm run test
|
||||
```
|
||||
Runs Vitest (`vitest run`) with `jsdom` + `@testing-library/react` for component-level tests.
|
||||
|
||||
### 2. Review Test Results
|
||||
- **Expected**: 9 tests pass across 3 test files, 0 failures
|
||||
- **Test Coverage**: No formal coverage threshold configured; tests cover key rendering/interaction behavior of `Hero`, `Nav`, and the anchor-scroll utility
|
||||
- **Test Report Location**: Console output from `vitest run` (no HTML report is generated by default)
|
||||
|
||||
### 3. Static Analysis (complementary check)
|
||||
```bash
|
||||
pnpm run lint
|
||||
```
|
||||
- **Expected**: 0 errors. 2 non-blocking warnings are known and accepted (`react-refresh/only-export-components` in `src/routes/index.tsx` and `src/theme/ThemeProvider.tsx`) — cosmetic Fast Refresh warnings, not correctness issues.
|
||||
|
||||
### 4. Fix Failing Tests
|
||||
If tests fail:
|
||||
1. Review the Vitest console output for the failing test file/assertion
|
||||
2. Reproduce locally with `pnpm run test:watch` for fast iteration
|
||||
3. Fix the component/utility code or update the test if the expected behavior changed intentionally
|
||||
4. Rerun `pnpm run test` until all pass
|
||||
|
||||
## Verified Result (this session)
|
||||
- `pnpm run test`: 3 test files, 9 tests, all passed.
|
||||
- `pnpm run lint`: 0 errors, 2 known non-blocking warnings.
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
# Code Generation Plan — react-frontend-app
|
||||
|
||||
## Unit Context
|
||||
- **Unit**: `react-frontend-app` (single unit, greenfield)
|
||||
- **Workspace root**: `K:\Development\SlpSoftware\Projects\SlpSoftware` (from aidlc-state.md)
|
||||
- **Project structure pattern**: Greenfield single unit → `src/`, `tests/`, config files at workspace root
|
||||
- **Requirements source**: `aidlc-docs/features/react-frontend/inception/requirements/requirements.md`
|
||||
- **Design sources**: Functional Design, NFR Requirements, NFR Design artifacts under `aidlc-docs/features/react-frontend/construction/react-frontend-app/`
|
||||
- **Stories**: N/A (User Stories stage was skipped for this feature)
|
||||
- **Dependencies**: None (first and only unit)
|
||||
|
||||
## Steps
|
||||
|
||||
### Project Structure Setup
|
||||
- [x] Step 1: Scaffold Vite + React + TypeScript project files at workspace root (`package.json`, `vite.config.ts`, `tsconfig.json`, `tsconfig.node.json`, `index.html`, `.gitignore`)
|
||||
- [x] Step 2: Configure Tailwind CSS with red/purple theme variants (`tailwind.config.ts`, `postcss.config.js`, `src/index.css`) per NFR Requirements/Design decisions
|
||||
- [x] Step 3: Configure ESLint + Prettier (`.eslintrc.cjs` or `eslint.config.js`, `.prettierrc`)
|
||||
- [x] Step 4: Configure Vitest + React Testing Library (`vitest.config.ts` or Vite test config, `src/test/setup.ts`)
|
||||
|
||||
### Domain Data & Theme Foundation
|
||||
- [x] Step 5: Create typed content data module `src/data/content.ts` (NavLink, HeroContent, PackageCard, ProcessStep, AboutContent, ContactInfo) with the reference designs' Dutch copy ported 1-to-1 (FR-1, domain-entities.md)
|
||||
- [x] Step 6: Create theme tokens module `src/theme/tokens.ts` (red default + purple alternate, per domain-entities.md `ThemeTokens`)
|
||||
- [x] Step 7: Self-host fonts (Sora, Instrument Sans, JetBrains Mono) via `@fontsource` packages, imported in the app entry point (NFR Design font-loading decision)
|
||||
|
||||
### Application Shell (Router, Query, Theme, Error Handling)
|
||||
- [x] Step 8: Create `ThemeProvider` (`src/theme/ThemeProvider.tsx`) implementing BR-1/BR-2/BR-3 (default red, persistence, fallback on invalid stored value)
|
||||
- [x] Step 9: Create `ErrorBoundary` (`src/components/ErrorBoundary.tsx`) with on-brand fallback message (NFR Design resilience pattern)
|
||||
- [x] Step 10: Set up TanStack Router with hash history (`src/router.tsx`, `src/routes/__root.tsx`, `src/routes/index.tsx`) composing ErrorBoundary → ThemeProvider → QueryClientProvider → RootLayout → routed content
|
||||
- [x] Step 11: Set up TanStack Query (`src/queryClient.ts`) and the placeholder `usePackagesQuery` hook (`src/hooks/usePackagesQuery.ts`) with `staleTime: Infinity`
|
||||
- [x] Step 12: Wire the application entry point (`src/main.tsx`) to mount the router
|
||||
|
||||
### Frontend Components Generation
|
||||
- [x] Step 13: Create `RootLayout` (`src/components/RootLayout.tsx`)
|
||||
- [x] Step 14: Create `Nav` + `ThemeToggle` (`src/components/Nav.tsx`, `src/components/ThemeToggle.tsx`) with Dutch `aria-label`/`aria-pressed`
|
||||
- [x] Step 15: Create `Hero` (`src/components/Hero.tsx`) including the animated code line respecting `prefers-reduced-motion`
|
||||
- [x] Step 16: Create `PackagesSection` + `PackageCard` (`src/components/PackagesSection.tsx`, `src/components/PackageCard.tsx`) consuming `usePackagesQuery`
|
||||
- [x] Step 17: Create `ProcessSection` + `ProcessStep` (`src/components/ProcessSection.tsx`, `src/components/ProcessStep.tsx`)
|
||||
- [x] Step 18: Create `AboutSection` (`src/components/AboutSection.tsx`)
|
||||
- [x] Step 19: Create `ContactSection` (`src/components/ContactSection.tsx`)
|
||||
- [x] Step 20: Create `Footer` (`src/components/Footer.tsx`)
|
||||
- [x] Step 21: Assemble `src/routes/index.tsx` to render Hero, PackagesSection, ProcessSection, AboutSection, ContactSection in order
|
||||
|
||||
### Frontend Components Unit Testing
|
||||
- [x] Step 22: Write unit tests for theme logic (`src/theme/__tests__/ThemeProvider.test.tsx`) covering BR-1, BR-2, BR-3
|
||||
- [x] Step 23: Write component tests for `Nav`/`ThemeToggle` (`src/components/__tests__/Nav.test.tsx`)
|
||||
- [x] Step 24: Write component tests for `PackagesSection`/`PackageCard` (`src/components/__tests__/PackagesSection.test.tsx`)
|
||||
|
||||
### Documentation Generation
|
||||
- [x] Step 25: Create `README.md` at workspace root with setup/run/build/test instructions
|
||||
- [x] Step 26: Create code summary documentation at `aidlc-docs/features/react-frontend/construction/react-frontend-app/code/code-summary.md`
|
||||
|
||||
### Deployment Artifacts
|
||||
- [x] Step 27: Add `npm audit`-friendly `package-lock.json` (generated automatically on install) and confirm `.gitignore` excludes `node_modules`/`dist` — verified: `package-lock.json` present, `npm audit --omit=dev` reports 0 vulnerabilities
|
||||
- [x] Step 28: Verify `vite.config.ts` base path is compatible with hash-based routing on a static/FTP host (no special `base` needed since hash routing carries the route state client-side) — confirmed via successful `npm run build`
|
||||
|
||||
## Story Traceability
|
||||
No user stories exist for this feature (stage skipped). Traceability is instead to `requirements.md` (FR-1 through FR-5, NFR-1 through NFR-5) and to the approved Functional Design / NFR Requirements / NFR Design artifacts, referenced inline per step above.
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
# Functional Design Clarification Questions — react-frontend-app
|
||||
|
||||
Please answer each question by filling in the letter choice after the `[Answer]:` tag.
|
||||
|
||||
## Question 1: Theme toggle placement and style
|
||||
Where and how should the visible theme switcher (red/purple) be placed in the UI?
|
||||
|
||||
A) A small icon/swatch button in the nav bar (next to the "Start project" CTA), showing the current theme and toggling on click
|
||||
B) A labeled two-option switch (e.g. "Rood" / "Paars" segmented control) in the nav bar
|
||||
C) A floating control (e.g. fixed corner button) separate from the nav bar
|
||||
X) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]: A
|
||||
|
||||
## Question 2: Theme data shape
|
||||
How should the two theme palettes be modeled in code?
|
||||
|
||||
A) A single TypeScript object/record per theme (e.g. `{ red: {...tokens}, purple: {...tokens} }`) mapped to CSS custom properties at runtime via a `data-theme` attribute on `<html>`/`<body>`
|
||||
B) Two separate static CSS files (one per theme) that get swapped via a `<link>` tag toggle
|
||||
C) Tailwind config with two named theme variants selected via a class on the root element (e.g. `theme-red`, `theme-purple`)
|
||||
X) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]: C
|
||||
|
||||
## Question 3: Package/content data shape
|
||||
How should the static package/pricing/content data (nav links, hero copy, package cards, steps, etc.) be structured in code for this iteration?
|
||||
|
||||
A) One central static data module (e.g. `content.ts`) exporting typed constants/arrays consumed directly by components — easiest to later swap for a TanStack Query hook with the same shape
|
||||
B) Hardcoded directly inside each component (no separate data module)
|
||||
X) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]: A
|
||||
|
||||
## Question 4: TanStack Router route structure for this iteration
|
||||
Given there is effectively one page right now, how should the initial route structure look?
|
||||
|
||||
A) A root route (`__root.tsx`) rendering the shared layout (nav, theme provider, footer) with a single index route (`/`) rendering the full page content — ready to add sibling routes later
|
||||
B) A single flat route with no nested layout route yet (add the root/layout split only when a second route is introduced)
|
||||
X) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]: A
|
||||
|
||||
## Question 5: TanStack Query placeholder usage
|
||||
Since there's no backend yet, how should the "prepared for TanStack Query" requirement (FR-5) be reflected in this functional design?
|
||||
|
||||
A) Define one placeholder query hook (e.g. `usePackagesQuery`) that currently resolves the static data through `queryFn` (wrapped in a Promise) so the calling component already consumes it via `useQuery`, making a future real API swap a one-line change
|
||||
B) Only add the `QueryClientProvider` at the app root for now, without creating any actual query hook yet — components keep reading static data directly until a real API exists
|
||||
X) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]: A
|
||||
|
||||
## Question 6: Reduced-motion handling for the theme switch itself
|
||||
The reference design already respects `prefers-reduced-motion` for the caret blink. Should the theme-switch transition (background/color change) also respect this preference?
|
||||
|
||||
A) Yes — make the theme color transition instant (no fade) when `prefers-reduced-motion: reduce` is set, consistent with the existing caret handling
|
||||
B) No special handling needed for the theme transition — only the existing caret animation needs to respect it
|
||||
X) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]: A
|
||||
|
||||
## Question 7: Accessibility labeling for the theme switcher
|
||||
What accessible name/label should the new theme switcher control have?
|
||||
|
||||
A) `aria-label="Wissel kleurthema"` (Dutch, matching the rest of the site's language) with `aria-pressed`/`aria-checked` reflecting current state
|
||||
B) `aria-label="Toggle color theme"` (English)
|
||||
X) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]: A
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
# Functional Design Plan — react-frontend-app
|
||||
|
||||
## Unit Context
|
||||
- **Unit**: `react-frontend-app` (single unit — Units Generation was skipped per the approved execution plan; this is the only unit of work for this feature)
|
||||
- **Source**: `aidlc-docs/features/react-frontend/inception/requirements/requirements.md`
|
||||
|
||||
## Steps
|
||||
|
||||
- [x] Define the theme/color-token domain model (red default palette, purple alternate palette, shared token shape)
|
||||
- [x] Define the static content domain model (nav links, hero copy, package/pricing cards, process steps, about/tech-stack content, contact info) sourced 1-to-1 from the reference designs
|
||||
- [x] Define business rules for theme selection and persistence (default theme, switching behavior, persistence/fallback rules)
|
||||
- [x] Define the frontend component hierarchy (Nav, Hero, PackagesSection/PackageCard, ProcessSection/Step, AboutSection, ContactSection, Footer, ThemeProvider/ThemeToggle)
|
||||
- [x] Define props/state for each component and the user interaction flows (theme toggle click, anchor navigation, hover/focus states, mailto CTA)
|
||||
- [x] Define the TanStack Router route structure (root layout route + index route housing the whole page for this iteration)
|
||||
- [x] Define the TanStack Query integration point (provider placement + a placeholder query hook shape for future package/content data, backed by static data for now)
|
||||
- [x] Generate context-appropriate clarification questions and store in a dedicated question file
|
||||
- [x] Generate functional design artifacts: business-logic-model.md, business-rules.md, domain-entities.md, frontend-components.md
|
||||
- [x] Present completion message and await approval
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
# NFR Design Plan — react-frontend-app
|
||||
|
||||
## Steps
|
||||
|
||||
- [x] Define resilience pattern (error boundary fallback behavior)
|
||||
- [x] Define scalability pattern (justify minimal need given static single-page scope)
|
||||
- [x] Define performance pattern (TanStack Query caching config, font-loading strategy)
|
||||
- [x] Define security pattern (SRI application, CSP scope given deferred HTTP headers)
|
||||
- [x] Define logical components (theme store, content store, query client, router instance)
|
||||
- [x] Generate nfr-design-patterns.md and logical-components.md
|
||||
|
||||
## Category Applicability Justification
|
||||
|
||||
- **Resilience Patterns**: Applicable at a minimal level — a top-level React error boundary is the only meaningful resilience concern for a static, no-backend frontend (per SECURITY-15 from NFR Requirements).
|
||||
- **Scalability Patterns**: Not meaningfully applicable — this is a static single-page site with no server-side scaling concerns; the only "scaling" consideration is adding more routes/queries later, already covered by the Functional Design's extensible route/query structure.
|
||||
- **Performance Patterns**: Applicable — caching behavior of the placeholder TanStack Query hook and web font loading strategy are worth deciding now.
|
||||
- **Security Patterns**: Applicable at the scope already defined in NFR Requirements (SECURITY-10/13/15 addressed now; SECURITY-04/14 deferred) — this stage just needs to decide the concrete implementation pattern.
|
||||
- **Logical Components**: Applicable — need to name the concrete provider/store components (ThemeProvider, QueryClientProvider, Router instance) that implement the functional design.
|
||||
|
||||
## Clarification Questions
|
||||
|
||||
Please answer each question by filling in the letter choice after the `[Answer]:` tag.
|
||||
|
||||
### Question 1: Error boundary fallback behavior
|
||||
When an unexpected rendering error occurs, what should the top-level error boundary show?
|
||||
|
||||
A) A minimal, on-brand fallback message (styled with the current theme) with no technical details, e.g. "Er ging iets mis. Probeer de pagina te vernieuwen."
|
||||
B) A plain, unstyled fallback message (not necessarily following the current theme)
|
||||
X) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]: A
|
||||
|
||||
### Question 2: TanStack Query caching for the placeholder hook
|
||||
Since `usePackagesQuery` currently wraps static data (no real network call), how should its caching be configured?
|
||||
|
||||
A) Use TanStack Query defaults (`staleTime: 0`, refetch on window focus) — harmless for static data since the `queryFn` always returns the same static array anyway
|
||||
B) Set a long `staleTime` (e.g. `Infinity`) now, anticipating that once real data-fetching is wired in later, the content won't need to refetch constantly
|
||||
X) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]: A
|
||||
|
||||
### Question 3: Web font loading strategy
|
||||
The reference design loads Google Fonts (Sora, Instrument Sans, JetBrains Mono) via `<link>` tags. How should this be handled in the React app?
|
||||
|
||||
A) Keep loading via Google Fonts CDN `<link>` tags in `index.html`, adding SRI hashes where Google Fonts' CSS response allows it (per SECURITY-13 decision)
|
||||
B) Self-host the font files as static assets bundled with the app (avoids depending on an external CDN and SRI complications, at the cost of larger initial bundle/setup effort)
|
||||
X) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]: B
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
# NFR Requirements Plan — react-frontend-app
|
||||
|
||||
## Steps
|
||||
|
||||
- [x] Assess Security Baseline rule applicability for a static, no-backend React frontend
|
||||
- [x] Determine performance expectations (Lighthouse-style targets, bundle size sensitivity)
|
||||
- [x] Determine testing/tooling expectations (test runner, linting, formatting)
|
||||
- [x] Determine routing fallback strategy for the FTP/static hosting target (NFR-4 follow-up) — AI recommendation: hash-based routing (see nfr-requirements.md)
|
||||
- [x] Determine accessibility target level
|
||||
- [x] Determine CI expectations for this iteration
|
||||
- [x] Generate nfr-requirements.md and tech-stack-decisions.md
|
||||
|
||||
## Clarification Questions
|
||||
|
||||
Please answer each question by filling in the letter choice after the `[Answer]:` tag.
|
||||
|
||||
### Question 1: Performance target
|
||||
Is there a specific performance target for this static marketing site (e.g. Lighthouse score, load time)?
|
||||
|
||||
A) No hard numeric target — just keep the bundle reasonably small and avoid obvious performance mistakes (code-splitting not required yet for a single page)
|
||||
B) Target a Lighthouse Performance score of 90+ on the built production bundle
|
||||
C) No performance requirements at all for this iteration
|
||||
X) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]: A
|
||||
|
||||
### Question 2: Testing tooling
|
||||
Which testing setup should be used for this iteration (component/unit tests)?
|
||||
|
||||
A) Vitest + React Testing Library (natural fit with Vite)
|
||||
B) Jest + React Testing Library
|
||||
C) No automated tests for this iteration — only manual verification and a successful build
|
||||
X) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]: A
|
||||
|
||||
### Question 3: Linting / formatting
|
||||
Should linting and formatting be configured as part of this setup?
|
||||
|
||||
A) Yes — ESLint (with React/TypeScript rules) + Prettier, configured and passing on the initial codebase
|
||||
B) Only ESLint, no Prettier
|
||||
C) Not needed for this iteration
|
||||
X) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]: A
|
||||
|
||||
### Question 4: Routing fallback strategy for FTP hosting (NFR-4 follow-up)
|
||||
Since the target deployment is a plain FTP/static web host and TanStack Router is being set up now, which routing mode should be used to avoid future deep-link/404 problems on that kind of host?
|
||||
|
||||
A) Use TanStack Router's `hashHistory` (URLs like `/#/pakketten`) — works on any static host without server configuration, at the cost of slightly less clean URLs
|
||||
B) Use browser `history` mode now and defer the FTP rewrite-rule (`.htaccess`) problem to Deployment Setup in the Operations phase, since there's only one route today
|
||||
C) Not sure — let the AI recommend based on best practice for this hosting target
|
||||
X) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]: C
|
||||
|
||||
### Question 5: Accessibility target
|
||||
What accessibility (a11y) target should this frontend meet?
|
||||
|
||||
A) WCAG 2.1 AA as a target (semantic HTML, sufficient color contrast for both themes, keyboard-operable theme toggle, aria labels per functional design)
|
||||
B) No formal target — best-effort only, matching whatever the reference HTML already provides
|
||||
X) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]: B
|
||||
|
||||
### Question 6: CI for this iteration
|
||||
Should a CI pipeline (e.g. GitHub Actions) running build/lint/test be set up as part of this iteration?
|
||||
|
||||
A) Yes — set up a basic CI workflow that runs install, lint, test, and build on every push
|
||||
B) Not yet — CI can be addressed later, possibly during the Operations phase (Deployment Setup)
|
||||
X) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]: B
|
||||
|
||||
### Question: Security Baseline Rule Applicability
|
||||
Given this is a static, no-backend marketing frontend (per requirements.md), most backend-oriented Security Baseline rules (SECURITY-01, 02, 05, 06, 07, 08, 11, 12, 13, 14, 15) will be marked **N/A**. The following rules ARE potentially applicable and need a decision on scope:
|
||||
- SECURITY-04 (HTTP security headers) — depends on hosting; may need to be deferred to Deployment Setup if the FTP host doesn't support custom headers easily
|
||||
- SECURITY-09 (hardening/misconfiguration) — mainly "no sample/demo pages", generic error handling for a client-side app
|
||||
- SECURITY-10 (software supply chain) — dependency lock file + vulnerability scanning, applicable to any npm project
|
||||
- SECURITY-13 (integrity) — Subresource Integrity (SRI) for the Google Fonts CDN `<link>` tags
|
||||
|
||||
How should these be handled for this iteration?
|
||||
|
||||
A) Address SECURITY-10 (lock file + `npm audit`/dependency scanning) and SECURITY-13 (SRI where feasible) now during Code Generation; defer SECURITY-04 (HTTP headers) to Deployment Setup since it depends on the final hosting choice; mark SECURITY-09 items as addressed via a standard production build (no demo pages, generic error boundary)
|
||||
B) Defer ALL of these to the Operations phase (Deployment Setup / Production Readiness) — do not address any Security Baseline rule during Code Generation
|
||||
X) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]: A
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
# Code Summary — react-frontend-app
|
||||
|
||||
## Application Code (Workspace Root)
|
||||
|
||||
### Project Configuration
|
||||
- `package.json`, `vite.config.ts`, `tsconfig.json`, `tsconfig.node.json`, `index.html`, `.gitignore`
|
||||
- `tailwind.config.ts`, `postcss.config.js` — Tailwind theme mapped to CSS custom properties
|
||||
- `eslint.config.js` (ESLint 9 flat config), `.prettierrc`
|
||||
- `README.md` — setup/run/build/test instructions
|
||||
|
||||
### Domain Data & Theme
|
||||
- `src/data/content.ts` — typed static content (NavLink, HeroContent, PackageCardData, ProcessStepData, AboutContent, ContactInfo), copied 1-to-1 from the reference designs
|
||||
- `src/theme/tokens.ts` — theme value model (`red`/`purple`), storage key, validation/fallback (BR-1/BR-3)
|
||||
- `src/theme/ThemeProvider.tsx` — theme context, localStorage persistence (BR-2), applies `theme-red`/`theme-purple` class to `<html>`
|
||||
- `src/index.css` — theme token CSS variables (`.theme-red`, `.theme-purple`), global styles, caret animation, reduced-motion handling
|
||||
- `src/fonts.ts` — self-hosted font imports (`@fontsource/sora`, `@fontsource/instrument-sans`, `@fontsource/jetbrains-mono`)
|
||||
|
||||
### Application Shell
|
||||
- `src/queryClient.ts` — shared `QueryClient` (`staleTime: Infinity`)
|
||||
- `src/hooks/usePackagesQuery.ts` — placeholder query hook wrapping static package data
|
||||
- `src/components/ErrorBoundary.tsx` — top-level on-brand error boundary
|
||||
- `src/routes/__root.tsx` — root route composing ErrorBoundary → ThemeProvider → QueryClientProvider → RootLayout
|
||||
- `src/routes/index.tsx` — index route assembling the page sections
|
||||
- `src/router.tsx` — router instance with hash-based history
|
||||
- `src/main.tsx` — application entry point
|
||||
|
||||
### Components
|
||||
- `src/components/RootLayout.tsx`, `Nav.tsx`, `ThemeToggle.tsx`, `Hero.tsx`, `PackagesSection.tsx`, `PackageCard.tsx`, `ProcessSection.tsx`, `ProcessStep.tsx`, `AboutSection.tsx`, `ContactSection.tsx`, `Footer.tsx`
|
||||
|
||||
### Tests
|
||||
- `src/theme/__tests__/ThemeProvider.test.tsx` — 4 tests covering BR-1, BR-2, BR-3
|
||||
- `src/components/__tests__/Nav.test.tsx` — 3 tests (nav links, accessible toggle label, toggle interaction)
|
||||
- `src/components/__tests__/PackagesSection.test.tsx` — 2 tests (package rendering, featured badge)
|
||||
- `src/test/setup.ts` — Testing Library / jest-dom setup for Vitest
|
||||
|
||||
## Verification (Step 13.5)
|
||||
- **Build**: ✅ Success (`npm run build` — `tsc -b && vite build`)
|
||||
- **Unit Tests**: ✅ 9 passed, 0 failed (`npm run test` — Vitest)
|
||||
- **Lint**: ✅ 0 errors (2 non-blocking `react-refresh/only-export-components` warnings on files that intentionally export a hook alongside a component)
|
||||
|
||||
### Post-Review Fix: Deprecated Package Warnings
|
||||
After initial review, the user requested that the `npm install` deprecation warnings (`eslint@8.57.1`, `@humanwhocodes/config-array`, `@humanwhocodes/object-schema`, and related transitive packages) be resolved. Resolved by migrating from ESLint 8 (`.eslintrc.cjs`) to **ESLint 9 flat config** (`eslint.config.js`), using `typescript-eslint`, `@eslint/js`, `globals`, and `eslint-plugin-react-hooks` v5. Re-verified: build, tests (9/9), and lint (0 errors) all still pass after the migration, and `npm install` no longer reports deprecation warnings for these packages.
|
||||
|
||||
## Traceability
|
||||
- Requirements: FR-1 (content parity), FR-2 (componentization), FR-3 (theme switching), FR-4 (preserved micro-interactions), FR-5 (TanStack Router/Query scaffolding), NFR-1 through NFR-5
|
||||
- Functional Design business rules BR-1 through BR-6 implemented as described above
|
||||
- NFR Design patterns (error boundary, query caching, self-hosted fonts, hash routing) implemented as described above
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
# Business Logic Model — react-frontend-app
|
||||
|
||||
## Overview
|
||||
This unit's business logic is small and UI-centric: rendering static marketing content and managing the theme (red default / purple alternate) selection and persistence. There is no backend business logic in this iteration.
|
||||
|
||||
## Process Flow: Page Load and Theme Resolution
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
start_load["Visitor loads the site"]
|
||||
read_storage["Read stored theme preference from localStorage"]
|
||||
check_stored["Stored preference found?"]
|
||||
use_stored["Use stored theme (red or purple)"]
|
||||
use_default["Use default theme: red"]
|
||||
apply_theme["Apply theme class/attribute to document root"]
|
||||
load_content["Load static content module (nav, hero, packages, steps, about, contact)"]
|
||||
init_query["Initialize QueryClientProvider and packages placeholder query"]
|
||||
render_page["Render page sections via TanStack Router index route"]
|
||||
|
||||
start_load --> read_storage
|
||||
read_storage --> check_stored
|
||||
check_stored -->|"Yes"| use_stored
|
||||
check_stored -->|"No"| use_default
|
||||
use_stored --> apply_theme
|
||||
use_default --> apply_theme
|
||||
apply_theme --> load_content
|
||||
load_content --> init_query
|
||||
init_query --> render_page
|
||||
|
||||
classDef process fill:#9ae6b4,stroke:#2f855a,stroke-width:1px,color:#000;
|
||||
classDef decision fill:#fbd38d,stroke:#92400e,stroke-width:1px,color:#000;
|
||||
classDef terminal fill:#63b3ed,stroke:#2b6cb0,stroke-width:1px,color:#000;
|
||||
|
||||
class start_load,render_page terminal;
|
||||
class read_storage,use_stored,use_default,apply_theme,load_content,init_query process;
|
||||
class check_stored decision;
|
||||
```
|
||||
|
||||
Text alternative: On load, the app reads a stored theme preference; if found it is used, otherwise red is used as default; the theme is applied to the document, static content is loaded, the query provider is initialized, then the page renders.
|
||||
|
||||
## Process Flow: Theme Switch Interaction
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
click_toggle["Visitor clicks the theme toggle button in the nav"]
|
||||
determine_next["Determine next theme (red to purple, or purple to red)"]
|
||||
update_state["Update in-memory theme state (ThemeProvider context)"]
|
||||
persist_storage["Persist chosen theme to localStorage"]
|
||||
reapply_theme["Re-apply theme class/attribute to document root"]
|
||||
update_toggle["Update toggle button visual state and aria-pressed"]
|
||||
|
||||
click_toggle --> determine_next
|
||||
determine_next --> update_state
|
||||
update_state --> persist_storage
|
||||
persist_storage --> reapply_theme
|
||||
reapply_theme --> update_toggle
|
||||
|
||||
classDef process fill:#9ae6b4,stroke:#2f855a,stroke-width:1px,color:#000;
|
||||
classDef terminal fill:#63b3ed,stroke:#2b6cb0,stroke-width:1px,color:#000;
|
||||
|
||||
class click_toggle terminal;
|
||||
class determine_next,update_state,persist_storage,reapply_theme,update_toggle process;
|
||||
```
|
||||
|
||||
Text alternative: Clicking the toggle determines the other theme, updates in-memory state, persists it to localStorage, reapplies the theme to the document, and updates the toggle button's visual/accessible state.
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
# Business Rules — react-frontend-app
|
||||
|
||||
## BR-1: Default Theme Rule
|
||||
The site MUST use the **red** theme when no stored theme preference exists (first visit, cleared storage, or unsupported storage).
|
||||
|
||||
## BR-2: Theme Persistence Rule
|
||||
Whenever the visitor switches themes, the chosen theme MUST be written to `localStorage` immediately, so a page reload or new visit resolves to the same theme (BR-1 only applies when nothing is stored).
|
||||
|
||||
## BR-3: Valid Theme Values Rule
|
||||
Only two theme values are valid: `red` and `purple`. If a stored value is anything else (corrupted/unexpected), the app MUST fall back to the default theme (`red`) rather than error.
|
||||
|
||||
## BR-4: Content Fidelity Rule
|
||||
All rendered marketing copy, prices (€300 / €750 / "Op maat"), and the contact e-mail (`info@slpsoftware.nl`) MUST match the reference HTML designs exactly for this iteration (per requirements FR-1); no content may be altered, abbreviated, or replaced with placeholder text.
|
||||
|
||||
## BR-5: Reduced Motion Rule
|
||||
When the visitor's OS/browser signals `prefers-reduced-motion: reduce`, both the hero caret blink animation AND the theme-switch color transition MUST be instant / non-animated.
|
||||
|
||||
## BR-6: Single Route Rule (current iteration)
|
||||
For this iteration, all page sections (nav, hero, packages, process, about, contact, footer) are rendered under a single index route (`/`). No section requires its own route yet.
|
||||
|
||||
## Decision Flow: Theme Resolution on Load
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
load_pref{"Stored theme value exists?"}
|
||||
valid_check{"Stored value is 'red' or 'purple'?"}
|
||||
use_stored_value["Use stored value as active theme"]
|
||||
fallback_default["Fall back to default theme: red"]
|
||||
|
||||
load_pref -->|"No"| fallback_default
|
||||
load_pref -->|"Yes"| valid_check
|
||||
valid_check -->|"Yes"| use_stored_value
|
||||
valid_check -->|"No (corrupted/unexpected)"| fallback_default
|
||||
|
||||
classDef decision fill:#fbd38d,stroke:#92400e,stroke-width:1px,color:#000;
|
||||
classDef outcome fill:#9ae6b4,stroke:#2f855a,stroke-width:1px,color:#000;
|
||||
|
||||
class load_pref,valid_check decision;
|
||||
class use_stored_value,fallback_default outcome;
|
||||
```
|
||||
|
||||
Text alternative: If no stored theme exists, or the stored value is not "red"/"purple", the app falls back to red; otherwise the valid stored value is used.
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
# Domain Entities — react-frontend-app
|
||||
|
||||
## Overview
|
||||
This unit has no persisted backend entities. The "domain" here is the static content model and the theme model that drive rendering.
|
||||
|
||||
## Entity Relationships
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
theme_pref["ThemePreference"]
|
||||
theme_tokens["ThemeTokens"]
|
||||
site_content["SiteContent"]
|
||||
nav_link["NavLink"]
|
||||
hero_content["HeroContent"]
|
||||
package_card["PackageCard"]
|
||||
process_step["ProcessStep"]
|
||||
about_content["AboutContent"]
|
||||
contact_info["ContactInfo"]
|
||||
|
||||
theme_pref -->|"selects"| theme_tokens
|
||||
site_content -->|"has many"| nav_link
|
||||
site_content -->|"has one"| hero_content
|
||||
site_content -->|"has many"| package_card
|
||||
site_content -->|"has many"| process_step
|
||||
site_content -->|"has one"| about_content
|
||||
site_content -->|"has one"| contact_info
|
||||
|
||||
classDef entity fill:#2196F3,stroke:#0d47a1,stroke-width:1px,color:#000;
|
||||
classDef value fill:#FF9800,stroke:#e65100,stroke-width:1px,color:#000;
|
||||
|
||||
class theme_pref,site_content entity;
|
||||
class theme_tokens,nav_link,hero_content,package_card,process_step,about_content,contact_info value;
|
||||
```
|
||||
|
||||
Text alternative: A ThemePreference selects a set of ThemeTokens; SiteContent aggregates NavLinks, one HeroContent, many PackageCards, many ProcessSteps, one AboutContent, and one ContactInfo (blue = stateful entity, orange = static value objects).
|
||||
|
||||
## Entity Definitions
|
||||
|
||||
### ThemePreference
|
||||
| Field | Type | Required | Description |
|
||||
|---|---|---|---|
|
||||
| `value` | `'red' \| 'purple'` | Yes | Currently active theme; defaults to `'red'` per BR-1 |
|
||||
| `source` | `'stored' \| 'default'` | Yes | Whether the value came from `localStorage` or the default fallback |
|
||||
|
||||
### ThemeTokens
|
||||
| Field | Type | Required | Description |
|
||||
|---|---|---|---|
|
||||
| `bg`, `surface`, `surfaceAlt`, `line`, `text`, `muted` | `string` (hex) | Yes | Neutral palette tokens, taken 1-to-1 from the reference CSS variables |
|
||||
| `accent`, `accentSoft`, `accentLine` | `string` (hex/rgba) | Yes | Accent palette tokens (red or purple variant), taken 1-to-1 from the reference CSS variables |
|
||||
|
||||
### NavLink
|
||||
| Field | Type | Required | Description |
|
||||
|---|---|---|---|
|
||||
| `label` | `string` | Yes | Link text (e.g. "Pakketten") |
|
||||
| `href` | `string` | Yes | Anchor target (e.g. "#pakketten") |
|
||||
| `isCta` | `boolean` | No | Marks the "Start project" call-to-action link |
|
||||
|
||||
### HeroContent
|
||||
| Field | Type | Required | Description |
|
||||
|---|---|---|---|
|
||||
| `eyebrow` | `string` | Yes | Small label above the heading |
|
||||
| `heading` | `string` | Yes | Main H1 text |
|
||||
| `lead` | `string` | Yes | Lead paragraph text |
|
||||
| `codeLine` | `string` | Yes | The animated code-line snippet text |
|
||||
|
||||
### PackageCard
|
||||
| Field | Type | Required | Description |
|
||||
|---|---|---|---|
|
||||
| `id` | `string` | Yes | e.g. `pakket_01` |
|
||||
| `title` | `string` | Yes | e.g. "Landingspagina" |
|
||||
| `description` | `string` | Yes | Short description |
|
||||
| `price` | `string` | Yes | e.g. "€ 300" or "Op maat" |
|
||||
| `priceNote` | `string` | Yes | e.g. "eenmalig, excl. btw" |
|
||||
| `features` | `string[]` | Yes | Bullet list of included features |
|
||||
| `ctaLabel` | `string` | Yes | Button text |
|
||||
| `featured` | `boolean` | No | Marks the "Meest gekozen" (most chosen) card |
|
||||
|
||||
### ProcessStep
|
||||
| Field | Type | Required | Description |
|
||||
|---|---|---|---|
|
||||
| `label` | `string` | Yes | e.g. "stap 01 — intake" |
|
||||
| `title` | `string` | Yes | e.g. "Kennismaken" |
|
||||
| `description` | `string` | Yes | Step description |
|
||||
|
||||
### AboutContent
|
||||
| Field | Type | Required | Description |
|
||||
|---|---|---|---|
|
||||
| `paragraphs` | `string[]` | Yes | About-section body paragraphs |
|
||||
| `techStack` | `{ label: string; value: string }[]` | Yes | Tech-stack list items (Front-end, Back-end, API's, Focus) |
|
||||
|
||||
### ContactInfo
|
||||
| Field | Type | Required | Description |
|
||||
|---|---|---|---|
|
||||
| `heading` | `string` | Yes | Contact section heading |
|
||||
| `description` | `string` | Yes | Contact section body text |
|
||||
| `email` | `string` | Yes | `info@slpsoftware.nl` |
|
||||
| `mailSubject` | `string` | Yes | Prefilled mailto subject |
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
# Frontend Components — react-frontend-app
|
||||
|
||||
## Component Hierarchy
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
root_route["__root.tsx\n(Root Route: ThemeProvider + QueryClientProvider)"]
|
||||
layout["RootLayout\n(Nav + Footer wrapper)"]
|
||||
index_route["index.tsx\n(/ Index Route)"]
|
||||
nav["Nav"]
|
||||
theme_toggle["ThemeToggle"]
|
||||
hero["Hero"]
|
||||
packages_section["PackagesSection"]
|
||||
package_card["PackageCard (x3)"]
|
||||
process_section["ProcessSection"]
|
||||
process_step["ProcessStep (x3)"]
|
||||
about_section["AboutSection"]
|
||||
contact_section["ContactSection"]
|
||||
footer["Footer"]
|
||||
|
||||
root_route --> layout
|
||||
layout --> nav
|
||||
nav --> theme_toggle
|
||||
layout --> index_route
|
||||
index_route --> hero
|
||||
index_route --> packages_section
|
||||
packages_section --> package_card
|
||||
index_route --> process_section
|
||||
process_section --> process_step
|
||||
index_route --> about_section
|
||||
index_route --> contact_section
|
||||
layout --> footer
|
||||
|
||||
classDef root_node fill:#4CAF50,stroke:#2e7d32,stroke-width:2px,color:#000;
|
||||
classDef layout_node fill:#2196F3,stroke:#0d47a1,stroke-width:1px,color:#000;
|
||||
classDef page_node fill:#2196F3,stroke:#0d47a1,stroke-width:1px,color:#000;
|
||||
classDef guard_node fill:#FF9800,stroke:#e65100,stroke-width:1px,color:#000;
|
||||
|
||||
class root_route root_node;
|
||||
class layout,index_route layout_node;
|
||||
class nav,hero,packages_section,package_card,process_section,process_step,about_section,contact_section,footer page_node;
|
||||
class theme_toggle guard_node;
|
||||
```
|
||||
|
||||
Text alternative: The root route provides ThemeProvider and QueryClientProvider and renders a RootLayout (Nav with ThemeToggle, plus Footer) wrapping the index route, which renders Hero, PackagesSection (three PackageCard instances), ProcessSection (three ProcessStep instances), AboutSection, and ContactSection.
|
||||
|
||||
## Components: Props and State
|
||||
|
||||
| Component | Props | State | Notes |
|
||||
|---|---|---|---|
|
||||
| `RootRoute` (`__root.tsx`) | — | — | Hosts `ThemeProvider` and `QueryClientProvider`; renders `<Outlet />` |
|
||||
| `ThemeProvider` | `children: ReactNode` | `theme: 'red' \| 'purple'` (from context) | Reads/writes `localStorage`; exposes `theme` and `toggleTheme()` via context; applies `theme-red`/`theme-purple` class to `<html>` |
|
||||
| `RootLayout` | `children: ReactNode` | — | Renders `Nav`, `children` (routed content), `Footer` |
|
||||
| `Nav` | `links: NavLink[]` | — | Renders logo, `nav-links`, `ThemeToggle`, CTA link |
|
||||
| `ThemeToggle` | — | — | Reads `theme`/`toggleTheme` from `ThemeProvider` context; `aria-label="Wissel kleurthema"`, `aria-pressed` reflects whether purple is active |
|
||||
| `Hero` | `content: HeroContent` | — | Renders eyebrow, heading, lead, animated code line (caret respects `prefers-reduced-motion`), two CTA buttons |
|
||||
| `PackagesSection` | `packages: PackageCard[]` | — | Renders section head + grid of `PackageCard` |
|
||||
| `PackageCard` | `pkg: PackageCard` | — | Renders one pricing card; `featured` prop styling for "Meest gekozen" |
|
||||
| `ProcessSection` | `steps: ProcessStep[]` | — | Renders section head + grid of `ProcessStep` |
|
||||
| `ProcessStep` | `step: ProcessStep` | — | Renders one process step (label, title, description) |
|
||||
| `AboutSection` | `content: AboutContent` | — | Renders paragraphs + tech-stack panel |
|
||||
| `ContactSection` | `content: ContactInfo` | — | Renders contact box with mailto CTA |
|
||||
| `Footer` | — | — | Renders copyright + mono tagline |
|
||||
|
||||
## User Interaction Flows
|
||||
|
||||
- **Theme toggle click**: `ThemeToggle` → calls `toggleTheme()` from `ThemeProvider` context → context updates `theme` state → writes new value to `localStorage` (BR-2) → root element's theme class is updated → all themed elements re-render with new token values → transition is instant if `prefers-reduced-motion: reduce` (BR-5), otherwise a short color transition plays.
|
||||
- **In-page anchor navigation**: Clicking a `Nav` link or hero CTA scrolls smoothly to the target section (`scroll-behavior: smooth`), respecting `prefers-reduced-motion` (falls back to instant jump).
|
||||
- **Hover / focus-visible states**: Preserved 1-to-1 from the reference design on nav links, buttons, and cards (border/color changes on `:hover`/`:focus-visible`).
|
||||
- **Mailto CTA**: Clicking the contact CTA or the inline mail link opens the visitor's mail client via a `mailto:` link with a prefilled subject.
|
||||
|
||||
## Form Validation Rules
|
||||
None — this iteration has no forms; the only interactive control is the theme toggle and standard anchor/mailto links.
|
||||
|
||||
## API Integration Points (Forward-Looking)
|
||||
- `usePackagesQuery` (TanStack Query hook, placeholder): `queryFn` currently resolves the static `PackageCard[]` data from the content module wrapped in `Promise.resolve(...)`, consumed via `useQuery` in `PackagesSection`. This keeps the component's data-access pattern identical to what it will be once a real backend endpoint exists — only the `queryFn` implementation will need to change in a future iteration (per FR-5).
|
||||
- No other components call `useQuery` in this iteration; `Nav`, `Hero`, `ProcessSection`, `AboutSection`, and `ContactSection` read directly from the static content module for now.
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
# Logical Components — react-frontend-app
|
||||
|
||||
## Component/Provider Overview
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
router["Router Instance\n(TanStack Router, hash history)"]
|
||||
query_client["QueryClientProvider\n(staleTime: Infinity for placeholder queries)"]
|
||||
theme_provider["ThemeProvider\n(theme state + localStorage persistence)"]
|
||||
error_boundary["ErrorBoundary\n(on-brand fallback UI)"]
|
||||
root_route["Root Route (__root.tsx)"]
|
||||
fonts["Self-hosted Fonts\n(bundled static assets)"]
|
||||
|
||||
router --> root_route
|
||||
root_route --> error_boundary
|
||||
error_boundary --> theme_provider
|
||||
theme_provider --> query_client
|
||||
root_route -.->|"loads"| fonts
|
||||
|
||||
classDef infra fill:#9C27B0,stroke:#4a148c,stroke-width:1px,color:#000;
|
||||
classDef guard fill:#FF9800,stroke:#e65100,stroke-width:1px,color:#000;
|
||||
classDef layout fill:#2196F3,stroke:#0d47a1,stroke-width:1px,color:#000;
|
||||
classDef asset fill:#4CAF50,stroke:#2e7d32,stroke-width:1px,color:#000;
|
||||
|
||||
class router,query_client infra;
|
||||
class error_boundary guard;
|
||||
class theme_provider,root_route layout;
|
||||
class fonts asset;
|
||||
```
|
||||
|
||||
Text alternative: The router hosts the root route, which is wrapped by an ErrorBoundary, which wraps ThemeProvider, which wraps QueryClientProvider; the root route also loads self-hosted font assets (purple = infra provider, orange = guard/boundary, blue = layout/route, green = static asset).
|
||||
|
||||
## Logical Component Definitions
|
||||
|
||||
| Component | Type | Responsibility |
|
||||
|---|---|---|
|
||||
| **Router Instance** | Infrastructure | Created via TanStack Router with `createHashHistory()`; defines the root route and index route tree. |
|
||||
| **QueryClientProvider** | Infrastructure | Wraps the app with a single shared `QueryClient`; configures default `staleTime: Infinity` for this iteration's placeholder queries. |
|
||||
| **ThemeProvider** | Layout/Context | Owns `theme` state (`'red' \| 'purple'`), reads/writes `localStorage`, applies the active theme class to the document root; exposes `theme` + `toggleTheme()` via React context (implements BR-1, BR-2, BR-3). |
|
||||
| **ErrorBoundary** | Guard | Top-level React error boundary; renders the on-brand fallback UI on unexpected render errors (implements the Resilience Pattern). |
|
||||
| **Root Route (`__root.tsx`)** | Layout/Route | Composes `ErrorBoundary` → `ThemeProvider` → `QueryClientProvider` → `RootLayout` (Nav/Footer) → routed content (`<Outlet />`). |
|
||||
| **Self-hosted Fonts** | Static Asset | Sora, Instrument Sans, and JetBrains Mono font files bundled with the app and declared via local `@font-face`/`@fontsource` imports — no external CDN dependency. |
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
# NFR Design Patterns — react-frontend-app
|
||||
|
||||
## Resilience Pattern: Top-Level Error Boundary
|
||||
A single React error boundary wraps the routed content inside the root route. On an unexpected rendering error it shows a minimal, on-brand fallback message styled with the currently active theme (e.g. "Er ging iets mis. Probeer de pagina te vernieuwen."), with no stack traces or technical details exposed (satisfies SECURITY-09 and SECURITY-15 from NFR Requirements).
|
||||
|
||||
## Scalability Pattern: Not Applicable (Justified)
|
||||
This is a static single-page marketing site with no server-side component to scale. The only forward-looking "scalability" concern — adding more routes and swapping the placeholder query for a real API — is already accommodated structurally by the Functional Design's root/index route split and the `usePackagesQuery` hook shape, so no additional scalability pattern is introduced at this stage.
|
||||
|
||||
## Performance Patterns
|
||||
|
||||
### Query Caching
|
||||
The `usePackagesQuery` placeholder hook is configured with `staleTime: Infinity` (and no automatic refetch-on-window-focus), since its `queryFn` currently always returns the same static array. This is a deliberate choice anticipating the future real-data swap, where refetch behavior can be tuned once real network latency/staleness exists.
|
||||
|
||||
### Font Loading
|
||||
Fonts (Sora, Instrument Sans, JetBrains Mono) are **self-hosted** as static assets bundled with the app (via `@fontsource/*` packages or locally vendored font files + `@font-face` declarations), rather than loaded from the Google Fonts CDN.
|
||||
- **Impact on Security Baseline SECURITY-13 (integrity)**: Self-hosting removes the need for Subresource Integrity (SRI) hashes on font `<link>` tags entirely, since no external CDN resource is loaded for fonts anymore. The NFR Requirements SECURITY-13 note ("SRI where feasible") is superseded by this decision — self-hosting is a stronger mitigation (no external dependency at all) than SRI on a CDN resource.
|
||||
- **Trade-off accepted**: Slightly larger initial bundle/setup effort, in exchange for one fewer external dependency and a fully offline-buildable app.
|
||||
|
||||
## Security Patterns
|
||||
- **Dependency/supply chain (SECURITY-10)**: `package-lock.json` committed; `npm audit` step documented in build instructions (implemented in Build and Test stage).
|
||||
- **Integrity (SECURITY-13)**: Satisfied via the font self-hosting decision above (no external CDN assets requiring SRI remain in this iteration).
|
||||
- **Hardening (SECURITY-09) & fail-safe defaults (SECURITY-15)**: Satisfied via the Resilience Pattern (error boundary) above and a standard production build with no demo/sample routes.
|
||||
- **HTTP security headers (SECURITY-04)**: Remains deferred to Deployment Setup (Operations phase), unchanged from NFR Requirements — no hosting decision has been finalized yet.
|
||||
|
||||
## Logical Components
|
||||
See `logical-components.md` for the concrete component/provider list implementing these patterns.
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
# NFR Requirements — react-frontend-app
|
||||
|
||||
## Performance
|
||||
- **Target**: No hard numeric target. Keep the production bundle reasonably small; code-splitting is not required for this single-page iteration but the setup should not preclude it later (Vite handles this automatically as routes/queries grow).
|
||||
|
||||
## Testing
|
||||
- **Test runner**: Vitest (pairs naturally with Vite)
|
||||
- **Component testing**: React Testing Library
|
||||
- **Scope for this iteration**: Component rendering tests for key components (`Nav`, `ThemeToggle`, `PackagesSection`, `PackageCard`) and a unit test for the theme resolution/persistence logic (BR-1, BR-2, BR-3).
|
||||
|
||||
## Linting & Formatting
|
||||
- **ESLint** configured with React + TypeScript rules (e.g. `typescript-eslint`, `eslint-plugin-react-hooks`)
|
||||
- **Prettier** for consistent formatting
|
||||
- Both must pass cleanly on the initial generated codebase.
|
||||
|
||||
## Routing Strategy for Static/FTP Hosting (AI Recommendation)
|
||||
The user deferred this decision to the AI (Question 4 = "Not sure — let the AI recommend").
|
||||
|
||||
**Decision: Use TanStack Router's hash-based history (`createHashHistory`) for this iteration.**
|
||||
|
||||
**Rationale**:
|
||||
- The confirmed deployment target (requirements.md NFR-4) is a traditional web host via FTP/manual upload, where server-side rewrite rules (`.htaccess` or equivalent) are not guaranteed to be configurable or reliably supported.
|
||||
- Hash-based routing (`/#/route`) works correctly on any static file host with zero server configuration, because the part after `#` is never sent to the server — the server only ever needs to serve `index.html`.
|
||||
- The trade-off (slightly less clean URLs, e.g. `example.com/#/pakketten` instead of `example.com/pakketten`) is acceptable for a small marketing site and avoids a class of "404 on refresh/direct link" bugs that browser `history` mode would introduce on this hosting target.
|
||||
- If hosting later moves to a platform with reliable rewrite support (e.g. Netlify/Vercel per requirements.md's alternative hosting note), this can be revisited and switched to `createBrowserHistory` — this is a router configuration change only, not a structural one, since TanStack Router's history mode is set in one place at the router's creation.
|
||||
|
||||
## Accessibility
|
||||
- **Target**: Best-effort only, matching whatever the reference HTML already provides (no formal WCAG level mandated for this iteration). Note: the functional design already includes concrete accessibility details (Dutch `aria-label` and `aria-pressed` on the theme toggle) that will still be implemented, since they were explicit functional design decisions — this NFR decision only means no additional formal accessibility audit/target is required beyond that.
|
||||
|
||||
## CI/CD
|
||||
- **This iteration**: No CI pipeline is set up yet. Deferred to the Operations phase (Deployment Setup), which will define the concrete build/deploy process for the FTP target.
|
||||
|
||||
## Security Baseline — Rule-by-Rule Applicability
|
||||
|
||||
| Rule | Applicability | Decision / Rationale |
|
||||
|---|---|---|
|
||||
| SECURITY-01 (encryption at rest/in transit) | N/A | No data store exists in this static frontend. |
|
||||
| SECURITY-02 (access logging on intermediaries) | N/A | No load balancer/API gateway/CDN configured by this unit; would apply at hosting level if applicable, out of scope here. |
|
||||
| SECURITY-03 (application-level logging) | N/A | No server-side application component; a client-side app has no backend logs to configure. |
|
||||
| SECURITY-04 (HTTP security headers) | Deferred | Depends on the final hosting choice and whether the host supports custom headers — deferred to Deployment Setup (Operations phase). |
|
||||
| SECURITY-05 (input validation on API params) | N/A | No API endpoints exist in this unit. |
|
||||
| SECURITY-06 (least-privilege access policies) | N/A | No IAM/cloud roles involved. |
|
||||
| SECURITY-07 (restrictive network configuration) | N/A | No network/firewall resources involved. |
|
||||
| SECURITY-08 (application-level access control) | N/A | No authenticated resources or user accounts exist. |
|
||||
| SECURITY-09 (hardening/misconfiguration) | Addressed now | Production build via Vite has no sample/demo pages; a top-level React error boundary will show a generic message instead of exposing stack traces. |
|
||||
| SECURITY-10 (software supply chain) | Addressed now | `package-lock.json` committed; `npm audit` documented as part of build instructions; no unused dependencies added. |
|
||||
| SECURITY-11 (secure design principles) | N/A | No security-critical logic (auth, payments) exists in this unit. |
|
||||
| SECURITY-12 (authentication/credential mgmt) | N/A | No authentication exists in this unit. |
|
||||
| SECURITY-13 (software/data integrity) | Addressed now (partial) | Subresource Integrity (SRI) hashes will be added to the Google Fonts `<link>` tags where the CDN provides stable, hashable assets; no other external CDN resources are used. |
|
||||
| SECURITY-14 (alerting and monitoring) | Deferred | No backend/log service exists yet; revisit if/when Monitoring Setup (Operations phase) introduces any client-side error/analytics reporting. |
|
||||
| SECURITY-15 (exception handling / fail-safe defaults) | Addressed now | A top-level React error boundary is added; the `usePackagesQuery` placeholder hook's promise-based `queryFn` will have explicit error handling wired through TanStack Query's error state. |
|
||||
|
||||
**Summary**: 10 of 15 Security Baseline rules are N/A for this static, no-backend frontend. 4 rules (SECURITY-09, SECURITY-10, SECURITY-13, SECURITY-15) are addressed during this iteration's Code Generation. 2 rules (SECURITY-04, SECURITY-14) are explicitly deferred to the Operations phase.
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
# Tech Stack Decisions — react-frontend-app
|
||||
|
||||
| Concern | Decision | Rationale |
|
||||
|---|---|---|
|
||||
| Build tool | Vite | Confirmed in requirements.md NFR-1; fast dev server, first-class TypeScript/React support, pairs naturally with Vitest. |
|
||||
| Language | TypeScript | Confirmed in requirements.md NFR-1; type safety for the domain entities defined in functional design. |
|
||||
| UI library | React 18+ | Confirmed in requirements.md NFR-1. |
|
||||
| Styling | Tailwind CSS | Confirmed in requirements.md NFR-2. Theme variants implemented as Tailwind theme classes (`theme-red` / `theme-purple`) per Functional Design Question 2 answer. |
|
||||
| Routing | TanStack Router, hash-based history (`createHashHistory`) | Confirmed in requirements.md FR-5; hash history chosen per this stage's routing-strategy decision (NFR Requirements Question 4) to be safe on a plain FTP static host without server rewrite rules. |
|
||||
| Data fetching (forward-looking) | TanStack Query (`@tanstack/react-query`) | Confirmed in requirements.md FR-5; a `QueryClientProvider` is set up now, with one placeholder query hook (`usePackagesQuery`) as defined in Functional Design. |
|
||||
| Testing | Vitest + React Testing Library | NFR Requirements Question 2 answer A. |
|
||||
| Linting | ESLint (`typescript-eslint`, `eslint-plugin-react-hooks`) + Prettier | NFR Requirements Question 3 answer A. |
|
||||
| Package manager | npm (with committed `package-lock.json`) | Default choice for a Vite-scaffolded project; supports SECURITY-10 (lock file requirement). |
|
||||
| CI | None for this iteration | NFR Requirements Question 6 answer B — deferred to Operations phase. |
|
||||
| Accessibility | Best-effort (no formal WCAG target this iteration) | NFR Requirements Question 5 answer B; explicit a11y attributes from Functional Design are still implemented. |
|
||||
Reference in New Issue
Block a user