Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .docker/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
worker_processes auto;

error_log /dev/stderr notice;
pid /tmp/nginx.pid;

events {
worker_connections 1024;
}

http {
proxy_temp_path /tmp/proxy_temp;
client_body_temp_path /tmp/client_temp;
fastcgi_temp_path /tmp/fastcgi_temp;
uwsgi_temp_path /tmp/uwsgi_temp;
scgi_temp_path /tmp/scgi_temp;

include /etc/nginx/mime.types;
default_type application/octet-stream;

log_format main '$http_x_real_ip - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /dev/stdout main;

sendfile on;
keepalive_timeout 65;

gzip on;

include /etc/nginx/conf.d/*.conf;
}
24 changes: 24 additions & 0 deletions .docker/templates/default.conf.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
server {
listen ${NGINX_PORT};
server_name localhost;

set_real_ip_from 172.16.0.0/16;
set_real_ip_from 192.168.39.0/24;
real_ip_recursive on;
real_ip_header X-Forwarded-For;

location / {
proxy_read_timeout 120;
proxy_pass http://${NGINX_PROXY_SERVICE};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}

# Send log message to files symlinked to stdout/stderr.
error_log /dev/stderr;
access_log /dev/stdout main;
}
2 changes: 2 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
COMPOSE_PROJECT_NAME=research-api
COMPOSE_DOMAIN=research.local.itkdev.dk
11 changes: 6 additions & 5 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,21 @@ jobs:
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
- uses: actions/setup-node@v4
with:
python-version: "3.12"
node-version: "24"
cache: "npm"

- name: Install dependencies
run: pip install -r requirements.txt
run: npm ci

- name: Build site
run: mkdocs build --strict
run: npx vitepress build docs

- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: site
path: _site

deploy:
needs: build
Expand Down
25 changes: 25 additions & 0 deletions .github/workflows/verify_build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Verify build

on:
pull_request:
paths:
- "docs/**"
- "package.json"
- "package-lock.json"

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: "24"
cache: "npm"

- name: Install dependencies
run: npm ci

- name: Build site
run: npx vitepress build docs
15 changes: 6 additions & 9 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
# MkDocs build output
site/
# VitePress build output
_site/
docs/.vitepress/cache/
docs/.vitepress/dist/

# Python
__pycache__/
*.py[cod]
*.egg-info/
.venv/
venv/
env/
# Node
node_modules/

# OS
.DS_Store
Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [Unreleased]

### Changed
- Migrated from MkDocs + Material theme to VitePress
- Adopted ITK Dev brand colors (teal/cyan) replacing neutral black/grey palette
- Restructured project to follow ITK Dev documentation site conventions
- Added Docker dev environment with Nginx + Traefik (docker-compose.yml)
- Added Taskfile.yml for dev/build/lint automation
- Added client-side password gate component for protecting individual projects
- Added VitePress home layout with hero and project feature cards
- Moved interactive mock HTML files to docs/public/ (served as raw static assets)
- Replaced MkDocs admonitions (!!! info/warning) with VitePress custom containers (::: info/warning)
- Replaced MkDocs Material button syntax with HTML mock-button links
- Updated GitHub Actions to use Node.js + VitePress instead of Python + MkDocs
- Added PR build verification workflow

### Added
- MkDocs site with Material theme for publishing research as GitHub Pages
- GitHub Actions workflow for automatic deployment on push to main
Expand Down
97 changes: 65 additions & 32 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,63 +2,96 @@

## Project overview

Research publishing site for ITKdev, built with MkDocs + Material theme and deployed to GitHub Pages.
Research publishing site for ITKdev, built with VitePress and deployed to GitHub Pages. The theme and dev setup follow the ITK Dev documentation site conventions.

**Live site:** https://itk-dev.github.io/research-projects/
**Repository:** https://github.com/itk-dev/research-projects

## Structure

```
docs/ # All publishable content (MkDocs source)
docs/index.md # Landing page — lists all projects
docs/about.md # About ITKdev Research
docs/stylesheets/custom.css # Custom theme overrides
docs/projects/<project-name>/ # Each research project
index.md # Main report
mocks/ # Interactive HTML prototypes
mocks.md # Mocks listing page
images/ # Screenshots and figures
mkdocs.yml # Site config and navigation
overrides/main.html # Theme override (noindex meta tag)
requirements.txt # Python dependencies
.github/workflows/deploy.yml # Auto-deploy on push to main
docs/ # VitePress source content root
docs/.vitepress/config.mts # Site config (title, nav, search, meta)
docs/.vitepress/sidebar.mts # Sidebar and nav definitions
docs/.vitepress/theme/index.ts # Custom theme entry
docs/.vitepress/theme/custom.css # ITK Dev brand colors + custom styles
docs/.vitepress/theme/DocLayout.vue # Layout with author info + password gate
docs/.vitepress/theme/PasswordGate.vue # Client-side password protection component
docs/.vitepress/theme/usePasswordAuth.ts # Auth composable (SHA-256 + sessionStorage)
docs/index.md # Landing page (VitePress home layout)
docs/about.md # About ITKdev Research
docs/public/robots.txt # SEO control (noindex)
docs/public/projects/<name>/mocks/ # Interactive HTML prototypes (static assets)
docs/projects/<project-name>/ # Each research project
index.md # Main report
mocks.md # Mocks listing page
images/ # Screenshots and figures
package.json # Node dependencies
docker-compose.yml # Docker dev environment
Taskfile.yml # Task automation (dev, build, lint)
.github/workflows/deploy.yml # Auto-deploy on push to main
.github/workflows/verify_build.yml # PR build verification
```

## Conventions

- All content is written in Markdown
- Interactive mocks are self-contained (no external dependencies). Small mocks use a single HTML file. Larger mocks may split CSS and JS into co-located files with the same base name (e.g. `unified-platform.html`, `.css`, `.js`)
- Images are PNG screenshots of the mocks
- Interactive mocks are self-contained HTML files stored in `docs/public/projects/<name>/mocks/` so VitePress serves them as raw static assets
- Images are PNG screenshots stored alongside project markdown in `images/`
- Every project document starts with a project label: `<small>**Project:** Project Name</small>`
- Use MkDocs Material admonitions (`!!! info`, `!!! warning`) for callouts
- Internal links use relative paths
- Theme is neutral (black/grey) — no bright colors
- Use VitePress custom containers for callouts: `::: info Title` / `::: warning Title`
- Mock links use absolute paths from the public root: `/projects/<name>/mocks/file.html`
- Mock buttons use the `mock-button` CSS class: `<a href="..." class="mock-button" target="_blank">Text ↗</a>`
- Theme uses ITK Dev brand colors (teal/cyan) with dark/light mode
- Site has `noindex, nofollow` meta tags and `robots.txt` to prevent crawling

## Password-protecting a project

Add frontmatter to any markdown file to require a password:

```yaml
---
protected: true
passwordHash: "<sha256-hash-of-password>"
passwordGroup: "project-name"
---
```

Generate the hash: `echo -n "your-password" | shasum -a 256 | cut -d ' ' -f 1`

Pages sharing the same `passwordGroup` only prompt once per browser session.

**Note:** This is client-side protection only. Content is in the HTML source. Adequate for casual access control, not for sensitive data.

## Adding a new research project

1. Create `docs/projects/<project-name>/` with at least an `index.md`
2. Add `<small>**Project:** Project Name</small>` at the top of each document
3. Add the project as a new nav section in `mkdocs.yml`:
```yaml
- Project Name:
- Overview: projects/<project-name>/index.md
- ...: projects/<project-name>/....md
```
4. Add a summary card on `docs/index.md`
5. If the project has mocks, create a `mocks.md` listing page and a `mocks/` folder
3. Add the project sidebar and nav entries in `docs/.vitepress/sidebar.mts`
4. Add a feature card on `docs/index.md` (VitePress features array in frontmatter)
5. If the project has mocks:
- Create a `mocks.md` listing page in the project folder
- Place mock HTML files in `docs/public/projects/<project-name>/mocks/`
- Link to mocks with absolute paths: `/projects/<project-name>/mocks/file.html`

## Building locally

### With Docker (recommended)

```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
mkdocs serve
task dev # Start dev server via Docker + Traefik
task build # Build static site
task open # Open in browser
```

Then open http://127.0.0.1:8000
### Without Docker

```bash
npm install
npm run docs:dev # Dev server at http://localhost:5173
npm run docs:build # Build to _site/
npm run docs:preview # Preview built site
```

## Deployment

Expand Down
45 changes: 45 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
version: "3"

# https://taskfile.dev/usage/#env-files
dotenv: [".env.local", ".env"]

vars:
# https://taskfile.dev/reference/templating/
BASE_URL: '{{.TASK_BASE_URL | default .COMPOSE_SERVER_DOMAIN | default .COMPOSE_DOMAIN | default "http://research.local.itkdev.dk"}}'
DOCKER_COMPOSE: '{{.TASK_DOCKER_COMPOSE | default "docker compose"}}'

tasks:
default:
cmds:
- task --list-all
silent: true

dev:
desc: "Start development server"
cmds:
- "{{.DOCKER_COMPOSE}} up --detach"

build:
desc: "Build static site"
cmds:
- "{{.DOCKER_COMPOSE}} run --rm node sh -c 'npm install && npx vitepress build docs'"

open:
cmds:
- open {{ .BASE_URL }}
silent: true

lint:apply:
desc: "Apply coding standards for Markdown"
cmds:
- "{{.DOCKER_COMPOSE}} run --rm markdownlint markdownlint '**/*.md' --fix"

lint:check:
desc: "Check coding standards for Markdown"
cmds:
- "{{.DOCKER_COMPOSE}} run --rm markdownlint markdownlint '**/*.md'"

lint:yaml:
desc: "Check coding standards for YAML"
cmds:
- "{{.DOCKER_COMPOSE}} run --rm prettier '**/*.{yml,yaml}' --check"
57 changes: 57 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
networks:
frontend:
external: true
app:
driver: bridge
internal: false

services:
nginx:
image: nginxinc/nginx-unprivileged:alpine
networks:
- app
- frontend
depends_on:
- node
ports:
- "8080"
volumes:
- ./.docker/templates:/etc/nginx/templates:ro
- ./.docker/nginx.conf:/etc/nginx/nginx.conf:ro
- ./:/app:delegated
environment:
NGINX_PORT: 8080
NGINX_PROXY_SERVICE: ${COMPOSE_PROJECT_NAME:?}-node-1:5173
labels:
- "traefik.enable=true"
- "traefik.docker.network=frontend"
- "traefik.http.routers.${COMPOSE_PROJECT_NAME:?}.rule=Host(`${COMPOSE_DOMAIN:?}`)"

node:
image: node:24-alpine
command: sh -c "npm install && npx vitepress dev docs --host 0.0.0.0"
working_dir: /app
networks:
- app
ports:
- "5173"
volumes:
- ./:/app:delegated
- node_modules:/app/node_modules

markdownlint:
image: itkdev/markdownlint
profiles:
- dev
volumes:
- ./:/md

prettier:
image: jauderho/prettier
profiles:
- dev
volumes:
- ./:/work

volumes:
node_modules:
Loading
Loading