Design system  /  Identity  /  Websites

Where we publish what.

Six domains, two hosting stacks. Each domain does one thing for one audience. This page explains what lives where, how we set it up, and why the stack is split this way.

What we publish where · six domains
CMS · OpenCatalogi
conduction.nl

Main brand. Services, consultancy, building, innovation. This is where we brand. The story for the SMB decision-maker who finds us via Google: what we build, who for, and how to install something today.

Audience
SMB primary, government secondary
Content
Brand, blog, news, "about us"
Tech
OpenCatalogi on Nextcloud
Editor
Non-developer, via CMS
Static · Docusaurus
connext.conduction.nl

Platform proposition. ConNext as a product. For the government and SMB decision-maker who wants to see the system level: app ecosystem, workspace + apps, "what is ConNext".

Audience
Decision-makers, architects
Content
Platform, ecosystem, integrations
Tech
Docusaurus on GitHub Pages
Editor
Marketing + dev, via PR
Static · Docusaurus
[product].conduction.nl

Per product. One subdomain per app: opencatalogi, openregister, docudesk, launchpad, openconnector, and the rest. For the user and developer who wants to work with that specific app. Docs, API, install link.

Audience
End user + developer
Content
Per-product docs, API spec
Tech
Docusaurus on GitHub Pages
Editor
App team, via repo
CMS · OpenCatalogi
apps.conduction.nl

App store. Our own catalogue. Built on OpenCatalogi, because we dogfood our own product. Externally linked from every other site, because this is where the "install now" button lives.

Audience
Everyone, SMB-primary
Content
Apps + solutions catalogue
Tech
OpenCatalogi on Nextcloud
Dogfood
Yes, this is our own product
Common Ground · Docusaurus
commonground.nu

Hosting proposition. For customers who want their own Nextcloud environment, managed by us. Apex domain, so its own GitHub Pages repo with its own CNAME. Brand colour is Common Ground yellow, not cobalt: this is the Common Ground branch, not the Conduction one.

Audience
IT leads, government
Content
Hosting, management, pricing
Tech
Docusaurus on GitHub Pages
Editor
Sales + ops, via PR
Common Ground · Live Nextcloud
[client].commonground.nu

Customer environment. Real Nextcloud instances per customer. No GitHub Pages, no static build. Live application with data, users, integrations. This is the working software, not the proposition.

Audience
The customer themselves
Content
Their own data, their own apps
Tech
Nextcloud, one instance per customer
Editor
The customer + Conduction ops
The domain tree
conduction.nl connext [product] apps commonground.nu [client] Conduction · cobalt The single orange accent · where you want the visitor to click Common Ground · yellow

Rendered with <cn-domain-tree> + <cn-hex>.

How we set up a site · two stacks
Static stack

Docusaurus on GitHub Pages

For anything that's marketing or docs content. Static, version-controlled, deployable via a GitHub Actions workflow. One repo per subdomain, because GitHub Pages binds one custom domain per repo.

  • One repo per subdomain. connext repo for connext.conduction.nl, opencatalogi repo for opencatalogi.conduction.nl, and so on.
  • Shared @conduction/docusaurus-preset. One npm package with the brand tokens, theme, navbar, footer. Every site imports it; visual consistency without a monorepo.
  • CNAME file per repo. The custom-domain name lives in static/CNAME. GitHub reads it on every deploy.
  • DNS via the registrar. A records from the subdomain to the GitHub Pages IPs. The apex domain is verified once at org level; all subdomains inherit that verification.
  • Auto-deploy via Actions. Push to main builds and publishes to gh-pages. Live within one or two minutes.
Dynamic stack

OpenCatalogi on Nextcloud

For anything that's CMS content, or an actual product. We dogfood our own apps: the main brand and the app store run on OpenCatalogi; customer environments run on stock Nextcloud.

  • conduction.nl as a CMS site. Brand content, blog, and news are OpenCatalogi objects. Non-developers edit via the Nextcloud UI; no PR needed.
  • apps.conduction.nl as the app store. Apps and solutions are OpenCatalogi objects in the standard register. One OpenCatalogi instance, one polished public register.
  • Custom theme. A Nextcloud app that loads the brand tokens from this design system. Same colours, same typography, same hex components.
  • Customer environments, not propositions. [client].commonground.nu is a working Nextcloud instance per customer, with their own data and users. No marketing layer.
Multilingual · nl, en, de, fr
Configuration

Four languages, one build

Docusaurus 3 supports multilingual sites natively. One build generates all four languages into the same build/ output. Dutch is the default and lives at the root; the other languages get a sub-path: /en/, /de/, /fr/. Marketing copy and docs are always available in four languages without separate deploys.

  • defaultLocale: 'nl'. Dutch at /, the cleanest URL, matches "we lead in NL".
  • localeConfigs per language: htmlLang, label for the switcher, direction (all ltr).
  • trailingSlash: true. Set explicitly, otherwise hreflang breaks on GitHub Pages.
  • localeDropdown in the navbar. One line added to themeConfig.navbar.items.
  • hreflang comes automatically. Docusaurus emits <link rel="alternate"> per language, on every page, no config required.
Translation process

NL first, then the rest

Translations live in i18n/<locale>/ per site. Markdown files go into i18n/de/docusaurus-plugin-content-docs/current/; UI strings into code.json. If a translation is missing, Docusaurus falls back to the defaultLocale. Pages stay findable; the fallback isn't a 404.

  • npm run write-translations -- --locale fr. Generates JSON with all navbar, sidebar, and theme strings that still need translation.
  • Crowdin or git. Both work. Crowdin for team translators (its own UI), git for developer translators (PRs).
  • Decide up front. Migrating from git to Crowdin halfway means matching every key again.
  • Tracking discipline. Don't lean on the fallback; add npm run write-translations to a CI check so missing keys break PRs, not production.
  • Build time. Scales linearly, ~3 to 4 times a single-locale build. On GitHub Actions we stay well within the free minutes.

Configuration lives in @conduction/docusaurus-preset. One i18n block there, all five or six sites stay in lockstep. Template settings for the preset:

// docusaurus.config.js
i18n: {
  defaultLocale: 'nl',
  locales: ['nl', 'en', 'de', 'fr'],
  localeConfigs: {
    nl: { label: 'Nederlands', htmlLang: 'nl-NL' },
    en: { label: 'English',    htmlLang: 'en-GB' },
    de: { label: 'Deutsch',    htmlLang: 'de-DE' },
    fr: { label: 'Français',   htmlLang: 'fr-FR' },
  },
},
trailingSlash: true,

Source: Docusaurus i18n introduction · i18n tutorial

Why the split is the way it is
GitHub Pages · the restriction

One CNAME per repo. No wildcards. Hence N repos for N subdomains.

GitHub Pages binds exactly one custom domain to each repo, via the CNAME file at the root. Wildcards (*.conduction.nl) are explicitly discouraged by GitHub because of domain-takeover risk. One Docusaurus build can't serve multiple subdomains.

That could turn into a messy pile of repos. Which is why they all share the @conduction/docusaurus-preset package. Brand tokens, navbar, footer, theme: everything comes from one place. Fix a design bug in the preset once, every site benefits on the next build.

The CMS route via OpenCatalogi doesn't have that restriction: one Nextcloud instance serves all of its own content. But that only applies where we actually do CMS work. Docs and marketing content stay static, because static is faster, cheaper, and more reliable. GitHub Pages docs.

Setting up a new subdomain · four steps
Step 01

Repo + CNAME

Create a new repo under ConductionNL/. Scaffold a Docusaurus site with npx create-docusaurus@latest. Add static/CNAME with your subdomain: new.conduction.nl.

Step 02

Preset + DNS

Install @conduction/docusaurus-preset and wire it into docusaurus.config.js. Brand, colours, and typography work immediately. Add the A record on DNS to the GitHub Pages IPs.

Step 03

i18n + content

The preset ships the i18n block (nl/en/de/fr). Translate NL first, then the rest in i18n/<locale>/. Run npm run write-translations -- --locale <x> to generate UI strings.

Step 04

Workflow + verify

Add .github/workflows/deploy.yml (publishes to gh-pages). Set GitHub Pages in repo settings to that branch. The apex domain is already verified at org level; DNS checks work immediately.