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│       │   │   ├── preview-version-alert.html  # Alert box for preview versions
 55│       │   │   ├── redirect.html     # HTML meta redirect
 56│       │   │   ├── release-notes.html  # Release note summary page generator
 57│       │   │   ├── rollworks.html    # Rollworks analytics tags
 58│       │   │   ├── scripts.html      # Global JavaScript includes
 59│       │   │   ├── search-button.html  # Algolia search button
 60│       │   │   ├── sidebar           # Static links in the left-side nav
 61│       │   │   ├── single-list.html  # Template used by all single and list type pages
 62│       │   │   ├── skippy.html       # Shift the page when the target is an anchor link
 63│       │   │   ├── social.html       # Social media data includes
 64│       │   │   ├── stylesheet-cached.html  # Static CSS that never changes
 65│       │   │   ├── stylesheet-dynamic.html # Dynamic CSS that may change between pages
 66│       │   │   ├── toc.html          # Table of contents modifications
 67│       │   │   ├── utils             # Utils imported from Geekdoc theme
 68│       │   │   └── version-dropdown-menu.html  # Version dropdown menu
 69│       │   └── shortcodes
 70│       │       ├── check.html        # Produce and style a Checkmark
 71│       │       ├── editCode.html     # Code box with editable field
 72│       │       ├── expand.html       # Expand button
 73│       │       ├── getCRDs.html      # Generate API pages
 74│       │       ├── hint.html         # Hint boxes
 75│       │       ├── hover.html        # Hover to highlight 
 76│       │       ├── img.html          # Image optimizer
 77│       │       ├── include.html      # Include an external file
 78│       │       ├── markdown.html     # Run content through the markdown engine again
 79│       │       ├── param.html        # Import from Bootstrap theme
 80│       │       ├── partial.html      # Import from Bootstrap theme
 81│       │       ├── placeholder.html  # Import from Bootstrap theme
 82│       │       ├── propertylist.html # Import from Bootstrap theme
 83│       │       ├── tab.html          # Individual Tab. Related to tabs.html
 84│       │       ├── table.html        # Apply bootstrap styles to markdown tables
 85│       │       ├── tabs.html         # Tab builder, related to tab.html
 86│       │       ├── url.html          # Create a download link to a file. Used by the APIs
 87│       │       └── year.html         # Print the current year
 88│       └── static       # Static global image files
 89└── utils   # Scripts and tools related to the docs
 90    ├── htmltest  # htmltest link checker
 91    ├── vale      # Vale linter
 92    │   └── styles
 93    │       ├── Crossplane  # Crossplane spelling exceptions
 94    │       ├── Google      # Google's Vale rules
 95    │       ├── Microsoft   # Microsoft's Vale rules
 96    │       ├── alex        # Write inclusive language
 97    │       ├── gitlab      # Gitlab's Vale rules
 98    │       ├── proselint   # Write better
 99    │       └── write-good  # Write better
100    └── webpack   # JavaScript tools
101        ├── package-lock.json # NodeJS dependency version lock
102        ├── package.json      # NodeJS dependencies
103        ├── src 
104        │   └── js
105        │       ├── bootstrap/          # Unmodified Bootstrap JavaScript
106        │       ├── colorMode.js        # Color mode switcher
107        │       ├── customClipboard.js  # Custom copy-to-clipboard tool
108        │       ├── globalScripts.js    # Point of entry for all scripts compiled by Webpack
109        │       ├── hoverHighlight.js   # Hover to highlight 
110        │       ├── slackNotify.js      # "Join Crossplane Slack" bubble
111        │       └── tabDeepAnchor.js    # Link inside a tab
112        └── webpack.config.js