This is where the design-system work began: a pattern lab for Dachser — components, in-file documentation with SassDoc and JSDoc, a build pipeline and a versioned changelog, built and maintained over three years.
It is private client work, so the repository is not public; what stayed with me is the approach. Documenting components where they are written, treating the library as a product with releases, and keeping the brand decisions in tokens.
| Fact | Detail |
|---|---|
| Type | Client pattern lab (private) |
| Role | Built and maintained it, v2.18.0 |
| Scope | Components, SassDoc/JSDoc, releases |
| Period | January 2018 – November 2020 · 1,182 commits |
What's in it
SassDoc and JSDoc generated from the components themselves, so documentation could not lag behind.
Versioned with a changelog — component libraries deserve release discipline.
The experience that later became kickstartDS: same instincts, smaller scope.
In detail
What is actually inside it
Printing the changelog for a release was part of the process. Unfashionable, and it meant non-developers could read what changed.
SassDoc and JSDoc ran against the components themselves, so documentation could not lag behind the implementation the way a wiki does.
changes/, versioned releases and a CI spec: a client pattern library run like a product with a roadmap, not like a folder of components.
| Detail | Value |
|---|---|
| Scale | 1,182 commits over ~3 years |
| Documentation | SassDoc + JSDoc, in-file |
| CI | Bamboo (`bamboo-specs`) |
| Period | January 2018 – November 2020 |
Technical notes
The decisions behind it
What carried over into kickstartDS?
Why is the repository private?
Inside the library
Nine layers, ten entrypoints, one repository for several frontends
One repository served several independently released frontends: the Mediaroom, the corporate website, country sites and a microsite, plus shell, error and frame modules used by other products. Changelog entries are tagged [MR], [CW] or [CS] for exactly that reason, and changes that force integrators to touch their templates get their own Markup section instead of hiding in Fixed.
The pattern tree is atomic design mapped onto ITCSS. 00-settings and 01-tools hold variables, functions and mixins and deliberately emit no CSS; 02-generic and 03-objects are normalize plus grid primitives; the components live in 04-atoms (89 files), 05-molecules (176 — flyover, issue-bar, shortlist, track-trace, sharedlinks, country-switch, form-slider, likebar), 06-organisms (72, numbered 01-header … 18-survey-form) and then 08-templates / 09-pages.
There is deliberately no concat-all task. Every product line gets its own entrypoint under source/_modules/<name>/ that imports only what it needs, so the Mediaroom bundle never carries the career site's forms. gulp picks up module SCSS by glob; JS entrypoints additionally have to be listed in webpack.config.js — which is how frame.js, added in 2.18.0, ended up shipping its SCSS but never a JS bundle.
04-atoms-button-_button-default: compiled markup, no Pattern Lab includes, no styleguide chrome.patternExtension is hbs. The mustache styleguide kit only renders the surrounding shell — which is also the reason Pattern Lab needed patching..scss and 75 .js files in source/, before any of the exported HTML and before the changelog that describes it.What actually broke
Three years of build, browser and Pattern Lab bugs
Why is Pattern Lab patched inside node_modules?
The library ran on Pattern Lab 3 while it was still alpha (@pattern-lab/core ^3.0.0-alpha.11), and two things we needed were missing upstream.
- Custom Handlebars helpers.
@pattern-lab/engine-handlebarshad no hook to load them (the nunjucks engine did), sopatches/engine_handlebars.diffmakes itrequireour rootpatternlab-handlebars-config.js. That file is whereeq,or,calc,chooseandcardTypeClasscome from — the templates use them for card sizing and conditional markup. - A post-build hook.
patches/events.diffandpatches/index.diffadd aPATTERNLAB_BUILD_PATTERN_ENDevent.patches/ui_builder.difffixes view-all pages being named after a pattern subtype instead of the pattern itself.
All four are applied by npm run postinstall as patch -f -F 0 -p0 -i … || true — a failed patch is swallowed rather than failing the install. Getting them applied reliably took several attempts; one commit is titled, verbatim, "Monkeypatch engine-handlebars in Dockerfile instead of package.json for build system compatibility".
How did critical CSS get inlined?
Every module's SCSS is compiled twice through the same PostCSS chain, split by postcss-critical-split: one pass emits output_types.CRITICAL_CSS into critical.css, the other REST_CSS into the module stylesheet. The chain is autoprefixer (last 2 versions, IE >= 10, grid on), css-mqpacker, postcss-combine-duplicated-selectors, plus postcss-clean in prod only and with conservative settings (advanced: false, aggressiveMerging: false, roundingPrecision: 3). gulp-inline-source then inlines critical.css into every pattern's HTML.
That created a rule you can still read in the SCSS: anything that must not flash needs its rules in the same file, which is why the main-nav and hamburger-button imports carry // critical definitions in file comments — and why main-nav submenus were moved into the inline CSS rather than the async stylesheet.
Why did the ES6 migration break the build?
Because the build tooling wasn't ES6-ready. The commit that fixed it reads, verbatim: "Fix the build, was broken by gulp-uglify not being ES6 compatible, changed it out for gulp-ulgify-es" — including the typo, which is how gulp-uglify-es ended up in devDependencies.
Webpack runs babel-loader with @babel/preset-env targeting last 2 versions / IE >= 10, useBuiltIns: 'usage' and @babel/plugin-transform-classes. In May 2019 the whole source/_patterns tree was converted from CommonJS to ES modules. uglify() is still commented out in jsBuildProd() to this day.
jQuery is injected at build time by webpack's ProvidePlugin ($ / jQuery), but some modules assign window.$ = $ themselves for the integrating page — "don't expose jquery in career module" is a real commit, and frame.js still carries the assignment.
What did the export task have to work around?
The export runs Pattern Lab's patternsonly build into pattern_exports/, with the general-header and general-footer partials pointed at /dev/null so no styleguide chrome leaks into the markup. The output still carried Pattern Lab's own trace, so exportDeletePatternLabTrace() deletes lines matching pattern-lab.css or the <!-- Begin Pattern Lab --> markers and rewrites asset paths (../../base → ../base).
Because of that /dev/null path the whole task is Unix-only — the README says so plainly: "compiles each pattern markup without pattern lab specific includes (⚠️ doesn't work on windows)".
A separate Pattern Lab quirk forced a hack in 2.13.4: the CS Start Page dropdown only appeared in the export when the pseudo-pattern had been saved, so it was duplicated as a real pattern. The commit calls it a temporary hack, and the changelog still lists the page.
Which browser bugs were the worst?
IE11 and Safari, repeatedly, and always in the least glamorous parts of the interface.
- Safari gradients. The cards' white-to-transparent gradient rendered as white-to-grey — a known WebKit bug. The fix (commit "fix card gradients in safari; revert background-image on pr card") was to stop using a gradient background on the press-release card altogether.
- IE11 polyfills.
01-tools/polyfills.jspulls inurl-polyfillforURL/URLSearchParamsand hand-rollsElement.prototype.matchesandclosest. IE also got an SVG dimension fix and a first-load timing fix for the flyover / article actions. - The image gallery had two bugs at once: Chrome not switching the caption on the last image, and IE showing a spurious scrollbar.
- iOS zoom. In 2.13.4 the viewport meta became
width=device-width, initial-scale=1, maximum-scale=1to stop iOS zooming in when an input got focus. It worked; it also takes pinch-zoom away from the reader.
Shipping it
Releases, performance budgets and the integration contract
Each release shipped a folder such as changes/2.13.6/ with an index, one page per change and the styleguide, listing every affected markup-only pattern. The diff pages group identical changes across templates — "Similar Change 0 … 23 files affected" — so a reviewer saw one change instead of twenty-three files. These are checked-in artifacts: no gulp or Makefile target produces them.
CHANGELOG.pdf sat next to the 91 KB CHANGELOG.md and was regenerated on every version bump — a 536 KB binary living in git history. The changelog is Keep a Changelog plus local conventions: a Markup type for changes integrators must apply, [MR] / [CW] / [CS] tags per frontend, and spreadsheet ids like (MR: 185) pointing back into the client's tracking sheet.
bamboo-specs/ holds the Bamboo plan as Java code (Atlassian bamboo-specs 6.5.0) that reads rmConfiguration out of package.json. It runs sitespeed.io 7.7.3-plus1 over four live URLs with a hard budget: performance ≥ 80, best practice ≥ 75, accessibility ≥ 70. Chrome, simulated cable, three iterations, crawl depth 1; results went to Graphite/Grafana and were scp'd to /srv/artifacts/performance.
api/filter-search-articles.yml is a Swagger 2.0 document for something that never became an API: the Mediaroom filter and search builds this query string and then redirects to it via window.location.href. It pins down search, topic[], type[], permission_level[], time_mode (1 = all, 2 = last 3 months, 3 = last 6 months, 4 = explicit range), bookmarked, sort_by (date or relevance) and sort_order — written down so front and back end could not drift apart.
Links
Where to find this one
