Skip to main content
U.S. flag

An official website of the United States government

Dot gov

The .gov means it’s official.
Federal government websites often end in .gov or .mil. Before sharing sensitive information, make sure you’re on a federal government site.

Https

The site is secure.
The https:// ensures that you are connecting to the official website and that any information you provide is encrypted and transmitted securely.

August updates! We reorganized our patterns in August.

About

For developers

Documentation and resources for developers.

Parts of the Design System

Using the Design System

If you are working in the vets-website repository, you can skip straight to the developer documentation. Otherwise, proceed below.

How you implement VADS styles into your project depends on how your project is structured and your preferences. The easiest way to get started is by using npm. For a prototype where you need the formation styles, you can add a <link> tag with the href set to https://unpkg.com/@department-of-veterans-affairs/formation/dist/formation.min.css.

Install Formation into your project

We recommend using npm to install the formation package into your project.

$ npm install --save @department-of-veterans-affairs/formation

This line installs Formation as a dependency. You can use the compiled files found in the node_modules/@department-of-veterans-affairs/formation/dist directory.

If you would like to use the un-compiled Sass files instead, you can find those in the node_modules/@department-of-veterans-affairs/formation/sass directory.

If you prefer to change the location of the fonts/ and img/ directories relative to formation.min.css, set the following variables in your project:

$formation-asset-path: '../assets';
$formation-image-path: "#{$formation-asset-path}/img";
$formation-font-path: "#{$formation-asset-path}/fonts";

The example above is what is used on VA.gov, but you can customize this for your project.

Sass functions, variables, and interactive components

If you would like to use the Sass functions, such as for spacing, and variables in your project, you can import the files from your project scss. This documentation site imports Formation’s Sass files in its application.scss.

Load the Web Component library

The Design System team is working on developing a library of reusable Web Components that can be used on any HTML page or React application.

This is already handled for the vets-website repository. To get our Web Component library set up in a new project, here is what we recommend:

  1. Add the component-library dependency to your node/yarn project using yarn add @department-of-veterans-affairs/component-library.
  2. Import the global CSS file which contains important CSS variables:
    import "@department-of-veterans-affairs/component-library/dist/main.css";
    
  3. Import the defineCustomElements JS function (applyPolyfills is only necessary if you wish to support older browsers such as IE11):
    import {
      applyPolyfills,
      defineCustomElements,
    } from "@department-of-veterans-affairs/component-library";
    
  4. In the same JS file, call the defineCustomElements function, optionally chained after a call to applyPolyfills:
    applyPolyfills().then(() => {
      defineCustomElements();
    });
    
  5. Make sure this script gets loaded on the HTML page - preferably near the top of the document in the <head> tag.

Using Web Components

What are Web Components

Web Components are a set of web platform APIs that allow you to create new custom, reusable, encapsulated HTML tags to use in web pages and web apps. We can create our new component called <va-web-component>, with its unique styling and functionality, and use it in any JavaScript framework or library. The fact that these components are framework agnostic, helps us future proof our component library.

Web Components consist of three parts:

  • A custom HTML element
    • Where you register your own HTML tag
  • The shadow DOM
    • This is a separate DOM node tree for your custom HTML elements that includes scoped CSS styles
  • Templates and Slots
    • You write HTML templates that you can add to your HTML elements
    • You add slots to provide additional context within the component

Vanilla JavaScript Applications

If the Design System web components will be used in a vanilla JavaScript application, you are ready to use them (identified by tags prefixed with <va-*>).

We make our best efforts to avoid creating web components with object or array properties in order to make them easier to use in static HTML pages.

React Applications

If the Design System web components will be used in a React application, you are ready to use them unless:

  • You must pass in a function, object or array to a web component’s properties
  • You must use custom events

If your use case is listed above, you will have to use our web component bindings for React. If you are not sure if you need to use a custom event, please refer to the web component’s Storybook documentation to see its events and properties.

Bindings are component wrappers that allow our web components to work as first-class React components, allowing us to handle custom events and to pass in more than strings and numbers to a web component’s properties. You will have to import each web component’s bindings like you would with a React component.

import { VaExampleComponent } from "@department-of-veterans-affairs/component-library/dist/react-bindings";

const exampleFunction = () => console.log("Hello, World!");

<VaExampleComponent exampleProp={exampleFunction} />

Custom Events

Some of the Design System web components allow for custom events.

If you must use custom events and you’re using JSX, you must prefix events with on. Given an event named vaChange, use onVaChange.

If you must use custom events and you’re not using JSX, you must add an event listener using the event name as the event type. Given an event named vaChange, use:

const element = document.querySelector('va-example-component');
element.addEventListener('vaChange', event => { /* your listener */ })

The majority of our web components also fire a component-library-analytics event used to translate component library actions into analytics data layer events. The event handler for this event exists in vets-website.

Native Events

Some of our web components utilize native HTML DOM events such as click and blur. We prefer to use native events when possible because it is easier for teams to test and may not require the use of React bindings.

To use native events in JSX, they must be prefixed with on and use camel case. Given the native blur event, use onBlur.

An example using the click event in JSX:

<va-button text="Edit" onClick={e => console.log(e)} />

To use native events in vanilla JavaScript, they can be used inline and prefixed with on or by adding an event listener using the event name as the event type.

An example using the blur event in vanilla JavaScript:

<va-button onblur="handleBlur()" />

Another example using the blur event in vanilla JavaScript:

const element = document.querySelector('va-button');
element.addEventListener('blur', event => { /* your listener */ })

React and Web Components

Note: Please use our VA Design System Web Components where applicable in your projects. We maintain this component library to provide VA teams with an ecosystem of vetted and tested components.

While large portions of VA.gov are built via React applications, there are some teams that cannot import React directly into their projects and have to add work around hacks in order to use React components.

Due to these issues the Design System Team recommends using our Web Components on VA.gov applications and pages.

For easy identification, all of our Web Components begin with a va- prefix. For example, the Web Component version of our alert component is named va-alert.

Benefits include:

  • Future proofing as Web Components can be imported into any JS Framework
  • Consistent syntax across frameworks and projects
  • Actively updated and maintained - we are deprecating most React components and they will not have the latest updates
  • Performance and speed

The Design System Team has specific linting and migration rules in place to help ease in the transition from React to Web Components. We also encourage all developers use Design System Components in their applications instead of creating their own similar components. If our components do not meet your needs, we would love to hear about it. Please reach out to us in Slack or submit a bug report. If you are interested in contributing a new component to the design system, please review our documentation about that process.

How to migrate to Web Components

The Design System Team provides three ways to migrate specific React Components over to Web Components:

  • Manual - There are too many changes to automate and a guide will need to be followed.
  • ESLint Rule - In the vets-website repo there is a ESLint rule that informs you of the ability to convert from a React Component to a Web Component.
  • Migration Script - There is a script available to be used in the CLI when in the vets-website repo to convert the React Component to a Web Component.

Here is a list of each Web Component and the migration available:

Implementing design work

When a designer hands off work, it is vital to work through potential implications that design may have on Formation. Are there any new variations on components? Are there any new components not present on this site? For more on that process, read about how to contribute.

In general, some rules for implementing design work include:

  • Use spacing units instead of hard-coding pixel values for margins and padding
  • Use Sass variables for colors instead of hex codes
  • Discuss reusability of new design components and where is the most appropriate home for CSS and JS
  • Use the Formation naming convention
  • Do not use ID selectors

Use design system utilities

Sometimes you will need to modify certain default properties of a component depending on how it scaffolds with nearby elements. Use utilites instead of writing new CSS.

Do

Use utility classes to override default properties. This allows components to maintain a well-defined baseline of properties.

HTML
<div class="a-container">
  <div class="a-component vads-u-margin-top--3"></div>
</div>

Don’t

Don’t change CSS properties based on a container or other context. This makes baseline properties for components unclear.

HTML
<div class="a-container">
  <div class="a-component"></div>
</div>
CSS
.a-container .a-component {
  margin-top: 24px;
}

Contributing to the Design System

The two main ways for developers to contribute to the Design System are by writing new components or by modifying existing components. Regardless of which type of contribution you are making, each PR should:

  • have at least 90% test coverage
  • have appropriate comments/documentation for functions, classes, etc.
  • have Storybook stories for new features
  • minimize complexity
  • include only the smallest changeset required for the feature or fix

New components

If you want to contribute something to the Design System, see the Contributing to the Design System page.

Modifying existing code

PRs which make a change to the Design System should be manageable in size (less than ~500 lines of code). This is to meant to:

  • Save your time as the developer
  • Keep PRs tightly focused
  • Keep the review process short.

Writing CSS for the design system

When naming components, be sure to use Formation’s naming conventions.

Searchable selectors

Many of the features in Sass make it easy to use shorthand to reduce repetitive typing and write cleaner .scss files. However, this makes using search features in GitHub or text editors much more difficult because it is not always clear how the shorthand was written; finding the right query requires guesswork.

Do

Write out the full name of each selector.

.alert {
}

.alert--warning {
}

.alert--error {
}

Don’t

