Skip to content

Repository files navigation

OSUOSL Hugo Static Site

Migrated from our static Pelican site

Based off of the Mainroad theme and the previous OSL theme

Development

Prerequisites

Read the Hugo getting started guide's prerequisite list and install the Hugo binary or package. You can check if it is installed from the command line by running:

hugo version

This project uses npm for the functionality of Prettier and Markdownlint. You can check that npm is installed from the command line by running:

npm -v

The search feature is implemented using Pagefind and can be run with the npm script serve or by downloading the binary.

Setup

If you do not have permissions to work directly on a branch of the repository, you will have to fork the repo and work from your fork. If you do have permission, create a branch and work from your branch.

Once the prerequisites are installed and you are working off the fork or branch, clone the repository to your local machine.

This can be done from the command line:

# Fork
git clone git@github.com:YOURUSERNAMEHERE/website.git ./osl-website

# Branch
git clone git@github.com:osuosl/website.git ./osl-website

Then, navigate to the folder and then install the dependencies:

cd ./osl-website
npm install

Local Development

To compile and host the site under development on port 1313, run:

hugo server

Once Hugo is done setting up, you should see a success message:

Web Server is available at http://localhost:1313/ (bind address 127.0.0.1)
Press Ctrl+C to stop

Formatting and Linting

This project uses prettier and markdownlint. You can install them in your preferred editor to see changes as you edit or run them from the command line.

To format and lint from the command line, run:

# Format via prettier
npm run format

# Lint via markdownlint
npm run lint

Preview Production

If you want to preview the production server to see the full search functionality, you can build the pages and then serve from Pagefind. Pagefind sources from the public/ directory Hugo compiles when the website is built.

First compile the pages:

hugo

Then run it using Pagefind through the provided helper npm script or the binary using the specific flags:

# NPM helper script
npm run serve

# Binary
./pagefind --site public --serve

Testing

CI (.github/workflows/hugo_build.yml) runs on every pull request: a Hugo build, a Pagefind index, internal link/asset validation (htmltest with .htmltest.yml), an alias-redirect check (scripts/check-aliases.py — every redirect stub must land on a real page, so the site can never ship a redirect chain or loop), and the accessibility scan described below. scripts/check-redirects.py is a manual companion that follows redirects on a live host (production or a staging deploy), catching collisions between the site's redirects and server-side Apache rules.

Accessibility testing

The site must conform to WCAG 2.1 Level AA: the Department of Justice's rule on digital accessibility under ADA Title II requires it of Oregon State University, with a compliance date of April 26, 2027 — see OSU's New ADA Rule on Digital Accessibility page. CI runs pa11y-ci (config: .pa11yci.js) as a blocking check. The URL list is generated from the built site, so new pages are scanned automatically. By default every unique page is scanned; individual blog posts and tag pages — hundreds of instances of one template — are sampled by one representative each, and alias redirect stubs are skipped. Scans use two runners, both fully enabled:

  • axe-core — actively maintained by Deque, analyzes fully rendered pages (including CSS custom properties), and is tuned for few false positives.
  • HTML CodeSniffer — maps checks directly onto WCAG techniques and is stricter and more verbose; older and less actively maintained than axe.

We run both because they analyze differently and each has caught real bugs the other missed in this repo: axe flagged the light-mode active-nav contrast; HTML CodeSniffer flagged the required-asterisk contrast that axe's text heuristics skip. When the runners disagree, fix the issue or document a narrowly-scoped exception with a comment in .pa11yci.js — the config intentionally contains no ignored rules.

Two kinds of result get different treatment, and the distinction matters:

  • Proven violations fail the build. No exceptions.
  • "Needs review" results are capped at warning (levelCapWhenNeedsReview). These are cases axe could not measure rather than failures it found — mainly contrast where an image sits behind or on the element (the hero photo, the chevron Bootstrap draws on every <select>). They stay visible in the CI output, and the manual contrast sweep in the QA checklist below is what actually clears them.

Prefer that cap over hideElements, which drops the element from the DOM both runners see and therefore deletes all coverage of it — an unlabeled <select> would sail through. hideElements is reserved for markup we cannot fix at all (the third-party reCAPTCHA widget).

To run the scan locally:

hugo && npx pagefind --site public
python3 -m http.server 8080 --directory public &
npx pa11y-ci --config .pa11yci.js

To sweep every rendered page — all blog posts and tag pages included — set PA11Y_FULL=1 (takes a few minutes; same settings otherwise):

PA11Y_FULL=1 npx pa11y-ci --config .pa11yci.js

Run the full sweep before a launch, after editing old blog posts (their raw HTML predates the markdownlint alt-text rule), or after changing the blog template or syntax highlighting.

Manual QA checklist

Automated scanners only see each page's initial, static state. Before launches or significant UI changes, also verify by hand:

  • Keyboard: Tab through every template — skip link appears first and works, nav dropdowns open with Enter/Space and close with Escape, the search dialog traps and restores focus, no keyboard traps.
  • Screen reader: smoke test (NVDA + Firefox or VoiceOver + Safari) on the homepage, a form (including the error path), search, and a blog post.
  • Both color modes: toggle dark mode and check contrast of interactive states (hover, focus, active), search results, and form messages — scanners only test the mode the browser prefers.
  • Interactive states: open search with results on screen, open each nav dropdown, submit a form with an error.
  • Zoom/reflow: 400% zoom (320px-wide reflow) with no horizontal scrolling or lost content; 200% text-only zoom.

Adding Content

Content is added inside the /content folder, though it varies based on what you would like to do.

Request forms

The five hosting/CI request forms are data-driven: each page holds only its intro text plus a {{</* request-form <name> */>}} shortcode, and the fields live in data/forms/<name>.yml. To add or change a field, edit the YAML — the shortcode and the form-field partial render Bootstrap-styled, accessible markup (labels, help text, required indicators, checkbox-group validation) automatically. Two rules:

  • A field's name key is the formsender POST parameter. Never rename one without coordinating with the formsender ticket templates.
  • Shared formsender settings (action URL, token, reCAPTCHA site key) live under [params.formsender] in config/_default/params.toml.

Adding a New Blog Post

Regular pages use the default /archetypes/default.md archetype.

Blog posts are stored in /content/blog and use the associated /archetypes/blog.md archetype.

To add a blog post, use the hugo new command:

hugo new blog/your-slug-title-here.md

The author of a page should be included as an array of authors within the front matter:

---
# ...
authors: [OSUOSL Admin]
# ...
---

To add a header image at the top of a blog post, use the CSS tag #blog:

![Image Alt](/images/image_path#blog)

Adding a New Tag

Tags (called Terms in Hugo) are added simply by adding an associated string in the frontmatter of a blog post. This alone will create a semi-broken tag due to how tags are rendered, so you will have to add an associated page in the content folder to add metadata.

After you add a tag to a blog post, create a new file in the directory /content/tags/[tag name here]/_index.md, making the folder if it does not already exist.

Inside this file, include the frontmatter template below as well as any content you want displayed along with the tag:

---
title: "Example Tag Here"
slug: example-tag-here
---

Content here will be placed on the tag page. You may also include images here.

If you do not do this, the tag will be displayed as not having a name.

License

Apache 2.0

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages