npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

mp-design-system

v2.1.62

Published

Malvern Panalytical's design system for the commercial marketing websites — the `*.malvernpanalytical.com` subdomains. Internally codenamed **"Franklin"**.

Readme

mp-design-system

Malvern Panalytical's design system for the commercial marketing websites — the *.malvernpanalytical.com subdomains. Internally codenamed "Franklin".

The repository serves two purposes from a single codebase:

  1. A documentation / brand-book website built with Astro. It presents the brand guidelines, foundations (colour, space, grid, typography), the component library, patterns and prototype pages. It is deployed to Cloudflare Pages (project mp-brandbook).
  2. A published npm package (mp-design-system) of compiled CSS and bundled JavaScript that the production websites consume via <link> and <script> tags.

What it is (and what it isn't)

  • It ships class-based CSS plus vanilla-JavaScript behaviours — not React or Vue components. Consumers write plain HTML with the documented BEM classes; the CSS styles it and the JS auto-initialises interactive behaviours (accordions, carousels, tabs, modals, off-canvas, etc.).
  • The .astro components in this repo exist only to render the documentation site and act as the canonical markup reference for each component. They are not published and are not imported by consumers.
  • The styling is a bespoke SCSS system. It does not use AutomaticCSS (ACSS) or Tailwind. It borrows familiar concepts — fluid type/space and CSS custom properties — but implements its own tokens (--color-{palette}-{step}, --space-*, --step-*) from scratch.

Tech stack

| Concern | Tooling | | ------------------------ | ---------------------------------------------------- | | Site framework | Astro 6 (@astrojs/mdx, @astrojs/vue) | | Styles | Dart Sass (sass), compiled by a custom Node script | | Scripts | esbuild (bundling + minification) | | Carousels | @splidejs/splide | | Code highlighting (docs) | prismjs + Astro/Shiki | | Formatting | Prettier (+ prettier-plugin-astro) | | Deploy | Wrangler → Cloudflare Pages |

There is no test framework and no ESLint configured.

Consuming the design system

Every subdomain has its own build. Each build is one main stylesheet, one legacy stylesheet, and one script bundle:

| Subdomain | Main CSS | Legacy CSS | Script | | ---------------------------------------------------- | ---------------- | ----------------------- | --------------- | | www.malvernpanalytical.com | mp-www.css | mp-www-legacy.css | mp-www.js | | www2.malvernpanalytical.com (Pardot landing pages) | mp-www2.css | mp-www2-legacy.css | mp-www2.js | | academy.malvernpanalytical.com | mp-academy.css | mp-academy-legacy.css | mp-academy.js | | brand.malvernpanalytical.com | mp-brand.css | mp-brand-legacy.css | mp-brand.js | | events.malvernpanalytical.com | mp-events.css | mp-events-legacy.css | mp-events.js | | mystore.malvernpanalytical.com | mp-mystore.css | mp-mystore-legacy.css | mp-mystore.js | | support.malvernpanalytical.com | mp-support.css | mp-support-legacy.css | mp-support.js |

Via CDN (recommended)

unpkg serves the built assets straight from the published npm package. Include the legacy stylesheet, the main stylesheet, and the script matching the subdomain, replacing <version> with the version you want to pin (the current version is shown in the top-right of the documentation site). Example for www2:

<link
  rel="stylesheet"
  href="https://unpkg.com/mp-design-system@<version>/dist/build/styles/mp-www2-legacy.css"
/>
<link
  rel="stylesheet"
  href="https://unpkg.com/mp-design-system@<version>/dist/build/styles/mp-www2.css"
/>

<!-- Place near the closing </body> tag — it will not run correctly in <head>. -->
<script src="https://unpkg.com/mp-design-system@<version>/dist/build/scripts/mp-www2.js"></script>

Self-hosted via npm

If you want to bundle or self-host the assets, install the package:

npm install mp-design-system

The same compiled files are then available under node_modules/mp-design-system/dist/build/styles/ and .../dist/build/scripts/ to copy or bundle.

Note: only the compiled output is published — the package files field ships dist/build/** and dist/assets/**. The SCSS/JS source under src/ is not included in the package, so you cannot @import the raw SCSS from node_modules. Consume the compiled dist/build assets.

Usage in HTML

Write markup with the documented BEM classes, scoped by a leading .mp class so the styles stay namespaced when injected into an external (e.g. WordPress) site:

<a class="mp c-button c-button--red" href="/contact">Contact sales</a>

Class prefixes follow ITCSS conventions: c- for components, o- for layout objects, u- for utilities, with BEM __element and --modifier suffixes.

Architecture

Two outputs from one repo

  • The documentation siteastro build renders the static site into dist/.
  • The publishable assetsbuild-assets-to-publish.mjs compiles the SCSS and bundles the JS into dist/build/. This is what npm publishes.

Repository layout

| Path | Purpose | | ----------------------------------- | ----------------------------------------------------------------------------------------------------- | | src/components/ | 60+ UI components, each in its own folder (see the component contract below), plus registry.ts | | src/patterns/ | Higher-level compositions (Form, GridOfCards, Sections, …) + registry.ts | | src/prototype/ | Full example pages (home, product, blog, contact, support, …) | | src/layouts/ | Astro page layouts (BaseLayout, DocsLayout, ComponentDocsLayout, …) | | src/pages/ | File-based routes for the docs site (brand/, components/, patterns/, prototype/, guide pages) | | src/styles/ | All SCSS — the per-subdomain entry files and the refresh/ + legacy/ trees | | src/assets/scripts/ | Client-side JavaScript — per-subdomain entry points and feature modules | | build-assets-to-publish.mjs | Compiles SCSS → compressed CSS and bundles JS → minified, into dist/build/ | | dev-assets-to-compile.mjs | Dev-time JS bundling (unminified) into public/assets/scripts/ for the docs site | | astro.config.mjs, tsconfig.json | Astro config (Vue + MDX, @src) and TS options | | bitbucket-pipelines.yml | CI: build + deploy to Cloudflare on pushes to master |

Styles — ITCSS layers

The website ecosystem is being redeveloped in phases — an ongoing "refresh". As each part is rebuilt it moves into the refresh system, so src/styles/ contains two coexisting systems:

  • refresh/ — parts of the ecosystem that have been redeveloped as part of the phased refresh. A 7-layer ITCSS structure: 1-settings (tokens) → 2-tools (functions/mixins) → 3-generic (reset, base, token emitters) → 4-elements5-objects6-components7-utilities.
  • legacy/ — the parts not yet redeveloped, still served in their pre-refresh form. Each subdomain ships a -legacy stylesheet alongside its main one.

Per-subdomain entry files (src/styles/mp-www.scss, mp-academy.scss, …) @use the layers in order and set a site identity variable.

Design tokens

Tokens are a two-layer pipeline:

  1. Authored as SCSS maps/variables in refresh/1-settings/ — e.g. the seven brand colours in colors.scss ($f-mp-petrol: #005461, $f-mp-green: #13aa13, …), each expanded into a stepped scale (step--2step-3); the fluid $spaces scale in space.scss; and the fluid type $steps in fluid-type.scss.
  2. Emitted as CSS custom properties in refresh/3-generic/color.scss generates --color-{palette}-{step} (e.g. --color-green-step-0), space.scss generates --space-* and fluid --fc-* pairs, and the type scale generates --step-*.

Components consume only the CSS custom properties, never the raw Sass. Type and space are fluid (Utopia-style), scaling smoothly between 320px and 1332px viewports.

Component contract

Each component lives in src/components/<Name>/ with a consistent set of files:

Button/
  Button.astro          # markup — builds BEM class strings from props
  button.types.ts       # TypeScript prop types
  button.variants.ts    # catalogue of variants for the docs gallery
  index.ts              # barrel export

Every component is registered in src/components/registry.ts, which maps a kebab-case id to { name, category, Component, variants, … } and drives the dynamic docs routes (src/pages/components/[component]/…). The src/components/-NewComponent/ folder is a scaffold/template for adding a new component.

Client-side JavaScript

src/assets/scripts/ holds plain vanilla-JS modules (no framework). Each per-subdomain entry point (mp-www2.js, mp-academy.js, …) imports feature modules from imports/ and imports/refresh/ (accordion, carousel, tabs, modal, off-canvas, scroll-spy, hero video, hotspot image, …) and runs an initializeApp() that auto-runs on load.

Prerequisites

  • Node.js 22 — matches the CI build image (bitbucket-pipelines.yml). The version is not pinned in-repo (there is no .nvmrc or engines field, and sandbox.config.json still says Node 14, which is stale) — use Node 22.
  • npm (used in CI as npm ci) and git.
  • Access to the Bitbucket repository (bitbucket.org/mp-website/mp-design-system, workspace mp-website).
  • To cut releases you additionally need npm publish rights for the mp-design-system package and Cloudflare Pages access for the mp-brandbook project.

Local development

git clone https://bitbucket.org/mp-website/mp-design-system.git
cd mp-design-system
npm install
npm run dev

Open http://localhost:4321 to browse the documentation site. Each component is viewable at /components/<id> (e.g. /components/button) — those routes are generated from the component registry (see below), so a new component appears in the docs simply by being registered.

npm run dev first bundles the client JS into public/assets/scripts/ (unminified) and then starts astro dev. Use npm start if you only need the Astro dev server without re-bundling the JS.

Contributing: adding or changing a component

Remember there are two tracks (see Architecture): the .astro files + registry power the docs/reference, while the compiled SCSS and JS are what actually ship to subdomains. Adding a component usually touches both.

Per component (do once)

  1. Copy the scaffold. Duplicate src/components/-NewComponent/ to src/components/<PascalName>/ (e.g. Button/). Rename the four files (<Name>.astro, <name>.types.ts, <name>.variants.ts, index.ts) and find/replace the NewComponent / newComponent / NewComponentProps identifiers.
  2. Types — in <name>.types.ts, define the Props interface and any variant/size unions.
  3. Markup — in <Name>.astro, destructure Astro.props, build the c-<name> BEM class strings (the codebase uses the [...].filter(Boolean).join(" ") idiom), and prefix the root class with mp so it stays namespaced when injected into external sites.
  4. Variants — in <name>.variants.ts, populate the <name>Variants array. Every entry needs a truthy kebab-case id — empty-id entries generate no docs route.
  5. Barrel — confirm index.ts exports the component, its Props type, and the variants array.
  6. Register — in src/components/registry.ts add an import from the barrel and a <kebab-id> entry with the fields id, name, category, Component, variants, example, defaultProps, and optional figma / bitbucket links. This alone makes the component appear in the docs routes.
  7. Styles — create src/styles/refresh/6-components/<name>.scss, starting with @use "../_tools" as *; (which forwards all tokens, functions and mixins). Use tokens and mixins, never hardcoded values (see Coding conventions).
  8. Interactive behaviour (if any) — create a module in src/assets/scripts/imports/refresh/ as a default-exported factory that queries its selector and returns early if the element is absent (safe to call on every page).

⚠️ Per subdomain (the easy-to-miss step — repeat for each subdomain that should ship it)

There is no glob/auto-include: src/styles/refresh/6-components/index.scss is empty and does nothing. Each subdomain entry file lists its components individually, and the subsets deliberately differ (mp-www.scss and mp-www2.scss do not carry the same components).

  1. CSS — add @use "./refresh/6-components/<name>.scss" as *; to each relevant refresh entry file: mp-www.scss, mp-www2.scss, mp-academy.scss, mp-brand.scss, mp-events.scss, mp-mystore.scss, mp-support.scss.
  2. JS — for interactive components, add the import and call <Name>() inside initializeApp() in each relevant src/assets/scripts/mp-*.js entry.

Forgetting a subdomain means the component renders unstyled (or inert) on that subdomain with no error — this is the most common mistake, so double-check every entry file you intend to ship to.

Then run npm run dev to preview, and npm run build to produce the publishable assets.

Coding conventions

  • CSS naming — ITCSS + BEM: c- for components, o- for layout objects, u- for utilities, with BEM __element and --modifier suffixes. Prefix a component's root class with .mp so styles stay namespaced inside external (e.g. WordPress) pages.
  • Tokens, not literals: colours are always var(--color-{palette}-{step}) (e.g. --color-green-step-0) — never raw Sass colour values. Use the _tools mixins/functions for spacing and type: space() / padding() / margin() / gap(), flow($size), step($n), ls("base"), plus helpers like clickable-parent and focus-parent.
  • Spelling follows the system: refresh code is American (color); legacy code is British (colour). Match the system you are editing.
  • File/id naming: kebab-case ids in registries, PascalCase component folders/names, camelCase + Variants suffix for the variants export.
  • Formatting: Prettier (with prettier-plugin-astro). Run npx prettier --write . before committing. There is no ESLint and no test suite.

Git workflow

master is the deploy branch — pushing to it automatically rebuilds and redeploys the docs site (see Releasing).

  • Today: the project has a single maintainer who commits directly to master.
  • Recommended going forward: work on a feature branch and merge (or open a Bitbucket pull request) into master, so master stays deployable and changes are reviewable.

Scripts

| Command | What it does | | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | npm run dev | Compile JS to public/assets/scripts (unminified) then start astro dev | | npm start | Start astro dev only | | npm run build | astro build (docs site → dist/) then build-assets-to-publish.mjs (16 CSS compressed + 8 JS minified bundles → dist/build/) | | npm run preview | Preview the built site | | npm run deploy:cloudflare | Deploy dist/ to Cloudflare Pages (project mp-brandbook) via Wrangler |

Releasing

There are two independent release acts — one does not imply the other.

1. Deploy the docs site (automatic). Pushing/merging to master triggers Bitbucket Pipelines (bitbucket-pipelines.yml), which runs npm cinpm run buildnpm run deploy:cloudflare and publishes the site to Cloudflare Pages (project mp-brandbook).

2. Publish the npm package that production sites consume (manual). CI does not publish to npm — you must do it locally:

  1. Be on an up-to-date master with no uncommitted changes.
  2. Bump version in package.json (semver: first number = breaking, middle = feature, last = fix). The current version is 2.1.60.
  3. Commit and push the version bump.
  4. Run npm publish. The prepublishOnly script wipes dist and rebuilds first, so only freshly compiled dist/build/** assets are published.
  5. Consumers pick up the change by bumping the pinned version in their unpkg <link>/<script> tags.

Important: deploying the docs site is not the same as publishing the package. Merging to master updates the brand-book site but does not ship anything to the production websites — for that you must bump the version and npm publish. Because both steps are manual, the docs site and the published package can drift; keep them in sync deliberately.

Gotchas & current caveats

  • Per-subdomain wiring is manual. A new component must be @used into every relevant mp-*.scss and, if interactive, imported + called in every relevant mp-*.js. There is no auto-include; refresh/6-components/index.scss is empty. Missing one subdomain fails silently.
  • Docs deploy ≠ npm publish, and version bumps are manual (see Releasing).
  • Some in-app docs are outdated:
    • src/pages/components/creating-a-component.astro describes an obsolete Nunjucks/.njk workflow — ignore it; follow this README's component contract instead.
    • src/pages/components/installation.astro documents consuming the system via an SCSS-source @import '~mp'; this does not work because src/ is not published (only dist/build). Use the compiled assets (see Consuming the design system).
  • Registry links are mostly empty/stale. In registry.ts the figma fields are unused and the two populated bitbucket links point at a defunct src/_includes/components/… path.
  • No tests, no ESLint, no pinned Node version — quality relies on manual review and Prettier.

Ownership & support

The system is maintained by the Design System Team at Malvern Panalytical. For questions, feedback, or change requests, raise a ticket with the Web Development team via the Service Desk portal (the same link is in the site footer).