/spectrum-css

The standard CSS implementation of the Spectrum design language.

Primary LanguageCSSApache License 2.0Apache-2.0

Spectrum CSS

The goal of this project is to provide a standard CSS implementation of the Spectrum design language for internal and 3rd party use on Adobe's platforms.

  1. Where is the JavaScript?
  2. Organization
  3. Using Spectrum CSS
    1. Multi-stop Strategy
    2. Single-stop Strategy
  4. Building
  5. Testing
  6. Contributing

Where is the JavaScript?

We have found that JavaScript is where a framework or library quickly becomes opinionated and stops being easy to use with or across other frameworks. To avoid this problem, Spectrum CSS avoids implementation details that require JavaScript. Where an element might require multiple states, the documentation here will simply show all the states in a flat, static example. We leave it to the frameworks implementing Spectrum CSS to create JavaScript that suits their needs.

Organization

The Spectrum CSS project can build both a 'multi-stop' and 'single-stop' version of the CSS. This enables a consumer to either allow for multiple Spectrum colorstops in a single CSS file, or can limit the number of selectors to only those needed for a single colorstop.

Spectrum CSS organizes the CSS source files in the src folder. Each Spectrum element has it's own folder. That folder contains an index.css file for the basic structual CSS for all variants of an element. There is also a skin.css file to hold the values that change when the colorstop of the element is specified.

The CSS source files also contain Topdoc comments with a placeholder for documentation values that are injected at build time. The source of those injected values is found in the YAML formatted files in the docs folder. A key part of the docs data is the markup node, which contains the HTML elements needed to apply the corresponding element selectors and render the elements as generated Topdoc output.

A successful build will create a dist folder. The dist/docs folder is where the Topdoc output and related template files will end up.

Updating internal dependencies

Internal-to-Adobe only - for now

Locally clone the spectrum-css-deps repo. This repo contains internal, non open source dependencies needed by Spectrum CSS. Keep spectrum-css-deps as a sibling repo to spectrum-css. In spectrum-css-deps, run npm run build to generate the dependencies and npm run copy to copy the generated dependencies into spectrum-css. Then go ahead and review and commit any changes.

Using Spectrum CSS

Spectrum CSS can be consumed as whole or in part with two distinct methods of applying colorstops.

To install Spectrum CSS, run npm install @adobe/spectrum-css from your project's root directory. You will be able to find everything you need for both strategies in the node_modules folder.

Multi-stop Strategy

The first method of applying colorstops, multistop, makes it possible to have any number of colorstops on the same page. This method results in slightly larger CSS files with more selectors, but is the method most products will use as dark and light colorstops are commonly mixed in Spectrum designs.

  1. To get all Spectrum components, include dist/spectrum-core.css then dist/spectrum-COLORSTOP.css for each colorstop you need (where COLORSTOP is light, dark, etc).

  2. To get only the CSS for components and colorstops you need, include the following to start:

  • dist/components/page/index.css
  • dist/components/page/multiStops/COLORSTOP.css for each colorstop
  • dist/components/typography/index.css
  • dist/components/typography/multiStops/COLORSTOP.css for each colorstop

Then, for each component you need:

  • dist/components/COMPONENT/index.css for each component
  • dist/components/COMPONENT/multiStops/COLORSTOP.css for each colorstop

Set <body class="spectrum spectrum--light"> to skin the page with light colors, and add <div class="spectrum--dark"> wherever you need dark styles, or any combination of the above.

Note that, due to CSS selector specificity, whatever colorstop you import last will win if you nest colorstops 3 levels deep. That is, if you first import the light colorstop, the the dark colorstop, and you have the following HTML, the 3rd button ends up dark.

<body class="spectrum spectrum--light">
  <button class="spectrum-Button">I'm light!</button>

  <div class="spectrum--dark">
    <button class="spectrum-Button">I'm dark!</button>

    <div class="spectrum--light">
      <button class="spectrum-Button">I'm still dark!</button>
    </div>
  </div>
</body>

Single-stop Strategy

The second method of applying colorstops, singlestops, makes it so it's only possible to have a single colorstop on the page at once. This method results in less selectors and smaller CSS files.

  1. To get all Spectrum components for a specific colorstop, include only dist/standalone/spectrum-COLORSTOP.css.

  2. To get only the CSS for components you need and a single colorstop, include the following to start:

  • dist/components/page/index.css
  • dist/components/page/colorStops/COLORSTOP.css for the single colorstop
  • dist/components/typography/index.css
  • dist/components/typography/colorStops/COLORSTOP.css for the single colorstop

Then, for each component you need:

  • dist/components/COMPONENT/index.css for each component
  • dist/components/COMPONENT/colorStops/COLORSTOP.css for the single colorstop

As there is CSS for only one color stop present, simply set <body class="spectrum">. If mixing with individual components using the multistop strategy, you can add class="spectrum--dark" on <body> or anywhere else, but it only affects components whose colorstops were imported using the individual component multistop strategy.

Import Order and Components

With Spectrum CSS, dependency management between components is the responsibility of the consumer, you. The framework you're building likely has dependencies itself, such as dropdown depends on button, and each of the components includes its CSS. If this is the case, you'll get the CSS in the right order automatically, since dropdown is going to depend on button, and button will import the necessary CSS.

However, if you're doing a more manual inclusion of CSS files, the easiest thing to do is to use the fully built dist/spectrum-core.css + dist/spectrum-light.css or dist/standalone/spectrum-light.css files described above. If you need only specific components, be sure to follow the order in src/components.css so components can override styles of their dependencies.

Import Order and Colorstops

If your pages are light by default, with some dark elements embedded within (shell, etc), you should import the light colorstop first, adding .spectrum--light to an outer container (affecting the whole page), and adding .spectrum--dark to an inner container when you need dark elements (affecting only elements inside of it). That is, the import order of colorstops should match the nesting on your page.

Scale support

Spectrum CSS supports two scale sizes:

  • Medium - Desktop
  • Large - Mobile

Medium only

By default, when you import index.css for each component or spectrum-core.css, you'll get the Medium scale.

Large only

If your site is always mobile, you can get large by default by importing index-lg.css for individual components, or spectrum-core-lg.css for all components.

Medium and Large

If you need to display both Medium and Large, you can import index.css and index-diff.css for individual components, or spectrum-core.css and spectrum-core-diff.css for all components.

You can then switch scales by adding the .spectrum--large or .spectrum--medium class on the <html> element.

Note that the Spectrum CSS UI icons must change as well, see below for a full example.

Importing Icons

To get icons, you'll need to use the loadIcons() function. This function lives in the icons/loadIcons.js file and has the following signature:

loadIcons(svgURL {String}, callback {Function});

The callback function has the following signature:

function(error {Error}, svg {SVGElement})

First, you'll need the Spectrum CSS UI icons, which come in two flavors:

  • icons/spectrum-css-icons.svg - Both medium and large icons for responsive UIs
  • icons/spectrum-css-icons-medium.svg - Medium icons only, for desktop display
  • icons/spectrum-css-icons-large.svg - Large icons only, for mobile display

Import the icon set for the scale you're using:

loadIcons('icons/spectrum-css-icons.svg');

If you're using icons/spectrum-css-icons.svg, be sure to add .spectrum--medium or .spectrum--large to the <html> element, or you'll see both medium and large icons at once.

Finally, you'll need workflow icons for your app. These are currently Adobe-internal-only (sorry!) and live in icons/spectrum-icons.svg (if you have your local repo set up with all internal Adobe bits properly). This file contains all of the Spectrum icons. If you're using React Spectrum, you'll import each icon where it's used. If you're not, you can import the entire set of icons in all sizes as follows:

loadIcons('icons/spectrum-icons.svg');

You can then use the icons in your app. Visit the Spectrum CSS workflow icon list and click on any icon to get the markup.

Swapping out icons sets for scaling

If you're using different scales, you'll need to import the proper icon set to match. Note that both of the files have the same SVG IDs, so for a responsive site using index-diff.css for each component, you'll need to remove the SVG element from the DOM when switching scales:

var mediumIcons;
var largeIcons;

// Call when you switch scale
function switchScale(scale) {
  var otherScale = scale === 'large' ? 'medium' : 'large';

  // Remove the old icons from the DOM
  try {
    document.head.removeChild(scale === 'large' ? mediumIcons : largeIcons);
  }
  catch (err) {
    console.error(err);
  }

  // Add the new icons
  document.head.appendChild(scale === 'large' ? largeIcons : mediumIcons);

  // Switch the outer class if you're using index-diff.css to support both medium and large
  document.documentElement.classList.toggle('spectrum--' + scale, true);
  document.documentElement.classList.toggle('spectrum--' + otherScale, false);
}

// Load medium icons
loadIcons('../icons/spectrum-css-icons-medium.svg', function(err, svg) {
  mediumIcons = svg;
});

// Load large icons
loadIcons('../icons/spectrum-css-icons-large.svg', function(err, svg) {
  largeIcons = svg;

  // Immediately remove from the DOM -- it will be added back when we switch scale
  document.head.removeChild(largeIcons);
});

Lang support

To take advantage of locale specific change, for instance, placeholders not italicizing Japanese, applications should specify a response header for the language, a meta tag lang, or declare a lang attribute on their element. That's lowest to highest priority, so a meta tag can be overridden by an inline attribute.

Project Structure

In this project, there are three sets of source files.

  • src/: contains the source for element CSS selectors, with related attribute/value pairs.
  • docs/: this is the 'default' markup (in Topdoc format) that aligns with the CSS generated by this project. Documentation is generated using the metadata stored here.

Building

Run the following commands:

npm install -g gulp-cli
npm install
gulp

Your dist/ folder should now have a local copy of the Spectrum CSS docs and ready-to-use CSS files.

Important: Ensure you have NodeJS 10.x installed to build the CSS correctly (checkout #61 for more information).

Testing with the Visual Regression Tools

Under the hood, the visual regression tool is powered by a library called BackstopJS. The idea is that by running some gulp tasks, the BackstopJS library will take a screenshot of every component described in the docs/ folder using a headless chrome instance. This process is fairly intensive to run locally and has some quirks (eg, hanging chrome processes).

Local Testing

Typical testing workflow:

  1. Build the dist/ folder with gulp build
  2. gulp test
  3. Wait for results to finish (approx 7 mins)
  • If this is the first run test, the results will be all failures since it has no reference pictures to compare against. Use backstop approve to make the screen shots taken in this run to become the new reference set.
  • If you have an existing reference set, you will get a result of which components have changed. You may also choose to again backstop approve the new changes to set the new reference set or continue testing.
  1. Navigate to backstop_data/html_report/index.html to see the nicely formatted html report.

To note: If you see your tests returning with a 'Tag Not Found' image, then it's usually because of a particular chrome instance that was taking that screen shot was left hanging and timed out. Try these commands and run the test again: MacOS and Linux:

pkill -f "(chrome)?(--headless)"

Windows PowerShell:

Get-CimInstance Win32_Process -Filter "Name = 'chrome.exe' AND CommandLine LIKE '%--headless%'" | %{Stop-Process $_.ProcessId}

Testing Specific Components

Instead of waiting around and testing all of the components, you may supply optional arguments to the gulp test command like so:

gulp test --component button
gulp test --component button --component well

As long as the component name matches one of the folder names within dist/components, the gulp test command will find it and test it.

Remote Testing with the Test Server

Internal-to-Adobe only - for now. Follow progress on this issue.

There's a Jenkins box that's connected to this repo and will build on any branch and commit so long as it has the Jenkinsfile in the project root. If all goes well, you should see your commit in the status page for the JenkinsCI: https://spectrumci.ci.corp.adobe.com/blue/organizations/jenkins/spectrum-css/activity.

Additonally, the Jenkins server will build the html report but will try to compare the test images against the last commit OR if the last commit's images cannot be found, it will compare against what's currently on HEAD at master. The html report lives under the Artifacts tab and is labeled Spectrum CSS Visual Regression Report.

You may also view all of the screenshots taken so far by Jenkins in this repo: https://git.corp.adobe.com/Spectrum/spectrum-css-visual-reports.

To note: the JenkinsCI server will almost always have a green check mark in the commit message but should be taken with a grain a salt. The green check mark in this case means the project built succesfully BUT it does not mean the components aren't all mangled up or whatever. It is up to the developer who is committing to ensure that their commits are good and are using this tool to see how much visual change their commits are actually doing.

Contributing

Check out the contributing guidelines!

Releasing

We are currently releasing this package on npm. We are also releasing an internal version of this package for Adobe on an internal instance of Artifactory.

Before we get started, clean up your dependencies with the following command.

git checkout master
rm -rf node_modules && npm install

Now prepare the package for release by bumping the version (minor version bump by default), committing the changes, tagging the repo, and pushing the changes and tags up to GitHub. Run the following script which does all of this for you.

npm run bump (patch/minor/major)

This command takes an optional argument of the type of version increase you want. You can pass either patch, minor or major. If you don't supply an argument, the command will do a minor version bump by default. You could also pass a version you want the package to be. Ex: npm run bump 5.0.0.

Now that we have prepared the repo for publishing, let's publish the package to npm! Run the following command.

npm publish

Now let's prepare to release to artifactory. For legacy reasons, we need to update the scope from @adobe to @spectrum and add the internal only @spectrum/spectrum-icons dependency. We have a command that does this for you.

npm run prepare-artifactory

Now let's publish the package to artifactory. Run the following command.

npm publish --registry=https://artifactory.corp.adobe.com:443/artifactory/api/npm/npm-spectrum-release-local

Now that the package is published, let's revert the changes we did for artifactory by running the following command.

git checkout package.json package-lock.json

Now let's deploy the docs. We need to build the repo, make a copy of dist named after the release, checkout the gh-pages branch, add the new version of the docs and update references in the redirect files and commit. Run the following commands.

npm run build
cp -r dist 2.7.2 (replace this with the version you are releasing)
git checkout gh-pages
git add 2.7.2 (replace this with the version you are releasing)

Manually update the old versions in index.html and icons/index.html.

Now do a quick sanity check of the docs by testing the docs site. Run the site in your browser with the npm run start command.

If everything looks good, commit and push the docs.

git commit -am "Deploy version 2.7.0"
git push https://github.com/adobe/spectrum-css.git gh-pages
git checkout master

You are all done now! Clean up your working directory.

rm -rf node_modules && npm install