A static website for the Computational Multiphase Physics Laboratory, built with Jekyll and designed for hosting on GitHub Pages.
.
βββ _config.yml # Site-wide configuration
βββ _data # YAML data sources for data-driven pages
β βββ hero.yml # homepage hero (slides, copy)
β βββ news.yml # news feed (homepage + /news/ archive)
β βββ team.yml # team page (present, collaborators, alumni)
β βββ research_themes.yml # research themes block
βββ _includes # Reusable Jekyll fragments
β βββ footer-v2.html # site footer (v2)
β βββ theme-init.html # early theme persistence (avoids FOUC)
β βββ tokens-bridge.html # v2 token wiring
β βββ tokens-head.html # <head> token primer
βββ _layouts # Page templates
β βββ default.html # base layout used by most pages
β βββ research.html # research collection layout
β βββ teaching.html # teaching landing layout (sortable)
β βββ teaching-course.html # individual course layout
β βββ team.html # team page layout
β βββ join-us.html # join page layout
β βββ history.html # news history layout
βββ _research # Research project and publication entries
βββ _team # Team page (renders from _data/team.yml)
βββ _teaching # Teaching course entries and pages
βββ _join-us # Join Us page entries
βββ assets # Static files (images, css, js, logos, favicon)
β βββ css # Stylesheets
β β βββ tokens.css # v2 design tokens (single source of truth)
β β βββ bridge.css # maps legacy --color-* names to v2 tokens,
β β β # declares shared component primitives
β β βββ styles.css # legacy global stylesheet
β β βββ home.css # homepage v2 styles
β β βββ team-v2.css # team page v2 styles
β β βββ research-v2.css # research page v2 styles
β β βββ footer-v2.css # footer v2 styles
β β βββ about-layout.css # about-layout styles
β β βββ join-us.css # join page styles
β β βββ shared-news-history.css # news + history shared styles
β β βββ research.css # legacy research page styles
β β βββ team.css # legacy team page styles
β β βββ teaching.css # teaching page styles
β β βββ command-palette.css # βK command palette
β β βββ vendor.css # third-party CSS bundle
β βββ js # JavaScript files
β β βββ main.js # main JS (preloader, news loader, etc.)
β β βββ command-data.js # command palette data + behaviour
β β βββ platform-utils.js # platform detection / UI utilities
β β βββ shortcut-key.js # keyboard shortcut handling
β β βββ search_db.json # generated search index
β βββ favicon # Favicon files
β βββ images # Image assets (team, teaching, covers, β¦)
β βββ logos # Brand logos
βββ scripts # Build and utility scripts (see "Scripts Documentation")
βββ tests # Jest unit tests
βββ .github # CI workflows, issue/PR templates
βββ about.html # /about/ β JS redirect to /
βββ contact.html # /contact/ β JS redirect to /join
βββ join.html # /join/ β Join Us page
βββ index.html # / β Homepage
βββ News.md # /news/ feed legacy bullet list
βββ history.md # /history/ β full news archive
βββ Gemfile # Ruby dependencies
βββ _site # Generated site (ignored by Git)-
Quick Setup (Recommended)
For both fresh machines and existing development environments:
./scripts/setup.sh
This script will:
- Check for Ruby/Node.js and install them if missing (via rbenv/nvm)
- Install the repo-pinned Bundler version from
Gemfile.lock - Run a Ruby/Bundler preflight before installing dependencies
- Install all Ruby gems and npm packages
- Build the site and generate search database
- Install Git hooks for pre-commit checks (via Husky)
- Run validation tests
-
Manual Setup (Alternative)
Prerequisites:
- Ruby
3.4.10(matches.ruby-version) - Bundler
2.5.23(gem install bundler -v 2.5.23) - Node.js and npm (for linting and testing)
Install Dependencies:
# Ruby/Bundler preflight bash scripts/check-ruby-toolchain.sh # Ruby dependencies bundle _2.5.23_ install # JavaScript dependencies npm install
- Ruby
-
Build and Run
Quick Start (Recommended):
# Automatically finds available port and starts Jekyll with live reload ./scripts/deploy.shThis script will:
- Find an available port (4001-4999) to avoid conflicts
- Start Jekyll development server with live reload
- Display the local URL (e.g., http://localhost:4001)
- Auto-refresh browser on file changes
Manual Build (Alternative):
# Build the site and search database ./scripts/build.sh # Run local server bundle exec jekyll serve
- Visit http://localhost:4000 in the browser
- Changes require rebuilding with
./scripts/build.sh
-
Testing
# Run all tests npm test # Run tests with code coverage npm test -- --coverage # Run simple tests without Jest node scripts/simple-test.js # Run tests via wrapper script ./scripts/runTests.sh
-
Code Quality and Maintenance
# Run all linters and auto-fix issues ./scripts/lint-check.sh # Fix JavaScript line length issues (80 chars max) ./scripts/fix-js-line-length.sh # Convert single quotes to double quotes in JS files ./scripts/fix-quotes.sh # Fix script loading order (command-palette.js before command-data.js) ./scripts/fix-script-order.sh
-
Deployment
- Set this repository's Pages source to GitHub Actions. The sole publisher
is
jekyll.yml; pull requests build and test without publishing. - Main pushes (including search-index updates) build, validate and deploy one artifact. Production runs are serialized without interrupting a deployment.
- After deployment, the workflow verifies the live release manifest's Git SHA and purges only changed or removed website URLs. The first release purges its current URL inventory. Every purge requires HTTP and JSON API success.
- Repository secrets
CLOUDFLARE_ZONE_IDandCLOUDFLARE_API_TOKENsupply the existing zone-scoped cache-purge identity. Tokens never enter the artifact. - If purge fails after publication, rerun the failed deployment job in that same Actions run, while its predecessor-manifest artifact is retained. This preserves the URL delta needed to finish a partial purge. Starting a new workflow captures the already-published manifest and is not a substitute for completing the failed purge.
- Set this repository's Pages source to GitHub Actions. The sole publisher
is
The repository defines the response policy in security/response-headers.json.
GitHub Pages does not apply that file itself: deliver it using a Cloudflare
Response Header Transform Rule in the http_response_headers_transform
phase. Generate the exact rule with:
python3 scripts/browser-security.py cloudflare-ruleThe rule matches comphy-lab.org and the website's explicit owned paths from
scripts/purge-website-cache.py. It does not match other hosts, documentation
project paths, SL25 or the legacy calculation APIs. Add only this named rule
(public_website_browser_security), preserving existing rules, proxy state,
HTTPS enforcement and certificates. Re-read the ruleset immediately before
appending or updating it; do not replace the whole ruleset with this one rule.
After changing security/response-headers.json, apply the named rule with the
manual Sync Cloudflare security headers workflow (workflow_dispatch only).
It runs scripts/sync-cloudflare-security-headers.py with repository secrets
CLOUDFLARE_ZONE_ID and CLOUDFLARE_API_TOKEN, updates the existing
public_website_browser_security rule in place, and prints the rule id plus
whether the applied CSP still mentions fonts.googleapis (should be false
after the self-hosted fonts cutover). Local equivalent:
export CLOUDFLARE_ZONE_ID=...
export CLOUDFLARE_API_TOKEN=...
python3 scripts/sync-cloudflare-security-headers.pyThe CSP allows local scripts and Cloudflare's integrity-protected analytics
beacon, denies inline script handlers and eval, and denies framing and plugin
objects. Self-hosted brand webfonts load from 'self'. Font Awesome (cdnjs),
YouTube and the team map have explicit source allowances. Inline styles and HTTPS images remain supported for
existing layout and publication badges. Permissions-Policy disables camera,
microphone, geolocation, payment and USB while retaining video playback controls.
The stylesheet and image allowances do not permit executable scripts.
Marked, DOMPurify and Fuse are served locally. Their exact versions, package
integrity, file integrity and licenses are recorded in assets/vendor/manifest.json;
the shared include enforces file integrity in the browser. To update a dependency,
verify the package archive against its recorded registry integrity, replace only
the intended upstream files and license, update the manifest/include hashes, and
run the dependency and browser checks. Renew the Expires date in
.well-known/security.txt before it becomes stale.
Before activating a release:
Capture the current Pages API record and the named Cloudflare header rule (or its absence), including its ruleset/rule IDs and version, in a private release receipt before changing either provider. Record the current deployed commit and artifact alongside them. Use these saved records for the rollback below.
- Build and run the checks below. Test the loopback preview in both themes at desktop and mobile sizes, including search, tags, redirects, copy buttons, teaching pages, maps and video. The preview serves the exact proposed headers.
- Coordinate the Pages source change from legacy
main:/to GitHub Actions with merging this workflow. Wait for previous publishing runs to finish; retain the previous commit/artifact and provider settings for rollback. - Require the new deployment ID, live manifest SHA and successful purge receipt.
A green build or a successful
curlexit alone is insufficient. - Apply the named header rule. Use
--report-onlywhen generating a temporary CSP observation rule if production compatibility needs checking, then replace that same rule with the enforced policy. Verify real document responses and browser console/network behavior. Do not retain both policy variants. - Confirm
/.well-known/security.txtreturns UTF-8text/plainover HTTPS, and recheck representative documentation and SL25 paths for unchanged behavior.
./scripts/build.sh
python3 scripts/browser-security.py check --site-dir _site
python3 tests/browser-security.test.py
python3 tests/purge-website-cache.test.py
npm test -- --runInBand
python3 scripts/serve-security-preview.py --port 4173The preview also provides /about/__security-probe__.html to check that a local
script runs while inline scripts, handlers and an unapproved script are blocked;
/__frame-probe__.html attempts to frame it. These fixtures exist only in the
loopback server and are never included in the published artifact.
Rollback restores only the named header rule's saved version (or removes that rule if newly added), redeploys the previous known-good website release, and purges its affected URL inventory. If reverting the workflow migration as well, restore the saved Pages source after disabling the competing custom publisher. Preserve DNS, HTTPS enforcement, certificates, documentation base paths and unrelated edge rules throughout. Until activation and live readback are complete, the repository checks establish readiness, not deployed protection.
The homepage hero, news feed, team page, and research themes are
data-driven from YAML files in _data/. Edit the data file rather
than hand-writing HTML.
_data/hero.yml drives the hero section (slides, eyebrow, copy, video
poster). Add or reorder slides there; layout adapts automatically.
_data/news.yml: source of truth for the homepage feed and/news/archive. Each item hasdate,kind(paper|talk|people|move|award),title,meta,action_label,action_href.News.md: legacy short-form list, kept to 5 most recent items plus the pinned Durham announcement.history.md: full archive, never trimmed.- Years descend in
history.md; months descend within each year. Pinned items have no month/year header. - Use the
/add-newsslash command (in agent-driven workflows) or manually edit all three files together.
about.html(Jekyll permalink/about/): redirects to/via JS with a visible fallback link.contact.html(Jekyll permalink/contact/): redirects to/join.- Both mirror the same redirect pattern; keep them aligned if you change one.
The team page renders from _data/team.yml β three sections (present,
collaborators, alumni), each with name, role, photo, bio,
optional links. Photo files go under assets/images/team/ (1:1 crop,
used at --r-md radius). Bio is line-clamped per section (3 lines
present, 4 collaborators, unset alumni).
present:
- name: Member Name
role: Current position, Institution
photo: /assets/images/team/N.webp
bio: Brief research interest summary.
links:
- { kind: github, href: "https://github.com/username" }
- { kind: scholar, href: "https://scholar.google.com/citations?user=ID" }- Each paper should be added to
_research/index.mdin the following format:
<h3 id="NUMBER">[NUMBER] Author1, A., **Author2, B.**, & Author3, C. Title. _Journal_, Volume, Pages (Year).</h3>
<div class="tags"><span>Tag1</span><span>Tag2</span><span>Featured</span></div>
[](URL)
[](URL)
<iframe width="560" height="315" src="YOUTUBE_EMBED_URL" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>-
Important elements:
id="NUMBER": Unique ID for direct linking (e.g.,/research/#12)[NUMBER]: Paper number in square brackets- Author names: Use
<strong>for lab members - Journal names: Use italics with underscores
- Tags: Include relevant topic tags
- Badges: Use shields.io style badges for links
- Videos: Use YouTube embed code with privacy-enhanced mode
-
Featured Papers:
- Add
<span>Featured</span>to the tags to display the paper on the main page - Maximum 2 papers can be featured at any time
- Featured papers will automatically appear in the featured section of the homepage
- Add
-
Available Tags:
- Bubbles
- Drops
- Jets
- Sheets
- Non-Newtonian
- Coalescence
- Superamphiphobic-surfaces
- Impact forces
- Dissipative anomaly
- Soft-matter-singularities
- Featured
- (Add new tags as needed)
-
Common Badge Types:
[](URL) [](URL) [](URL) [](URL) [](https://blogs.comphy-lab.org)
-
Main Teaching Page
- Located at
_teaching/index.md - Lists all available courses
- Uses the
teachinglayout with sorting functionality
- Located at
-
Individual Course Pages
- Located in
_teaching/directory (e.g.,_teaching/2025-Basilisk101-Madrid.md) - Use the
teaching-courselayout (optimized for single course display without sorting functionality) - Follow this basic format:
--- layout: teaching-course title: "Course Title" permalink: /teaching/course-permalink --- <div class="course-image"> <img src="/path/to/banner-image.jpg" alt="Course Title" loading="lazy"> </div> # Course Title <div class="course-details"> <div class="course-details__item"> <h4><i class="fa-solid fa-calendar-days"></i> Dates</h4> <p>Date range</p> </div> <div class="course-details__item"> <h4><i class="fa-solid fa-location-dot"></i> Location</h4> <p>Location information</p> </div> </div> ## Course content...
- Located in
-
Course Images
- Store in
/assets/images/teaching/directory - Card images (600x400px): Used on the main teaching page
- Banner images (1200x400px): Used on individual course pages
- Follow naming convention:
[course-name]-[location].[extension]
- Store in
The website includes a powerful search feature that allows users to:
- Search through all content including titles, text, and tags
- Get instant search results with highlighted matching text
- See match percentage for each result
- Navigate directly to specific sections using anchor links
- Access search via keyboard shortcut (βK on Mac, ctrl+K on Windows) or by clicking the magnifying glass icon in the navigation
Search results are prioritized and filtered as follows:
- Team Members (highest priority)
- Direct matches in names
- Research interests and affiliations
- Social media links and profile information
- Teaching Content
- Course titles and descriptions
- Course details (dates, locations, prerequisites)
- Course schedules and topics
- Research Papers
- Titles and authors
- Tags and categories
- Blog Posts from blogs.comphy-lab.org
- Indexed directly from the GitHub repository (comphy-lab/CoMPhy-Lab-Blogs)
- Only indexes markdown files where the publish flag is not set to false
- Excludes todo markdown files (case-insensitive)
- Updated automatically every 12 hours via GitHub Actions
- Regular content (headings and paragraphs)
The search database is maintained in a separate repository comphy-lab/comphy-search and is automatically updated in this website via GitHub Actions. This approach:
- Centralizes search database generation in a dedicated repository
- Ensures consistent search functionality across the website
- Automatically updates the search database daily or when content changes
- Simplifies maintenance by separating search logic from the website code
Search results are prioritized using a two-step process:
-
Priority Field: Each entry in the search database has a priority field (1-5, with 1 being highest priority)
- Priority 1: Team members
- Priority 2: Featured papers and teaching content
- Priority 3: Regular papers and blog posts
- Priority 4-5: Other content
-
Relevance Scoring: Within each priority level, results are further ranked by relevance using:
- Title matches (70% weight)
- Content matches (20% weight)
- Tag and category matches (10% weight)
This ensures that higher-priority content (like team members) always appears before lower-priority content, even if the lower-priority content has a better text match.
The website includes a command palette feature that provides quick access to actions and navigation through keyboard shortcuts:
- Keyboard Shortcut: Access via βK on Mac, ctrl+K on Windows, or by clicking the terminal icon in the navigation
- Navigation Commands: Quickly navigate to any section of the website
- External Link Commands: Direct access to GitHub, Google Scholar, YouTube, and Bluesky
- Tool Commands: Scroll to top/bottom and other utility functions
- Context-Aware Commands: Additional commands appear based on current page
- Search Integration: Search is available separately via the βK shortcut
- Keyboard Navigation: Use arrow keys to navigate through commands, Enter to select, and Esc to close
Key features:
- Custom implementation with vanilla JavaScript for better control and performance
- Different visual styling from search to avoid confusion (indigo accent color vs blue for search)
- Grouping of commands by section for easy discoverability
- Shortcuts for common tasks (g h = go home, g r = go to research, etc.)
- Full keyboard navigation with arrow keys, Enter, and Escape
- Footer with keyboard shortcut hints for better usability
The command palette is built with:
- Custom vanilla JavaScript implementation
- Responsive and accessible design
- Integration with the site search database for content discovery
- Complete keyboard navigation support
Files:
/assets/js/command-data.js: Defines all available commands and search database integration/assets/css/command-palette.css: Styling for the command palette
Search behavior and features:
- Minimum query length: 3 characters
- Keyboard shortcut (βK / ctrl+K) opens a command palette style search interface on all pages
- Magnifying glass icon in navigation opens the search interface when clicked
- Search input in navigation shows the full "βK (search)" text by default
- Custom command palette implementation provides a modern command palette experience
- Search results appear instantly as you type
- Results are ranked by relevance and match percentage
The search functionality includes content from our external blog at blogs.comphy-lab.org:
- Blog posts are fetched and indexed in the comphy-search repository
- Each post's title and content are searchable
- Results link directly to the blog post
- Blog content is refreshed with each update to the search database
Research papers can be tagged with multiple topics. Tags are defined in the markdown files using the following format:
<div class="tags"><span>Tag1</span><span>Tag2</span></div>These tags are:
- Displayed with each paper
- Searchable through the search interface
- Used for filtering papers by topic
- Included in search match percentage calculations
_config.yml: Site-wide settings, collections, build options- Layout Templates in
_layouts/ - Partial Includes in
_includes/ - Assets in
assets/
The website supports both light and dark themes with an easy toggle switch in the header navigation:
-
Theme Preferences
- Automatically detects user's system preference (light/dark mode)
- Remembers user's manual selection using localStorage
- Maintains theme consistency across page navigation
-
Implementation Details
- Theme toggle button in the header of all layouts (default, team, research, teaching, teaching-course, join-us, history)
- Brand colours and theme variables live in
assets/css/tokens.css(single source of truth for the v2 design system) bridge.cssmaps legacy--color-*names onto the v2 tokens, so legacy stylesheets continue to work- Early theme persistence via
_includes/theme-init.htmlto avoid FOUC
-
Customizing Theme Colors
- Brand and theme tokens:
assets/css/tokens.css - Legacy variable bridge (kept for compatibility):
assets/css/bridge.css - Legacy globals:
assets/css/styles.css; legacy page styles inresearch.css,teaching.css,team.css - v2 page styles:
home.css,team-v2.css,research-v2.css,footer-v2.css - Theme is applied via the
data-themeattribute on the HTML element
- Brand and theme tokens:
- Color Scheme
- Gradient text (Red to Blue) for lab name
- Warm orange tint + blur for header
- Dark theme support with adjusted colors for better night viewing
- Theme Toggle
- Located in the header next to the Google Scholar icon
- Switches between light (default) and dark themes
- Uses SVG icons (moon/sun) with smooth transitions
- Persists user preference via localStorage
- Falls back to system preference when no user selection exists
- Typography
- Libre Baskerville, Open Sans
- Gradients for emphasis
- White text in dark theme for improved readability
- Favicon
- Located in
/assets/favicon/ - Multiple sizes for different devices and browsers
- Generated from CoMPhy Lab logo
- Located in
- Academicons 1.7.0 (SIL OFL 1.1, MIT)
- Font Awesome
- Fontello (Various licenses)
- Libre Baskerville (SIL Open Font License)
- Open Sans (Apache License 2.0)
| Workflow | Purpose |
|---|---|
jekyll.yml |
Builds and tests PRs; publishes main through Pages, verifies the live release, then purges changed website URLs. |
update-search.yml |
Pulls the search index from comphy-lab/comphy-search daily at 04:00 UTC and on content changes. |
content-rules-checks.yml |
Runs validate-content-rules.sh and the trigger-parity check. |
maintenance-regression-checks.yml |
Regression suite for housekeeping scripts (deploy, validators). |
teaching-content-checks.yml |
markdownlint + Prettier gate for _teaching/ pages. |
pr-hygiene-check.yml |
PR hygiene β blocks mixed dependency/security PRs. |
sync-org-profile-publications.yml |
Syncs publications to the org profile README. |
weekly-tests.yml |
Scheduled weekly smoke tests. |
Together these ensure the site builds, the search index stays fresh, and content invariants hold across PRs.
Blog content from blogs.comphy-lab.org is indexed in the comphy-search repository, sourced from comphy-lab/CoMPhy-Lab-Blogs.
Filtering rules:
- Only indexes markdown files where
publish: falseis not set in frontmatter. - Excludes any file with "todo" in the filename (case-insensitive).
Index refresh:
- Daily via GitHub Actions.
- On pushes that change markdown/HTML.
- Manually via the Actions tab.
The split-repo approach centralises search generation, reads raw
markdown from source, respects publish: flags, and avoids web-scraping
rate limits.
The scripts/ directory contains various utility scripts for development, testing, and maintenance:
-
setup.sh- Complete environment setup for both fresh and existing installations- Installs Ruby via rbenv and Node.js via nvm if not present
- Installs the repo-pinned Bundler version and runs a Ruby/Bundler preflight
- Installs all dependencies (Ruby gems and npm packages)
- Builds the site and runs validation tests
- Handles version conflicts gracefully
-
build.sh- Main build script- Builds the Jekyll site
- Generates the search database
- Updates SEO tags
- Creates filtered research pages by tags
-
deploy.sh- Local development server with smart port detection- Automatically finds available ports (4001-4999)
- Starts Jekyll development server with live reload
- Handles port conflicts gracefully
- Auto-refreshes browser on file changes
-
lint-check.sh- Code quality and formatting- Runs all linters (ESLint, Stylelint, markdownlint)
- Auto-fixes issues when possible
- Ensures code consistency across the project
-
check-ruby-toolchain.sh- Ruby/Bundler preflight- Verifies Ruby version matches
.ruby-version - Verifies the repo-pinned Bundler is available
- Used by
setup.sh; also runnable on its own when chasing "wrong Ruby" install errors
- Verifies Ruby version matches
-
validate-content-rules.sh- Content invariants- Checks
history.mdchronological ordering - Checks
_research/index.mdtag-markup format - Checks
CLAUDE.mdandREADME.mddon't reintroduce the deprecated baretagselement (replaced bydiv.tagsblocks) - Run by the
content-rulesCI workflow
- Checks
-
check-content-rules-trigger-parity.py- Workflow guard- Ensures the file paths that trigger the
content-rulesworkflow match the validated-file set, so the validator never silently skips a file by mistake
- Ensures the file paths that trigger the
-
fix-js-line-length.sh- JavaScript line length fixer- Ensures JavaScript files don't exceed 80 characters per line
- Automatically wraps long lines while preserving functionality
- Uses the Node.js script
fix-line-length.js
-
fix-quotes.sh- Quote standardization- Converts single quotes to double quotes in JavaScript files
- Platform-aware (handles macOS/Linux sed differences)
- Processes all JS files in
assets/js/
-
fix-script-order.sh- Script dependency ordering- Ensures
command-palette.jsloads beforecommand-data.js - Scans HTML files in
_layouts/and_includes/ - Automatically reorders script tags when needed
- Ensures
-
generate_seo_tags.rb- SEO optimization- Generates meta tags for better search engine visibility
- Creates Open Graph and Twitter Card metadata
- Processes all research papers and pages
-
generate_filtered_research.rb- Research filtering- Creates tag-based filtered pages for research papers
- Generates individual pages for each research tag
- Updates navigation and search functionality
sync_org_profile_publications.py- Org profile sync- Pulls the publications block from
_research/index.mdinto the GitHub org profile README (kept in_org_profile/) - Run periodically; not part of the regular build
- Pulls the publications block from
-
simple-test.js- Lightweight test runner- Runs basic tests without external dependencies
- Validates project structure and file existence
- Checks build outputs and syntax
- Provides colored terminal output
-
runTests.sh- Test wrapper- Simple wrapper for
npm test - Returns appropriate exit codes for CI/CD
- Displays success/failure messages
- Simple wrapper for
fix-line-length.js- Line breaking utility- Core logic for breaking long JavaScript lines
- Handles strings, comments, and code intelligently
- Preserves code functionality while improving readability
This project uses Jest for unit testing JavaScript functionality with comprehensive coverage:
- fix-line-length.js - Line breaking utilities and string processing
- command-data.js - Command palette functionality and search integration
- platform-utils.js - Platform detection and UI utilities
- shortcut-key.js - Keyboard shortcut handling
- teaching.js - Teaching page course sorting and filtering
- setup.js - Browser environment mocks and test setup
# Install dependencies
npm install
# Run all tests
npm test
# Run tests with code coverage
npm test -- --coverage
# Run tests in watch mode (re-runs on file changes)
npm test -- --watch
# Run a specific test file
npm test -- command-data.test.js
# Run simple validation tests (no dependencies)
node scripts/simple-test.js- Tests are located in the
/testsdirectory - Test files follow the naming pattern:
[module-name].test.js - Browser environment is mocked in
setup.jsfor DOM-dependent code - Tests use Jest's built-in assertions and mocking capabilities
- Coverage reports are generated in the
/coveragedirectory
- Unit Tests - Test individual functions and modules
- Integration Tests - Test component interactions
- Validation Tests - Check file structure and build outputs
- Mock Tests - Verify browser API mocks work correctly
-
Create a new test file in the
/testsdirectory -
Import the module or function to test:
const { functionName } = require("../path/to/module");
-
Structure tests using
describeanditblocks:describe("Module Name", () => { it("should do something specific", () => { expect(functionName(input)).toBe(expectedOutput); }); });
-
Run tests to ensure they pass
-
Check coverage with
npm test -- --coverage
- Maintain at least 80% code coverage
- Focus on critical path testing
- Mock external dependencies appropriately
- Test edge cases and error handling
The repository includes several issue templates to streamline the process of reporting problems or requesting changes:
- π Report a Bug: Use this template to report website issues or malfunctions
- π€ Add Team Member: Template for requesting addition of new team members
- π Add Publication: Template for adding new research publications
- β¨ Suggest Enhancement: For suggesting improvements or new features
To create a new issue:
- Click on one of the links above to use a template directly
- Or go to the Issues tab and click "New Issue"
- Choose the appropriate template
- Fill in the required information
- Submit the issue
When submitting changes, use the provided PR template which includes:
- Description of changes
- Type of change (bug fix, feature, content update, etc.)
- Testing checklist
- Related issues
- Screenshots (if applicable)
To submit a PR:
- Fork the repository
- Make your changes in a new branch
- Test changes locally
- Create a PR using the template
- Link any related issues
- Wait for review
- Use 2-space indentation across all files
- Follow DRY principles: reuse components, variables, and styles
- Add comments for complex logic, but keep code self-documenting
- Use semantic HTML elements
- Follow BEM naming convention for CSS classes (e.g.,
s-header__nav-list) - Keep content files in markdown format where possible
- Use CSS variables for colors and typography (defined in
:root) - Use responsive breakpoints at 1700px, 1300px, 900px, 768px, 500px
- Use
remunits for font sizes and spacing - Follow mobile-first approach for media queries
- Leverage CSS custom properties for theme switching
- Organize media queries by breakpoint for better maintainability
- Use standardized variable naming for consistent styling
- Use ES6+ features (arrow functions, const/let, template literals)
- Always include 'use strict' mode
- Use async/await for asynchronous operations
- Implement error handling with try/catch blocks
- Use camelCase for variable and function names
- Prefer event delegation for multiple similar elements
- Optimize images for web (compress to reduce file size)
- Follow naming convention:
[name]-[descriptor].[extension] - Include alt text for all images
The repository uses automated tools to ensure code quality and consistency:
-
Install dependencies (automatically includes pre-commit hooks):
./scripts/setup.sh
Or manually:
npm install ./node_modules/.bin/husky install
This repository uses Husky and lint-staged to automatically check and format code before commits:
- JavaScript files: ESLint (with auto-fix) + Prettier
- CSS files: Prettier formatting
- Markdown files: markdownlint-cli2
- JSON/YAML files: Prettier formatting
When you commit, these checks run automatically on staged files only. If any issues are found that can't be auto-fixed, the commit will be blocked.
- JavaScript: ESLint with recommended rules
- Run manually:
npm run lint:js
- Run manually:
- CSS: Stylelint with standard configuration
- Run manually:
npm run lint:css
- Run manually:
- Markdown: markdownlint-cli2 for consistent documentation
- Run manually:
npm run lint:md
- Run manually:
- Code Formatting: Prettier
- Run manually:
npm run format
- Run manually:
- Tests: Jest
- Run manually:
npm test
- Run manually:
- Stage your changes:
git add . - Commit:
git commit -m "your message" - Pre-commit hooks automatically:
- Run ESLint on JavaScript files (auto-fixes when possible)
- Format all files with Prettier
- Check Markdown files with markdownlint
- If all checks pass, the commit proceeds
- If any check fails, the commit is blocked with error details
If needed, hooks can be bypassed with:
git commit --no-verifyThe site is mid-migration to a "v2" design system. New work composes from the v2 layer; legacy styles remain so older pages keep rendering.
-
Design tokens (
assets/css/tokens.css)- Single source of truth for the v2 layer
- Brand colours (
--c-brand-*,--c-accent-*) - Typography (
--t-*), spacing (--s-*), radii (--r-*) - Light theme by default; dark overrides under
[data-theme="dark"]
-
Bridge layer (
assets/css/bridge.css)- Loaded after
tokens.cssandstyles.cssso its:rootdeclarations win the cascade - Maps legacy
--color-*/--font-*/--shadow-*names onto v2 tokens β legacy pages pick up the new palette without rewrites - Declares shared component primitives reused across v2 pages
(panel, eyebrow, lede, chip, btn-ghost, paper-card, news-row,
team-tile,
.visually-hidden)
- Loaded after
-
v2 page stylesheets
home.css,team-v2.css,research-v2.css,footer-v2.css,about-layout.css,shared-news-history.css,join-us.css- Compose from bridge primitives; only add page-specific rules
-
Legacy stylesheets
styles.css,research.css,team.css,teaching.css- Kept for pages not yet migrated; bridged into the v2 token system
-
Consolidated media queries
- Breakpoints: 1700, 1300, 1200, 900, 768, 500 (mobile-first)
- Organised by breakpoint for cache-friendly delivery