Skip to content

Site fixes: serve at domain root, SEO baseline, docs mobile UX, and cleanup - #120

Merged
guanzhousongmicrosoft merged 10 commits into
documentdb:mainfrom
GuanzhouSong:site-improvements
Jul 30, 2026
Merged

Site fixes: serve at domain root, SEO baseline, docs mobile UX, and cleanup#120
guanzhousongmicrosoft merged 10 commits into
documentdb:mainfrom
GuanzhouSong:site-improvements

Conversation

@GuanzhouSong

@GuanzhouSong GuanzhouSong commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

One combined PR covering ten self-contained commits (each individually revertable). Every finding was verified against the live site, the source, or the documentdb/docs content before changing anything. (The four URL-correctness fixes this batch originally included — domain-root serving, trailing slashes, stale-artifact cleanup, robots.txt — already merged separately as #106#109; this PR contains everything that came after.)

SEO baseline

  • Build-time sitemap.xml from a dependency-free Node script, advertised from robots.txt — tested locally against a mock out/ tree (correct URL set incl. $avg-style reference pages, excludes _next/deb/rpm/packages, idempotent robots append, refuses to write an empty sitemap so a broken export fails the build).
  • Canonical URLs + metadataBase sitewide, real titles/descriptions for /packages, /samples, and /docs (previously all shipped the generic homepage metadata), og:type corrected (website default, article for docs/reference content).
  • Blog social cards + Atom feed: the Jekyll layout had zero og:/twitter: tags (shared posts render bare links); adds them (cover image when present, social card otherwise) plus a Liquid-template Atom feed at /blogs/feed.xml — a template rather than jekyll-feed so Gemfile.lock stays untouched. The feed sets layout: null to opt out of the site-wide layout default.
  • Branded 404 page replacing the unbranded Next.js default (emitted as out/404.html, which GitHub Pages serves automatically).

Docs UX

  • Responsive docs + reference sidebars. Both layouts render a fixed 320px sidebar with no breakpoint — on a 375px phone, content gets a sliver. Sidebar is now desktop-only with a no-JS <details> navigation on mobile. Also removes the accidental second h1 (the sidebar section label) — the article's real h1 comes from the markdown.
  • Internal docs links no longer open new tabs. The Markdown renderer put target="_blank" on every link, so each hop through the quick-start guides spawned a tab; now only external http(s) links do.
  • Reference listing pages get a real h1 (index/type/category pages started at h2; entry pages already get an h1 from their markdown — verified in documentdb/docs).
  • Reference descriptions actually italicfont-italic isn't a Tailwind utility; the class was silently dropped.

Cleanup

  • Five unreferenced images deleted (Google Cloud ×2, Neon, Oracle, 203 KB alt logo) — zero references in this repo or anywhere in the documentdb org (GitHub code search, plain and %20-encoded forms).
  • Discord invite centralized — was hardcoded in three files; now one constant per stack (externalLinks.ts / site.discord_url), verified by grep that only the two definition sites remain.

Validation

🤖 Generated with Claude Code

https://claude.ai/code/session_01XGMeNSmhAgmqzkdc7cQQgf

The Markdown renderer put target="_blank" on every link in docs and
reference content, so following an internal cross-link like the Mongo
Shell Quick Start from the Docker guide spawned a new tab on every hop.
The quick starts are chains of internal links, which made normal docs
reading accumulate tabs.

Only links with an http(s) scheme now open in a new tab with
rel="noopener noreferrer"; site-internal paths and in-page anchors
navigate in place.
GoogleCloudLogo.png, GoogleCloudAltLogo.png, NeonLogo.png,
OracleLogo.png, and 'DocumentDB - Logo next to text.png' (203 KB) are
referenced nowhere - not in this repository, and not in any repository
in the documentdb organization (checked GitHub code search for both the
plain and the %20-encoded filename forms). They ship in every deploy as
dead weight, and stray Google Cloud / Oracle / Neon logos in the images
directory invite the wrong conclusions about project affiliations.
The discord.gg/vH7bYu524D invite was hardcoded in three places: the
Next.js navbar, the VS Code quick-start guide content, and the Jekyll
blog layout. If the invite ever expires or rotates, each copy has to be
found and updated separately.

The Next.js app now reads it from app/services/externalLinks.ts (which
already exists for exactly this purpose), and the Jekyll side reads
site.discord_url from blogs/_config.yml, leaving one definition per
stack with cross-references in comments.
The description column in the reference list grid used font-italic,
which is not a Tailwind utility (the font-style utilities are italic
and not-italic), so the class was silently dropped and the text
rendered upright. Use the real utility.
The MQL reference index, type, and category pages used an h2 as their
top-level heading, leaving those pages with no h1 at all. Individual
reference entries are fine - their h1 comes from the compiled markdown
(each entry in documentdb/docs starts with an h1 title) - so only the
three listing pages change. Styling classes are unchanged; the
_metadata.description.md blurbs rendered below are plain paragraphs
with no headings, so no heading-order conflict is introduced.
Search engines currently get no canonical link on any page, no
metadataBase, and three high-intent pages (/packages, /samples, /docs)
ship the generic homepage title and description because they are client
components with no metadata of their own. The homepage also declared
og:type article.

- metadataService gains a siteUrl constant, metadataBase, an optional
  path that becomes the canonical URL, and an og type parameter that
  defaults to website (article is used for docs and reference content).
- /packages and /samples get metadata via route layouts (the pages are
  client components); /docs derives its metadata from content.yml.
- Docs articles and all reference pages now emit canonical URLs and
  og/twitter tags with their real titles instead of inheriting the
  root layout's generic ones.

Canonical paths use the trailing-slash form to match the URL shape the
site serves once trailingSlash lands.
The live site serves a 404 for /sitemap.xml, so crawlers have to
discover several hundred documentation and reference pages by link
walking alone.

A dependency-free Node script runs as the final build step, after the
Next.js export and the Jekyll blogs build have both written into out/.
It emits one sitemap entry per exported page (any directory containing
an index.html, the shape produced by trailingSlash and Jekyll's pretty
permalinks), skips non-page outputs (_next, deb, rpm, packages, images,
404), and appends the Sitemap directive to out/robots.txt, creating a
minimal robots.txt when public/ does not provide one. It refuses to
write an empty sitemap so a broken export fails the build instead of
shipping silently.
Unknown URLs currently land on the unbranded Next.js default 404 (a
bare 'This page could not be found'). With the static export, a root
app/not-found.tsx is emitted as out/404.html, which GitHub Pages serves
for any unknown path.

The new page matches the site's dark styling and routes visitors to
the places a lost visitor most likely wants: docs, downloads, samples,
home, and an issue link for reporting broken links.
Both docs layouts render a fixed 320px sidebar with no responsive
breakpoint, so on a 375px phone the sidebar consumes most of the
viewport and article content is squeezed into a sliver. Documentation
is the page mobile visitors from link aggregators land on most.

The sidebar is now desktop-only (hidden below md), and mobile gets a
native details/summary disclosure above the article with the same
navigation links - no client JS involved, so it works identically in
the static export. The article page's navigation markup is computed
once and rendered in both places.

Also demotes the sidebar's section label from h1 to p: the article's
real h1 comes from the markdown content, so docs pages previously
shipped two h1s, and the sidebar copy would have disappeared on mobile.
Content padding tightens to p-4 on phones.
The Jekyll layout's head had only title, description, and theme-color:
sharing any blog post on X, LinkedIn, or Discord rendered a bare link
with no card, and the blog had no feed for aggregators or readers.

- og:/twitter: tags in the shared layout, using the post's cover image
  when present and the site social card otherwise, with og:type article
  on dated posts and website elsewhere.
- A hand-rolled Atom feed at /blogs/feed.xml built from a Liquid
  template, advertised via a rel=alternate link. A template instead of
  jekyll-feed avoids a Gemfile.lock regeneration, which cannot be done
  on machines without local bundler access.
- site.url is now set, which absolute_url and the feed's permanent
  entry IDs both require.

The feed opts out of the site-wide layout default with layout: null;
without it the config's defaults would wrap the XML in the HTML shell.
@guanzhousongmicrosoft
guanzhousongmicrosoft merged commit 30266dc into documentdb:main Jul 30, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants