--- title: Docs Infrastructure weight: 600 description: "Learn how the docs and our tools are built" --- The Crossplane document website is in a standalone [GitHub repository](https://github.com/crossplane/docs) separate from Crossplane core. The Crossplane docs tools consist of: * [Netlify](https://netlify.com/) - web hosting and DNS provided by the CNCF. * [Hugo](https://gohugo.io/) - to compile markdown to static HTML. * [Bootstrap](https://getbootstrap.com/docs/5.2/) - for pre-built CSS options. * [PostCSS](https://postcss.org/) - for CSS optimization. * [Webpack](https://webpack.js.org/) - for Javascript optimization. ## Netlify Builds for production deploys and PR previews are automatically done by Netlify. {{< hint "note" >}} The CNCF controls Netlify access. {{< /hint >}} Settings for Netlify are inside [`netlify.toml`](https://github.com/crossplane/docs/blob/master/netlify.toml). Settings inside the `netlify.toml` file override any settings in the Netlify web interface. The [`build`](https://github.com/crossplane/docs/blob/3e9e10671c32e368f5381d83e406e16bc38c93bc/netlify.toml#L1) directive defines what Netlify does to build the site. The [`HUGO_VERSION`](https://github.com/crossplane/docs/blob/3e9e10671c32e368f5381d83e406e16bc38c93bc/netlify.toml#L7) settings defines which version of Hugo Netlify uses. The [`redirects`](https://github.com/crossplane/docs/blob/3e9e10671c32e368f5381d83e406e16bc38c93bc/netlify.toml#L9) are server side HTTP redirects for moved pages. The [Netlify documentation](https://docs.netlify.com/configure-builds/file-based-configuration/) has more information about configuring `netlify.toml`. Netlify automatically detects the [`package.json`](https://github.com/crossplane/docs/blob/master/package.json) file and loads the listed NodeJS dependencies. ### netlify_build.sh During a build Netlify runs the Bash script [`netlify_build.sh`](https://github.com/crossplane/docs/blob/master/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](https://docs.netlify.com/configure-builds/environment-variables/#build-metadata) Hugo runs, defining the [`BaseURL`](https://gohugo.io/methods/site/baseurl/) to use for generating internal links. ## Hugo Crossplane uses [Hugo](https://gohugo.io/), a static site generator. Hugo combines HTML templates, markdown content and generates static HTML content. {{}} The Hugo web server is only used for local development. Crossplane documentation uses Netlify for hosting. Hugo only acts as an HTML compiler. {{< /hint >}} 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](https://getbootstrap.com/docs/5.2/getting-started/introduction/). 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/`. {{}} Don't edit the original Bootstrap stylesheets. It makes the ability to upgrade to future Bootstrap versions difficult or impossible. {{< /hint >}} 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`. {{}} When creating new styles, use variables for any colors, even if both themes share the color. {{< /hint >}} ### 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](https://postcss.org/). The PostCSS configuration is in `/postcss.config.js`. The optimizations includes: * [postcss-lightningcss](https://github.com/onigoetz/postcss-lightningcss) - for building, minimizing and generating a source map. * [PurgeCSS](https://purgecss.com/plugins/postcss.html) - removes unused styles to reduce the CSS file size. * [postcss-sort-media-queries](https://github.com/yunusga/postcss-sort-media-queries)- to organize the output CSS for another small performance boost. #### How optimization works Crossplane runs a [different Hugo CSS command](https://github.com/crossplane/docs/blob/3e9e10671c32e368f5381d83e406e16bc38c93bc/themes/geekboot/layouts/partials/stylesheet-dynamic.html#L4) 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`. {{}} Running Hugo in `production` mode requires the Hugo _extended_ version to support PostCSS. Standard Hugo fails to build the documentation. {{< /hint >}} 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](https://gohugo.io/getting-started/configuration/#configure-build) enabled. {{}} 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. ```css /* purgecss start ignore */ @import "color-modes"; /* purgecss end ignore */ ``` {{< /hint >}} 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](https://sass-lang.com/install) * [NPM](https://www.npmjs.com/) * 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](https://webpack.js.org/) 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. {{< hint "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. {{< /hint >}} ### 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](https://instant.page/) and [Bootstrap's JavaScript](https://getbootstrap.com/docs/5.2/getting-started/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: * [NodeJS](https://nodejs.org/en/download) v20.1.2 or later. * [NPM CLI](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) From the `/utils/webpack` director use `npm` to install the NodeJS dependencies. ```shell cd utils/webpack npm 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. {{}} Always use `npm run prod` to build the compiled JavaScript to use on the Crossplane documentation site. {{< /hint >}} ```shell npm run prod > prod > webpack --mode=production assets by status 80.9 KiB [cached] 1 asset asset ../../data/assets.json 158 bytes [compared for emit] orphan modules 180 KiB [orphan] 81 modules runtime modules 670 bytes 3 modules cacheable modules 223 KiB modules by path ./src/js/*.js 186 KiB ./src/js/globalScripts.js + 81 modules 181 KiB [built] [code generated] ./src/js/colorMode.js 2.69 KiB [built] [code generated] ./src/js/tabDeepAnchor.js 2.31 KiB [built] [code generated] modules by path ./node_modules/ 37.6 KiB ./node_modules/instant.page/instantpage.js 11.4 KiB [built] [code generated] ./node_modules/clipboard/dist/clipboard.js 26.2 KiB [built] [code generated] webpack 5.89.0 compiled successfully in 1248 ms ``` ## Search [Algolia](https://www.algolia.com/) provides search functionality through their [DocSearch](https://docsearch.algolia.com/) program. {{< hint note >}} Crossplane docs don't use the Netlify Algolia plugin and relies on the [Algolia Crawler](https://www.algolia.com/doc/tools/crawler/getting-started/overview/) to discover new content and changes. {{< /hint >}} ## Sitemap and robots Hugo generates the `robots.txt` [automatically](https://gohugo.io/templates/robots/). For search engine discovery Crossplane uses a `sitemap.xml` file generated from the [`sitemap.xml` template](https://github.com/crossplane/docs/blob/master/themes/geekboot/layouts/_default/sitemap.xml). ## Link checking Crossplane checks links with Hugo and [`htmltest`](https://github.com/wjdp/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](https://github.com/crossplane/docs/blob/master/.github/workflows/weekly-link-checker.yml) runs to validate external links with `htmltest`. The configuration for `htmltest` is in [`/utils/htmlstest`](https://github.com/crossplane/docs/blob/master/utils/htmltest/.htmltest.yml). ## Annotated website tree Expand the tab below to see an annotated `tree` output of the website repository. {{}} ```shell ├── config.yaml # Hugo configuration file ├── content # Root for all page content │   ├── contribute │   ├── master │   ├── media # Images used in docs pages │   ├── v1.13 │   ├── v1.14 │   └── v1.15 ├── hugo_stats.json # Generated by Hugo writeStats for PurgeCSS ├── netlify.toml # Netlify configuration ├── netlify_build.sh # Custom build script for Netlify ├── package-lock.json # NodeJS dependency version lock ├── package.json # NodeJS dependencies ├── postcss.config.js # PostCSS configuration ├── static # Legacy docs site images ├── themes │   └── geekboot # The Hugo theme used by Crossplane │   ├── LICENSE-bootstrap │   ├── LICENSE-geekdoc │   ├── assets │   │   ├── js # Compiled JavaScript │   │   └── scss # Sytlesheets │   │   └── bootstrap # Unmodified Bootstrap 5.2 SCSS │   ├── data # Hugo mapping for JavaScript files. Autogenerated. │   ├── layouts # HTML template pages │   │   ├── 404.html # 404 page template │   │   ├── _default │   │   │   ├── _markup/ # Templates for rendering specific style components │   │   │   ├── baseof.html # Entrypoint template for all pages │   │   │   ├── list.html # List type pages, see partials/single-list.html │   │   │   ├── redirect.html # Provides HTML redirect functions │   │   │   ├── section.rss.xml # RSS feed template │   │   │   ├── single.html # Single type pages, see partials/single-list.html │   │   │   └── sitemap.xml # Sitemap template │   │   ├── partials # Template includes │   │   │   ├── analytics.html # Analytics and trackers │   │   │   ├── crds.html # Entrypoint for API documentation │   │   │   ├── docs-navbar.html # Top header links │   │   │   ├── docs-sidebar.html # left-side navigation menu │   │   │   ├── favicons.html # Favicons │   │   │   ├── feature-state-alert.html # Alert box for alpha/beta features │   │   │   ├── footer.html # Footer copyright and links │   │   │   ├── ga-tag.html # Google Analytics │   │   │   ├── google-analytics.html # Notice for GA release version │   │   │   ├── header.html # content │   │   │   ├── icons # Icons from fontawesome and Crossplane specific │   │   │   ├── icons.html # SVG includes common enough to be on every page │   │   │   ├── left-nav.html # Left-hand navigation │   │   │   ├── master-version-alert.html # Alert box for the master version │   │   │   ├── mermaid.html # Styling and JavaScript for mermaid diagrams │   │   │   ├── meta-common.html # tags used on all pages │   │   │   ├── ms-clarity.html # Microsoft Clarity tags │   │   │   ├── old-version-alert.html # Alert box for versions that aren't the latest │   │   │   ├── redirect.html # HTML meta redirect │   │   │   ├── release-notes.html # Release note summary page generator │   │   │   ├── rollworks.html # Rollworks analytics tags │   │   │   ├── scripts.html # Global JavaScript includes │   │   │   ├── search-button.html # Algolia search button │   │   │   ├── sidebar # Static links in the left-side nav │   │   │   ├── single-list.html # Template used by all single and list type pages │   │   │   ├── skippy.html # Shift the page when the target is an anchor link │   │   │   ├── social.html # Social media data includes │   │   │   ├── stylesheet-cached.html # Static CSS that never changes │   │   │   ├── stylesheet-dynamic.html # Dynamic CSS that may change between pages │   │   │   ├── toc.html # Table of contents modifications │   │   │   ├── utils # Utils imported from Geekdoc theme │   │   │   └── version-dropdown-menu.html # Version dropdown menu │   │   └── shortcodes │   │   ├── check.html # Produce and style a Checkmark │   │   ├── editCode.html # Code box with editable field │   │   ├── expand.html # Expand button │   │   ├── getCRDs.html # Generate API pages │   │   ├── hint.html # Hint boxes │   │   ├── hover.html # Hover to highlight │   │   ├── img.html # Image optimizer │   │   ├── include.html # Include an external file │   │   ├── markdown.html # Run content through the markdown engine again │   │   ├── param.html # Import from Bootstrap theme │   │   ├── partial.html # Import from Bootstrap theme │   │   ├── placeholder.html # Import from Bootstrap theme │   │   ├── propertylist.html # Import from Bootstrap theme │   │   ├── tab.html # Individual Tab. Related to tabs.html │   │   ├── table.html # Apply bootstrap styles to markdown tables │   │   ├── tabs.html # Tab builder, related to tab.html │   │   ├── url.html # Create a download link to a file. Used by the APIs │   │   └── year.html # Print the current year │   └── static # Static global image files └── utils # Scripts and tools related to the docs ├── htmltest # htmltest link checker ├── vale # Vale linter │   └── styles │   ├── Crossplane # Crossplane spelling exceptions │   ├── Google # Google's Vale rules │   ├── Microsoft # Microsoft's Vale rules │   ├── alex # Write inclusive language │   ├── gitlab # Gitlab's Vale rules │   ├── proselint # Write better │   └── write-good # Write better └── webpack # JavaScript tools ├── package-lock.json # NodeJS dependency version lock ├── package.json # NodeJS dependencies ├── src │   └── js │   ├── bootstrap/ # Unmodified Bootstrap JavaScript │   ├── colorMode.js # Color mode switcher │   ├── customClipboard.js # Custom copy-to-clipboard tool │   ├── globalScripts.js # Point of entry for all scripts compiled by Webpack │   ├── hoverHighlight.js # Hover to highlight │   ├── slackNotify.js # "Join Crossplane Slack" bubble │   └── tabDeepAnchor.js # Link inside a tab └── webpack.config.js ``` {{}}