The Design System of The Metropolitan Museum of Art (@metmuseum)
Marble is the design system of the Metropolitan Museum of Art's Digital Deptartment. Marble is currently only an innersource project.
Our component explorer, powered by Storybook, lives on Chromatic at:
ποΈ main--5ef272f9ab690c0022ef30ab.chromatic.com π
Marble's design docs and homepage are at:
π¨ https://marble.metmuseum.org/ π‘
Marble installs from GitHub, not the NPM registry.
It's recommended that your CI/CD process use npm ci
instead of npm install
so that:
- You get deterministic, reproducible builds.
- You don't accidentally introduce a newer version of Marble than you intend to.
There are multiple ways to install Marble:
Lock to one version:
npm install metmuseum/marble#0.11.15
Lock to a specific commit:
npm install metmuseum/marble#9e68bab
Use any npm-compatible semantic versioning syntax:
npm install metmuseum/marble#semver:^0.11.15
Just get whatever's on main:
npm install metmuseum/marble
Remember, if you're using npm ci
in production (recommended), then you'll need to periodically run npm install ...
or npm upgrade ...
and check in the updated package-lock.json.
Marble can be imported a few different ways, depending on how your project preprocesses and bundles things. If you're unsure, please reach out in our #app-dev Slack channel.
-
If your project only uses CommonJS syntax (vanilla Node.js environments):
const marble = require("@metmuseum/marble");
-
ESM syntax (Webpack, etc):
import marble from "@metmuseum/marble";
-
Reference the files directly (not recommended):
Path/To/Your/Project/node_modules/@metmuseum/marble/dist/marble.js
- This exposes a variable called
marble
.
- This exposes a variable called
Path/To/Your/Project/node_modules/@metmuseum/marble/dist/marble.js.map
- Source maps (recommended)
- TODO: Consider CDN-hosted file for
<script>
tag? (easy with CI, but like, version control?)
This should work, please let us know if it does not:
@import "~@metmuseum/marble/src/marble.scss";
- If your project supports the
~
syntax:import "~@metmuseum/marble";
- If not, production-ready assets are available to reference directly (not recommended):
Path/To/Your/Project/node_modules/@metmuseum/marble/dist/marble.css
- TODO: Consider CDN-hosted file for stylesheet
<link>
tag? (easy with CI, but like, version control?)
We recommend just using all of Marble's styles, for now. See above. βοΈ
TODO: investigate Sass @import
vs new @use
syntax:
- https://sass-lang.com/documentation/at-rules/import#import-only-files
- will inform if we need our file structure to have
module.import.scss
naming. - TODO: Probably we should move away from:
@import "marble/src/base/base";
(kinda bad/leaky abstraction because what if we change file structure?) - Depending on SCSS preprocesssor, namely scss-loader+postcss (webpack) vs gulp sass, there is totally different behave regarding scope and iikjf it follows dependencies :(
Marble does not currently export component html or templates, only styles and javascript. Think of it a little bit like Twitter's Bootstrap Framework, or like a meal kit, but not dish you serve.
It's up to your project to implement the proper markup, based on examples you can find in /src
and on our Storybook.
Take the structure and classes from Marble:
// in src/components/section-heading/section-heading.html.js
html`<div
class="section-heading section-heading--text-${textAlignment} ${inSitu
? "productive-component"
: ""}"
>
<h2 class="section-heading__heading ${context}">${header}</h2>
<div>${he.decode(bodyCopy)}</div>
<a
class="button tertiary-button section-heading__text-link"
role="button"
tabindex="0"
href="#"
>
${CTA1}</a
>
</div>`;
And interpret them for your project's framework and data models:
<div class="section-heading section-heading--text-center productive-component section-header-@Model.Name">
<h2 class="section-heading__heading expressive">@Html.Raw(Model.Header)</h2>
<div>@Html.Raw(@Model.Description)</div>
<a
class="button tertiary-button section-heading__text-link"
role="button"
tabindex="0"
href="@Model.UrlLink"
>@Html.Raw(Model.CTA)</a>
</div>
By design, Marble's Javascript should only cause one side-effect: exposing a variable called marble
. It's up to your project to tell Marble what to do and when.
At the bare minimum, you probably want to run Marble's global code:
import marble from "@metmuseum/marble";
marble.global();
If you use a specific component, say the jumpLinkBanner
, you need to call that somewhere, as well:
import marble from "@metmuseum/marble";
marble.jumpLinkBanner();
TODO: a marble.everything();
option?
You can expect that Marble's default branch, main
, is always stable and releaseable. If you installed as above, npm install
and npm update
should always be safe.
Make sure you always check in your your package-lock.json.
Use npm ci
instead of npm install
. This will ensure the project only builds with the exact commit (and dependencies) that were specified earlier in the package-lock.json
.
Example:
"marble": {
"version": "github:metmuseum/marble#d765ab8a340e1e989953207115414469307da93c",
"from": "github:metmuseum/marble#main",
"requires": {
"he": "^1.2.0",
"smoothscroll-polyfill": "^0.4.4",
"vanilla-lazyload": "^12.5.1"
}
}
More info: https://docs.npmjs.com/cli/ci.html
It is not recommended to point your installation of Marble to an environment-specific branch on staging or production. Please always use main
.
- mock your data separately
- use component story format
- use controls
- try to provide in-situ examples?
- make sure a11y passes!
-
file organization
- stories
- scss
- javascript
- text casing (kebab-case?)
-
linting
- JS: pnpm run lint
- S/CSS: pnpm run stylelint
You may want to see your changes to Marble locally and in the context of another project you're working on. We can do this easily with pnpm link based on npm's link.
-
Tell your
pnpm
that this is the local folder where Marble lives.-
Navigate to your local Marble repo (ie, where this README lives) and just run:
pnpm link
-
-
Next, tell whatever project you're working on to use that local, linked version of Marble.
-
Navigate to a local project folder and find the directory that contains the
package.json
that originally specified Marble. * For example, in Ghidorah, this wouldn't be the project root, it would be:ghidorah/MMA.Ghidorah/
-
From that directory, run:
pnpm link @metmuseum/marble
-
Now, instead of what's installed in
node_modules
, pnpm knows to pull our Marble package files from the directory in Step 1. We can make our changes in our Marble repo (more on that below), and our other project will show them to us.
-
Storybook is the preferred way of developing components for Marble.
Recommended: install Docker Desktop and run it.
Always build the latest changes if you pull, etc:
docker-compose build
Run the container:
docker-compose up
Storybook will now be available at: http://localhost:54525
To start Storybook locally, launch the app with: pnpm run storybook
To publish the Storybook to its web homepage (via GitHub pages), please commit your changes, then run: pnpm run deploy-storybook
. Be mindful that this overwites the current Storybook with your local version.
We use the html preset
for Storybook. There are many good exmaples of html stories and add-ons at the official "kitchen sink" example directory:
TODO: update this to have CI build dist on merge
-
For releases of Marble, we'll want to compile everything into a production-ready
.css
and.js
file. -
We use Webpack to build and bundle these files to
/dist
-
Our build step is aliased in package.json as:
pnpm run build
- See also: /webpack.production.config.js
- Make sure you commit this production-ready build of Marble and not the development version that would also be generated to
/dist
anytime you run webpack-dev-server.
We target the latest 2 major versions of most browsers, via our .browesrslistrc
file.
To see what those currently are, run:
pnpm dlx browserslist
To update the list of current browsers, it's important to frequently use:
pnpm dlx browserslist@latest --update-db
...because of reasons.
Currently for internal use only. Β© Copyright 2021 The Metropolitan Museum of Art. All rights are reserved.