Docs Infrastructure

The Crossplane document website is in a standalone GitHub repository separate from Crossplane core.

The Crossplane docs tools consist of:

  • Netlify - web hosting and DNS provided by the CNCF.
  • Hugo - to compile markdown to static HTML.
  • Bootstrap - for pre-built CSS options.
  • PostCSS - for CSS optimization.
  • Webpack - for Javascript optimization.

Netlify

Builds for production deploys and PR previews are automatically done by Netlify.

Note
The CNCF controls Netlify access.

Settings for Netlify are inside netlify.toml.

Settings inside the netlify.toml file override any settings in the Netlify web interface.

The build directive defines what Netlify does to build the site.

The HUGO_VERSION settings defines which version of Hugo Netlify uses.

The redirects are server side HTTP redirects for moved pages.

The Netlify documentation has more information about configuring netlify.toml.

Netlify automatically detects the package.json file and loads the listed NodeJS dependencies.

netlify_build.sh

During a build Netlify runs the Bash script netlify_build.sh.

The script creates a new docs section called latest and copies the defined LATEST_VER to /latest.

Next the script enables writeStats in the Hugo configuration file.

Then, using the Netlify $CONTEXT environmental variable Hugo runs, defining the BaseURL to use for generating internal links.

Hugo

Crossplane uses Hugo, a static site generator.

Hugo combines HTML templates, markdown content and generates static HTML content.

Note

The Hugo web server is only used for local development. Crossplane documentation uses Netlify for hosting.

Hugo only acts as an HTML compiler.

Hugo influences the directory structure of the repository.

The /content directory is the root directory for all documentation content.

The /themes/geekboot directory is the root directory for all website related files, like HTML templates, shortcodes and global media files.

The /utils/ directory is for JavaScript source code and files unrelated to Hugo used in the website.

CSS

Crossplane documentation uses Bootstrap 5.2.

Bootstrap provides multiple prebuilt styles and features making CSS easier.

The docs import all Bootstrap SCSS files and rely on Hugo and PostCSS to optimize the compiled CSS file.

The unmodified Bootstrap SCSS files are in /themes/geekboot/assets/scss/bootstrap/.

Any docs-specific overrides are in per-element SCSS files located one directory higher in /themes/geekboot/assets/scss/.

Important
Don’t edit the original Bootstrap stylesheets. It makes the ability to upgrade to future Bootstrap versions difficult or impossible.

The file /themes/geekboot/assets/scss/docs.scss defines all the stylesheets Hugo loads and compiles. Add any new styles to the docs.scss file to include them.

Color themes

Crossplane docs support a light and dark color theme that’s applied via CSS variables.

Universal and default variables are defined in /themes/geekboot/assets/scss/_variables.scss.

Provide theme specific color overrides in /themes/geekboot/assets/scss/light-mode.scss or /themes/geekboot/assets/scss/dark-mode.scss.

Note
When creating new styles, use variables for any colors, even if both themes share the color.

SCSS compilation

Hugo compiles the SCSS to CSS. Local development doesn’t require SCSS installed.

For local development (when using hugo server) Hugo compiles SCSS without any optimizations.

In production, when publishing on Netlify or using hugo server --environment production, Hugo compiles SCSS and optimizes the CSS with PostCSS.

The PostCSS configuration is in /postcss.config.js.

The optimizations includes:

How optimization works

Crossplane runs a different Hugo CSS command if it’s in local development or production.

Hugo is in “production” when using hugo to only build HTML or with hugo server --environment production.

Important

Running Hugo in production mode requires the Hugo extended version to support PostCSS.

Standard Hugo fails to build the documentation.

PurgeCSS relies on a JSON file of every HTML tag and CSS class used across the website and only preserves the matching CSS styles. The resulting file is around 20x smaller than unoptimized CSS.

Hugo generates the JSON file with the buildStats (or writeStats) configuration setting enabled.

Important

Some tags or classes are dynamically created or not always enabled, like light or dark mode.

Exclude these style sheets from CSS optimization with the purgecss start ignore comment.

For example, the Crossplane documentation ignores the color-modes style sheet.

1/* purgecss start ignore */
2@import "color-modes";
3/* purgecss end ignore */

The Crossplane documentation only enables this flag during Netlify builds. Manually update the config.yaml file to enable local optimization.

Optimizing CSS locally with PostCSS requires installing extra packages.

  • Sass
  • NPM
  • NPM packages defined in /package.json with npm install.

JavaScript

A goal of the documentation website is to use as little JavaScript as possible. Unless the script provides a significant improvement in performance, capability or user experience.

Local development has no run-time JavaScript dependencies. To prevent dependencies, making JavaScript changes requires compiling and committing to git.

The source JavaScript is in /utils/webpack/src/js and requires Webpack to bundle and optimize the code.

Webpack places the compiled JavaScript in /themes/geekboot/assets/js/ and updates /themes/geekboot/data/assets.json to tell Hugo the new compiled JavaScript filename.

Important
The JavaScript source in /utils/webpack/src, newly compiled JavaScript in /themes/geekboot/assets/js and updated /themes/geekboot/data/assets.json must be in git for production and preview deploys to use the changed JavaScript.

JavaScript files

  • bootstrap/ is the entire Bootstrap JavaScript library.
  • colorMode.js provides the ability to change the light and dark mode color theme.
  • customClipboard.js supports the copy-lines code box function.
  • globalScripts.js is the point of entry for Webpack to determine all dependencies. This bundles instant.page and Bootstrap’s JavaScript.
  • hoverHighlight.js provides dynamic “hover to highlight” function.
  • slackNotify.js creates the “Join Crossplane Slack” bubble on the home page.
  • tabDeepAnchor.js rewrites anchor links inside tabs to open a tab and present the anchor.

The globalScripts.js file is the entry point for Webpack. Any JavaScript modules or scripts must be in globalScripts.js to get compiled.

Building JavaScript

Requirements:

From the /utils/webpack director use npm to install the NodeJS dependencies.

1cd utils/webpack
2npm install

Building JavaScript has two options:

  1. npm run dev
  2. npm run prod

Using the dev argument doesn’t optimize the JavaScript, simplifying debugging.

Using the prod argument optimizes the JavaScript but makes debugging more difficult.

Important
Always use npm run prod to build the compiled JavaScript to use on the Crossplane documentation site.
 1npm run prod
 2
 3> prod
 4> webpack --mode=production
 5
 6assets by status 80.9 KiB [cached] 1 asset
 7asset ../../data/assets.json 158 bytes [compared for emit]
 8orphan modules 180 KiB [orphan] 81 modules
 9runtime modules 670 bytes 3 modules
