* root: switch cspell to typo-only reporting cSpell's default mode reports every word it does not recognize, so each new integration name or identifier needed a dictionary entry. Switch to unknownWords: report-common-typos, which matches CodeSpell's behavior: only known misspellings (reported with a suggested fix) and flagWords fail the check. Remove the per-topic dictionaries and word-shape ignore regexes this makes unnecessary, fix the 18 typos the old allowlists were masking, and document the new workflow. Claude-Session: https://claude.ai/code/session_014ZuquWGxU5ReaXR5D3NmwW * web/docs: fix style-loader-plugin path and wording in CSS architecture doc Claude-Session: https://claude.ai/code/session_014ZuquWGxU5ReaXR5D3NmwW * website: remove stray blank lines left by ignore-comment removal Claude-Session: https://claude.ai/code/session_014ZuquWGxU5ReaXR5D3NmwW
5.9 KiB
CSS Build Pipeline
- Date: 2026-06-18
This document describes how the CSS is built in our current system, and what we can do to make that process less challenging and fraught with difficulties:
styleLoaderPlugin (./bundler/style-loader-plugin/node.js)
All of our CSS is compiled together by this ESBuild plugin.
./scripts/build-web.mjs
Calls ESBuild internally, passes it the styleLoaderPlugin configuration, and the paths to three
endpoint files ending in .global.css. Each of these represents a single product that will be
loaded by Django into a document's global stylesheet.
This script is a top-level run via npm run build.
The endpoint files:
interface.global.css: The top-level stylesheet that is used by the User and Admin applications.flows.global.css: The top-level stylesheet that is used by the Flow applicationstatic.global.css: The top-level stylesheet used by a handful of Django static pages
Each endpoint file consists of a collection of imports and overrides. static is not a huge
concern at the moment, but interface and flows matter:
Interface load pattern:
The interface.global.css file is almost entirely imports:
- @patternfly/patternfly/base/patternfly-common.css — PF reset + base element rules + utilities
- @patternfly/.../patternfly-globals.css — the --pf-global--* custom properties at :root
- @patternfly/.../patternfly-themes.css — dark-theme token reassignments
- @patternfly/.../patternfly-fa-icons.css, patternfly-pf-icons.css — icon @font-face + classes
- @patternfly/.../components/Spinner/spinner.css
- @goauthentik/fonts/faces.css — RedHat @font-face
- ./base/fonts.css
- ./base/variables.css, which in turn loads:
- colors
- colors-dark
- spacers
- icons
- shadows
- z-indexes
- borders
- miscellaneous overrides of Patternfly
- --ak-*
- --ak-v2-global-*
- ./base/scrollbars.css
- ./base/globals.css
- ./base/common.css
- ./base/placeholder.css
- #styles/locales/{ja,ko,zh}/globals.css
Flow load pattern:
The flow.global.css file duplicates (not imports!) the exact same files as interface, then adds:
- #elements/ak-drawer/ak-drawer.root.css (after the base block)
- @patternfly/.../components/BackgroundImage/background-image.css
- #elements/locale/ak-locale-select.css — imported twice (flows.global.css:24-25)
- #flow/FlowExecutor.css — which is also bundled into the shadow root via FlowExecutor.ts:11
- A large block of inline rules: the --ak-c-login--* token system, the PF4 .pf-c-login overrides (with !important on container padding, flows.global.css:139-140), and the [name="flow-links"] grid.
Layering (not with @layers):
We use the old-school mechanism of:
- source order
- selector specificity
!important.
This is known to be fragile, and our discipline around it has not been fantastic, mostly because we're trying to improve something that wasn't built with improvability at the design layer in mind.
Solutions
Using layers to raise the specificity of CSS Custom Properties enables us to get away with a cleaner and clearer idea of how our CSS is used:
On the Document:
@layer reset, legacy, theme, brand, components:
- reset: A CSS Reset for the global document
- legacy: Patternfly 4's global CSS Custom Properties and assets such as fonts & icons
- theme: Inject Authentik's CSS Custom Properties for its design system here, overriding the P4 names
- brand: Inject any customer overrides of the global properties.
- components: downstream Document-scoped look and feel for web components
The discipline of maintaining the layers in source order must be maintained; one of our personas is
"External user: Elderly volunteer for a non profit who's using a Macbook from 2016 and can't upgrade
to a version of Safari that understands @layer." The Flow application (but not User or
Admin) must support these users. 2016 (ten years ago) is considered our cutoff because it was the
first year shadowDOM v1 became "baseline newly available" and 64-bit iPads became standard.
Inside a component:
@layer reset, local:
Only the inheritable resets are inherited. If you want border-box to work inside a component, you
need to say so explicitly.
Action plan:
- De-duplicate: The exact same code is cut-and-pasted into interface, static, global, and base! Let's move all that into its own file.
- Write a top-level
document-layers.cssfile that describes the layers we're going to use. - Vendor the Patternfly top-level (but not component) stuff that we're keeping.
- Include our overrides.
At this point, this is just the rationalization of the existing styles folder. This should be a
single PR.
And then we have:
- Integrate the design-system
themebranch into this, replacing the overrides. - Fix the way
brandcss is injected, so that it comes in the right place in the cascade. Note that as long as it has the@layer brand;declaration at the top, it actually doesn't matter when it's added to the globaladoptedStylesheetcollection; its specificity will allow its CSS Custom Properties to override those of the the layers that precede in thedocument-layers.cssdeclaration. setting on the server. - Continue the work of incorporating our CSS decisions into our Elements collection
These are all side-projects and optional, but desired for the white-label capability:
After step 5:
- (Optional) Write a web component that exposes our theme as a collection of sliders, inputs, and color-pickers, and let people see what the site looks like in real-time.
- (Optional) Enable algorithmic sizing & spacing
- (Optional) Enable algorithmic sizing & spacing with viewport-relative sizes and clamps
- (Optional) Enable algorithmic color theming. See example
After step 6:
- (Optional) Enable saving the output of the web component described above into a custom brand