Skip to main content

One documentation site, two front doors

Β· 3 min read
Rukia Labs Engineering
The team building the platform

This site is served at two addresses:

docs.rukialabs.com Rukia Labs' own colours
yourorg.rukialabs.com/docs your organisation's colours

Same words, same pages, same build inputs. Only the branding differs β€” and, less obviously, the path everything is mounted at.

Why not just one​

An organisation's documentation should look like their product, because inside their programme it is their product. Sending a learner from a branded console to a page in somebody else's orange is a seam where there does not need to be one.

And the general site has to exist too, for everyone who has not signed in to anything yet.

The colours are the easy half​

Branding already arrives at the edge, injected into the HTML before the page runs β€” see Branding that arrives before the JavaScript does, earlier on this blog. The docs read the same value and repaint their palette from it.

The one subtlety is ordering. The injected configuration lands at the very end of <head>, so anything in the head runs too early to see it. The repaint happens in the first tag of <body> instead: after the whole head, before anything is painted. Late enough to have the data, early enough that nobody sees the default first.

A version of this that ran at hydration would also be correct, eventually, and would flash on every page load β€” which is the whole thing we were avoiding.

The path is the hard half​

A static site generator compiles its base path into everything: every link, every asset URL, the client router's notion of where the root is. Ours is no exception.

That makes one build physically unable to answer at both mounts. Served under /docs, a build made for / emits a link to /guides/intro β€” and the first click walks the reader out of the documentation and into the learner application, which has never heard of that route.

Rewriting the HTML at the edge does not save it either. You can re-base the href in the markup; you cannot re-base the base path already compiled into the router bundle. The first click is client-side and never reaches the edge at all.

So it is two builds of one directory, differing in a single variable. Same content, same theme, same everything β€” two artifacts, because the mount point is a build input rather than a runtime one.

The bit we nearly got wrong​

Two copies of identical content on different hostnames is a duplicate-content problem. Left indexable, an organisation's copy competes with the general site for the same queries, and search engines pick a winner we did not choose.

The per-organisation mounts are noindex. They are for the people already inside that organisation; the copy meant for everyone else is the one that gets indexed.

Not a deep insight, but the kind of thing that is free to get right on the day and expensive to notice six months later.