Don’t use Sass shorthand features, such as nesting with ampersands often used with BEM syntax.

.alert {
  &--warning {
  }
  &--error {
  }
}

Contributing experimental design code

This document explains the process for contributing code for experimental designs and the reasoning behind that process.

If you haven’t read it already, refer to the contributing to the design system page for more information about the full process.

Writing experimental design code

Each experimental design should:

  • Be absent of business logic and domain knowledge
  • Not import application code
  • Not introduce breaking changes

Developing the experiment as if it were a standalone library will make the code more reusable and graduating the component or pattern into the official design system smoother.

Each experimental design should include a README and be owned by a team.

Sharing experimental design code

Sharing code between applications is necessarily more involved than writing code for a single application. To avoid the overhead that sharing code introduces (reporting development status, managing breaking changes, deprecating the code etc.), it’s recommended to develop experimental designs in the application directory and not import it from another application.

If the time comes that another application needs to use the experiment, the rest of this section describes the process for how to share this code.

Code location

Each experimental design is located in its own directory in vets-website at src/experimental/ unless otherwise noted in its documentation on this site.

Example:
If your team needs an experimental button that’s larger than the standard button, you would create src/experimental/large-button/index.jsx as the entry file for your “library.”

README

Each experimental design should have a README that contains the following information:

  • Development status: stable, unstable, or deprecated
    • The unstable status means the code is under active development and the public API may change without notice
    • The stable status means the public API is finalized, but the code may still receive backward-compatible updates such as accessibility improvements and bug fixes
    • The deprecated status means the code should no longer be used in applications
      • This may be because of a breaking change (see Breaking changes below), official adoption into the design system, or research which indicates the experiment was unsuccessful
      • See Ending the experiment below for instructions on what to do when deprecating code
  • API documentation (optional but encouraged)

Breaking changes

“Breaking changes” is defined here In semver terms as a backwards incompatible change to the public API of your component or pattern. (See the Semantic Versioning Specification for more details.)

Once the code for an experimental design is stable, breaking changes should not be introduced. Other applications may depend on this code, but are unable to pin the version because it’s not a “proper” library.

If you need to introduce breaking changes, do not modify the existing code. Instead, copy the contents of the directory to a sibling directory post-fixed with a version number.

Example:
The LargeButton you created accepted children, but because of reasons, you need to limit the content of the button to only text. You’ve decided to remove the children prop and add a label prop instead which accepts only strings. To introduce this change, you would:

  1. Copy the contents of src/experimental/large-button/ to src/experimental/large-button-2
  2. Update the status in src/experimental/large-button/README to deprecated and indicate why (because there’s a new version)
  3. Make the breaking changes to src/experimental/large-button-2
  4. Change the import statements 'experimental/large-button-2' in your application
  5. Update the CODEOWNERS file to add the new directory
  6. Make an announcement for anybody who may be using the deprecated code

CODEOWNERS

Add your team’s GitHub team name to the CODEOWNERS file to take ownership of the experiment’s code. This will mean your team will be required reviewers on all changes to this code.

Test coverage

As with all code, test coverage is critical. This is especially true with shared code. Aim for at least 90% unit test coverage before declaring an experiment to be stable.

Using shared experimental designs

Before using an experimental design, first check the src/experimental/ directory in vets-website to see if it’s been shared yet. If not, work with the authoring team to move the code into src/experimental/. See Sharing experimental design code above for more information.

The babel module resolver plugin has the root set to "./src", so you can import your experimental design with the following:

import LargeButton from '~/experimental/large-button';

Ending the experiment

Experimental designs are meant to be short-lived. The experimental design code may no longer be needed because:

  • The design was approved for adoption into the design system
  • The design was rejected for adoption into the design system
  • A breaking change was introduced and a new version was created

When code is deprecated for any of these reasons, the goal is to delete the code. If there are no applications using the deprecated code, simply delete the directory.

If there are applications using the deprecated code:

  1. In the README: Mark the code as deprecated
  2. In the REAMDE: Clearly outline what engineers should do to stop using the experiment
    • This may be something like “upgrade to ~/experimental/large-button-2,” “use @department-of-veterans-affairs/component-library/LargeButton,” or “discontinue use; the experiment has been rejected”
  3. In Slack: Notify teams that the code has been deprecated, either via an announcement or reaching out directly to the teams using the experiment
  4. Check in weekly to see if there are still applications using the experiment; delete the directory when no applications are dependent on it

It’s the responsibility of the code owners to delete deprecated code when it’s no longer in use.

Edit this page in GitHub (Permissions required)
Last updated: Sep 27, 2022