This repository contains the code of the GRASS project website: https://grass.osgeo.org/
There are two ways to contribute; pick the path that matches your change:
- Content contributors edit or add pages (news, events, text). For simple Markdown changes you do not need a local build environment. Jump to For content contributors.
- Website developers change templates, styling, or the build itself and need to preview or build the site. Jump to For website developers.
Content lives in content/ as Markdown (.md) files. HTML can be mixed into
Markdown for more advanced presentation. You can edit a file and open a pull
request without installing anything; a maintainer or CI will build the preview.
Useful references:
-
Go to
content/news/. -
Create a new
.mdfile (date-prefixed names are the convention, e.g.2026_05_12_release.md). -
Start with this front matter, then write the body in Markdown:
--- title: "GRASS 8.5.0 released" date: 2026-05-12T21:38:40+02:00 layout: "news" author: GRASS Development Team ---
-
To include images, add them under
static/images/news/and reference them with an absolute path, for example. -
The item appears at /news/.
-
Go to
content/events/. -
Create a new
.mdfile using this front matter template:title: "CONFERENCE NAME" event: start: YYYY-MM-DD end: YYYY-MM-DD where: CITY, COUNTRY website: URL layout: "event" logo: images/conferences_logos/CONF_LOGO_YEAR.png
-
Add the logo to
static/images/conferences_logos/. -
The event shows in the
Next Eventssidebar on the news page when it is one of the next three upcoming events.
-
Add a
.mdfile under the relevantcontent/subdirectory (for examplecontent/about/), with the usual Hugo front matter and Markdown body. -
To add it to the navigation, add a menu entry in
config.tomlunder the appropriate parent, for example:[[Languages.en.menu.main]] parent = "About Us" name = "My new page" URL = "/about/mypage" weight = 1
-
If the page needs custom presentation, a developer can add a template under
themes/grass/layouts/.
If a page's front matter defines a summary, it is used as the page meta
description; otherwise the site-wide description in config.toml is used.
Open a pull request with your improvements. Thank you!
The site is built with Hugo Extended and compiles its styles with Dart Sass, so the plain (non-extended) Hugo will not work. Frontend libraries are installed with npm, and the pinned Hugo build uses an external Dart Sass binary.
The exact pinned versions live in the docker compose files (hugomods/hugo
image tags), .devcontainer/devcontainer.json, the workflows under
.github/workflows/, and .nvmrc for Node.js. Renovate keeps all of these
pins current automatically. Check those files for the current values rather
than a number copied into this README, which would go stale.
Any of the options below gives you a working toolchain. The container options (A, B, C) bundle Hugo, Node, and Dart Sass for you.
A Dev Container config in .devcontainer/ provides a consistent Hugo/Node setup
and works with any tool that supports devcontainer.json (VS Code Dev
Containers, GitHub Codespaces). In VS Code: Command Palette →
Dev Containers: Reopen in Container, then:
hugo server --bind 0.0.0.0Port 1313 is forwarded by default. View the site at http://localhost:1313.
Runs the Hugo dev server in a container with live reload and file watching, no local Hugo/Node needed:
docker compose -f docker-compose.dev.yml up --buildView the site at http://localhost:1313.
Builds the site with Hugo (using the pinned image in docker-compose.yml) and serves the
generated public/ with Nginx, to test a production-like build:
docker compose up --build # serves at http://localhost:8080
docker compose down # stop
docker compose run --rm build # one-off build without starting NginxThe build uses the hugomods/hugo image (Extended Hugo + Go + Node.js + Git +
Dart Sass); see docker.hugomods.com.
Install Hugo Extended (see the
Hugo installation docs; the extended build is
mandatory) and Node.js at the version pinned in .nvmrc (for example
with nvm: nvm install && nvm use, which
reads .nvmrc), then:
npm ci # install frontend dependencies (required before building)
hugo # build to public/
hugo server # dev server at http://localhost:1313Styles are authored in SCSS under themes/grass/assets/sass/ and compiled by
Hugo's Dart Sass pipeline. The entry point main.scss imports the GRASS brand
tokens and Bootstrap variable overrides before Bootstrap, so Bootstrap's
defaults yield to the GRASS theme; custom rules in _styles.scss load last. The
compilation is wired in themes/grass/layouts/partials/head.html (the toCSS
call with includePaths pointing at node_modules/bootstrap/scss), which is
why npm ci must run before any build. See
themes/grass/assets/sass/README.md for the file-by-file breakdown.
Frontend libraries are managed with npm and exposed to Hugo via module mounts in
config.toml.
npm install <package> # runtime dependency
npm install --save-dev <package> # dev dependencyCommit both package.json and package-lock.json, then verify the build:
npm ci
hugoTo use the library in the site, mount it into Hugo's assets in config.toml and
reference it from the theme. Example (JS):
# config.toml
[[module.mounts]]
source = "node_modules/<package>/dist/<file>.min.js"
target = "assets/js/<package>/<file>.min.js"{{/* themes/grass/layouts/partials/head.html */}}
{{ $lib := resources.Get "js/<package>/<file>.min.js" }}
<script src="{{ $lib.Permalink }}"></script>
Example (CSS):
# config.toml
[[module.mounts]]
source = "node_modules/<package>/dist/<file>.min.css"
target = "assets/css/<package>/<file>.min.css"{{/* themes/grass/layouts/partials/head.html */}}
{{ $styles := resources.Get "css/<package>/<file>.min.css" }}
<link rel="stylesheet" href="{{ $styles.Permalink }}">
Hugo caches the compiled SCSS under resources/_gen/, keyed on main.scss. If
you edit an imported partial (_styles.scss, _colors.scss, etc.) and the CSS
does not update, clear the cache and rebuild:
rm -rf resources/_gen public
hugoCI is unaffected because it builds from a clean checkout.