10cacheable modules 223 KiB
11  modules by path ./src/js/*.js 186 KiB
12    ./src/js/globalScripts.js + 81 modules 181 KiB [built] [code generated]
13    ./src/js/colorMode.js 2.69 KiB [built] [code generated]
14    ./src/js/tabDeepAnchor.js 2.31 KiB [built] [code generated]
15  modules by path ./node_modules/ 37.6 KiB
16    ./node_modules/instant.page/instantpage.js 11.4 KiB [built] [code generated]
17    ./node_modules/clipboard/dist/clipboard.js 26.2 KiB [built] [code generated]
18webpack 5.89.0 compiled successfully in 1248 ms

Algolia provides search functionality through their DocSearch program.

Note
Crossplane docs don’t use the Netlify Algolia plugin and relies on the Algolia Crawler to discover new content and changes.

Sitemap and robots

Hugo generates the robots.txt automatically.

For search engine discovery Crossplane uses a sitemap.xml file generated from the sitemap.xml template.

Crossplane checks links with Hugo and htmltest.

Hugo builds fail for any broken links in a Hugo ref shortcode.

To catch markdown links htmltest crawls rendered HTML and validates all internal links.

On Monday a GitHub Action runs to validate external links with htmltest.

The configuration for htmltest is in /utils/htmlstest.

Annotated website tree

Expand the tab below to see an annotated tree output of the website repository.

  1├── config.yaml    # Hugo configuration file
  2├── content        # Root for all page content
  3│   ├── contribute
  4│   ├── master
  5│   ├── media      # Images used in docs pages
  6│   ├── v1.13
  7│   ├── v1.14
  8│   └── v1.15
  9├── hugo_stats.json   # Generated by Hugo writeStats for PurgeCSS
 10├── netlify.toml      # Netlify configuration
 11├── netlify_build.sh  # Custom build script for Netlify
 12├── package-lock.json # NodeJS dependency version lock
 13├── package.json      # NodeJS dependencies
 14├── postcss.config.js # PostCSS configuration
 15├── static            # Legacy docs site images
 16├── themes    
 17│   └── geekboot      # The Hugo theme used by Crossplane
 18│       ├── LICENSE-bootstrap
 19│       ├── LICENSE-geekdoc
 20│       ├── assets
 21│       │   ├── js    # Compiled JavaScript
 22│       │   └── scss  # Sytlesheets
 23│       │       └── bootstrap # Unmodified Bootstrap 5.2 SCSS
 24│       ├── data      # Hugo mapping for JavaScript files. Autogenerated.
 25│       ├── layouts   # HTML template pages
 26│       │   ├── 404.html  # 404 page template
 27│       │   ├── _default
 28│       │   │   ├── _markup/ # Templates for rendering specific style components
 29│       │   │   ├── baseof.html        # Entrypoint template for all pages
 30│       │   │   ├── list.html          # List type pages, see partials/single-list.html
 31│       │   │   ├── redirect.html      # Provides HTML redirect functions
 32│       │   │   ├── section.rss.xml    # RSS feed template
 33│       │   │   ├── single.html        # Single type pages, see partials/single-list.html
 34│       │   │   └── sitemap.xml        # Sitemap template
 35│       │   ├── partials  # Template includes
 36│       │   │   ├── analytics.html  # Analytics and trackers
 37│       │   │   ├── crds.html       # Entrypoint for API documentation
 38│       │   │   ├── docs-navbar.html  # Top header links
 39│       │   │   ├── docs-sidebar.html # left-side navigation menu
 40│       │   │   ├── favicons.html     # Favicons
 41│       │   │   ├── feature-state-alert.html  # Alert box for alpha/beta features
 42│       │   │   ├── footer.html       # Footer copyright and links
 43│       │   │   ├── ga-tag.html       # Google Analytics
 44│       │   │   ├── google-analytics.html # Notice for GA release version
 45│       │   │   ├── header.html       # <head></head> content
 46│       │   │   ├── icons             # Icons from fontawesome and Crossplane specific
 47│       │   │   ├── icons.html        # SVG includes common enough to be on every page
 48│       │   │   ├── left-nav.html     # Left-hand navigation
 49│       │   │   ├── master-version-alert.html   # Alert box for the master version
 50│       │   │   ├── mermaid.html      # Styling and JavaScript for mermaid diagrams
 51│       │   │   ├── meta-common.html  # <meta> tags used on all pages
 52│       │   │   ├── ms-clarity.html   # Microsoft Clarity tags
 53│       │   │   ├── old-version-alert.html  # Alert box for versions that aren't the latest
 54│       │   │   ├── redirect.html     # HTML meta redirect
 55│       │   │   ├── release-notes.html  # Release note summary page generator
 56│       │   │   ├── rollworks.html    # Rollworks analytics tags
 57│       │   │   ├── scripts.html      # Global JavaScript includes
 58│       │   │   ├── search-button.html  # Algolia search button
 59│       │   │   ├── sidebar           # Static links in the left-side nav
 60│       │   │   ├── single-list.html  # Template used by all single and list type pages
 61│       │   │   ├── skippy.html       # Shift the page when the target is an anchor link
 62│       │   │   ├── social.html       # Social media data includes
 63│       │   │   ├── stylesheet-cached.html  # Static CSS that never changes
 64│       │   │   ├── stylesheet-dynamic.html # Dynamic CSS that may change between pages
 65│       │   │   ├── toc.html          # Table of contents modifications
 66│       │   │   ├── utils             # Utils imported from Geekdoc theme
 67│       │   │   └── version-dropdown-menu.html  # Version dropdown menu
 68│       │   └── shortcodes
 69│       │       ├── check.html        # Produce and style a Checkmark
 70│       │       ├── editCode.html     # Code box with editable field
 71│       │       ├── expand.html       # Expand button
 72│       │       ├── getCRDs.html      # Generate API pages
 73│       │       ├── hint.html         # Hint boxes
 74│       │       ├── hover.html        # Hover to highlight 
 75│       │       ├── img.html          # Image optimizer
 76│       │       ├── include.html      # Include an external file
 77│       │       ├── markdown.html     # Run content through the markdown engine again
 78│       │       ├── param.html        # Import from Bootstrap theme
 79│       │       ├── partial.html      # Import from Bootstrap theme
 80│       │       ├── placeholder.html  # Import from Bootstrap theme
 81│       │       ├── propertylist.html # Import from Bootstrap theme
 82│       │       ├── tab.html          # Individual Tab. Related to tabs.html
 83│       │       ├── table.html        # Apply bootstrap styles to markdown tables
 84│       │       ├── tabs.html         # Tab builder, related to tab.html
 85│       │       ├── url.html          # Create a download link to a file. Used by the APIs
 86│       │       └── year.html         # Print the current year
 87│       └── static       # Static global image files
 88└── utils   # Scripts and tools related to the docs
 89    ├── htmltest  # htmltest link checker
 90    ├── vale      # Vale linter
 91    │   └── styles
 92    │       ├── Crossplane  # Crossplane spelling exceptions
 93    │       ├── Google      # Google's Vale rules
 94    │       ├── Microsoft   # Microsoft's Vale rules
 95    │       ├── alex        # Write inclusive language
 96    │       ├── gitlab      # Gitlab's Vale rules
 97    │       ├── proselint   # Write better
 98    │       └── write-good  # Write better
 99    └── webpack   # JavaScript tools
100        ├── package-lock.json # NodeJS dependency version lock
101        ├── package.json      # NodeJS dependencies
102        ├── src 
103        │   └── js
104        │       ├── bootstrap/          # Unmodified Bootstrap JavaScript
105        │       ├── colorMode.js        # Color mode switcher
106        │       ├── customClipboard.js  # Custom copy-to-clipboard tool
107        │       ├── globalScripts.js    # Point of entry for all scripts compiled by Webpack
108        │       ├── hoverHighlight.js   # Hover to highlight 
109        │       ├── slackNotify.js      # "Join Crossplane Slack" bubble
110        │       └── tabDeepAnchor.js    # Link inside a tab
111        └── webpack.config.js