Skip to content

Hennepin County pattern and component library

Getting started

How we design and develop our digital interfaces reflects a commitment to efficiently connect people with the information and services they need.

Audience and purpose

Frontend developers should use the pattern and component library as a guide. It contains design elements for county websites and applications.

The guide will help developers meet the county's standards and ensure Section 508 compliance.

Developers may need to customize the code examples to fit their technology stack.

Maintenance of pattern and components

The User Experience Community of Practice reviews and gives input to the standards.

Staff can ask for changes or exceptions to the standards:

Accessibility

Everyone should be able to easily interact with the county online. This includes people with visual, hearing, motor, and cognitive disabilities. These disabilities could be permanent, temporary or situational.

Our digital accessibility standards align with the county's accessibility web standards.

Requirements for all our websites, applications, vendor technology, and third-party tools:

  1. Follow the Web Content Accessibility Guidelines (WCAG)
    • Meet success criteria for level A and AA
    • Meet success criteria for level AAA where possible and relevant
  2. Be validated for accessibility with in-person testing and automated testing with tools like WAVE

To understand and follow web accessibility laws and best practices we use: 

Structural markup

Semantic structure is the bedrock of accessible markup. Screen readers rely HTML elements and attributes to convey information to blind users. Semantic markup allows assistive technologies to convey information through the accessibility API.

Interactive components

Design these for touch, mouse, and keyboard users. Use relevant WAI-ARIA to make interactive components understandable and useable by assistive technologies.

You may need to use extra ARIA attributes and JavaScript depending on the component function.

Color contrast

Some color combinations in our default palette may lead to insufficient color contrast. We suggest testing colors. We also suggest you change default colors to ensure adequate color contrast ratios. The WCAG 2.1 text color contrast ratio is 4.5:1. The WCAG 2.1 non-text color contrast ratio is 3:1.

Focus indicator

Any element that receives focus should have a visual focus indicator. The focus indicator must be a solid outline. The default color is inherited from the font color. If that does not meet acceptable contrast ratios, use black. If black does not meet contrast ratios, use another brand color that does. The width should be about 4px, but the width can be adjusted to accommodate large or small components. Set the default outline-offset to zero for padded elements like buttons and inputs. Adjust the offset to allow space around other elements that need it like inline text and image links. For example, tab through the interactive controls below:

Inline text link with surrounding text.
example image link
Example focus indicator CSS
*:focus {
    outline:4px solid; /* adjust the width depending on the element size */
    outline-offset:0; /* adjust the offset depending on the element */
}

Visually hidden content

Sometimes important visual cues are provided to sighted users, like red text. These cues are picked up by assistive technology only by using visually hidden content. For example:

Danger: This action is not reversible!

Example visually hidden markup HTML
<p class="text-danger">
    <span class="visually-hidden">Danger: </span>
    This action is not reversible!
</p>

For visually hidden controls like "skip" links make sure the control becomes visible once focused (for sighted keyboard users).

Reduced motion

You must include support for the prefers-reduced-motion media feature. CSS transitions should be disabled. Meaningful animations such as spinners should be slowed down.

Example Reduced motion CSS
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation: none !important;
        transition: none !important;
    }
}
Example Reduced motion JavaScript
document.addEventListener('click', event => {
    if ( window.matchMedia('(prefers-reduced-motion: reduce)').matches ) {
        executeTaskWithoutMotion();
    } else {
        executeTaskWithMotion();
    }
});

Additional resources

Alerts

Alerts are used to convey special messages distinct from regular content.

A simple primary alert—check it out!
A simple secondary alert—check it out!
A simple success alert—check it out!
A simple danger alert—check it out!
A simple warning alert—check it out!
A simple info alert—check it out!
A simple light alert—check it out!
A simple dark alert—check it out!
Example Alert HTML
<div class="alert" role="alert">
    A simple alert—check it out!
    <button type="button" class="close">
        Close <span aria-hidden="true">×</span>
    </button>
</div>
Example Alert CSS
.alert {
    position: relative;
    padding: 0.5rem 0.5rem;
    margin-bottom: 1rem;
    display: flex;
    justify-content: space-between;
    align-items: center;
}
.alert .close {
    padding: 0.75rem 1.25rem;
    color: inherit;
    font-size: 1rem;
    display: flex;
    align-items: center;
}
.alert .close:hover,
.alert .close:focus {
    color: inherit;
}
.alert .close span[aria-hidden] {
    font-size: 1.5rem;
    margin-top: -0.2rem;
}
Alert accessibility considerations

Use of color: Using color to add meaning provides only a visual cue. Assistive technologies will not pick up this visual cue. To make the color meaningful to assistive technologies, either use visual or visually hidden text. See the section on visually hidden content.

Accessible Role: The role="alert" is used for important and usually time-sensitive messages.

For example:

  • An invalid value was entered into a form field
  • The user's login session is about to expire
  • The connection to the server was lost, local changes will not be saved

Use the alert role sparingly because of its intrusive nature. Less urgent dynamic changes can use methods like aria-live="polite" or other live region roles.

Use of dismissible "close" button: Close buttons are optional on the alert component. If you choose to use one, it must have ample clickable spacing around it (at least 40px by 40px). The close button must be a button element, or make use of the button role (role="button"). The close button should include the visible word "Close." If that is not possible, it must be present through other means, like visually hidden text.

Branding

Refer to the Hennepin County brand guide for more information regarding logos, colors, typography, and more.

Logos

Hennepin County's logo and wordmark communicate who we are immediately. Using each correctly is a key part of maintaining a professional and coherent brand.

Hennepin County "H"

Hennepin County Minnesota H Logo

Hennepin County's primary logo is the trusted Hennepin "H." This bold and highly recognizable symbol acts as a seal of approval on all Hennepin County branded materials. It must be used in all publications and promotional materials for county programs and services.

Hennepin County wordmark

Hennepin County Minnesota Wordmark Logo

The Hennepin County wordmark is a clear and uniform visual expression of the county name. It is designed to augment the Hennepin "H" in specific circumstances, but should never be used in place of it.

Colors

Our color system reflects the diversity and vibrancy of Hennepin County. We're not a one-dimensional region and our organization is not a single shade of blue.

Core colors

Our rich core colors set the tone of any design and are used in contrast with ample proportions of white and/or white space. We use the core colors to create anchor design elements such as color floods and shapes or as dense color masks for images.

Hennepin Blue
  • #0058a4
  • rgb(0, 88, 164)
White
  • #ffffff
  • rgb(255, 255, 255)
Rich Purple
  • #3e1151
  • rgb(62, 17, 81)
Deep Blue
  • #113c66
  • rgb(17, 60, 102)

Accent colors

Use accent sets to add small pops of color. We recommend using one accent color set in a single design to avoid a rainbow effect.

Blue 1
  • #00aeef
  • rgb(0, 174, 239)
Blue 2
  • #44c8f5
  • rgb(68, 200, 245)
Green 1
  • #9fcc3b
  • rgb(159, 204, 59)
Green 2
  • #cbdb2a
  • rgb(203, 219, 42)
Gold 1
  • #f7941e
  • rgb(247, 148, 30)
Gold 2
  • #ffcb05
  • rgb(255, 203, 5)
Red 1
  • #ce1432
  • rgb(206, 20, 50)
Red 2
  • #ef413d
  • rgb(239, 65, 61)

Neutral colors

Three neutral colors help warm and soften design elements.

Neutral Dark
  • #928884
  • rgb(146, 136, 132)
Neutral Medium
  • #a49e99
  • rgb(164, 158, 153)
Neutral Light
  • #ded3ce
  • rgb(222, 211, 206)

Use breadcrumbs to show the current page's location within a navigational hierarchy.

Example breadcrumb HTML
<nav aria-label="breadcrumb">
    <ol class="breadcrumb">
        <li class="breadcrumb-item"><a href="#">Home</a></li>
        <li class="breadcrumb-item"><a href="#">Library</a></li>
        <li class="breadcrumb-item active" aria-current="page">Data</li>
    </ol>
</nav>
Example breadcrumb CSS
.breadcrumb {
  display: flex;
  flex-wrap: wrap;
  padding: 0.75rem 1rem;
  margin-bottom: 1rem;
  list-style: none;
  border-radius: 0;
}
.breadcrumb-item {
  display: flex;
}
.breadcrumb-item + .breadcrumb-item {
  padding-left: 0.5rem;
}
.breadcrumb-item + .breadcrumb-item::before {
  display: inline-block;
  padding-right: 0.5rem;
  color: #6c757d;
  content: ">";
}
.breadcrumb-item + .breadcrumb-item:hover::before {
  text-decoration: underline;
}
.breadcrumb-item + .breadcrumb-item:hover::before {
  text-decoration: none;
}
.breadcrumb-item.active {
  color: #6c757d;
}
Breadcrumb accessibility considerations

Since breadcrumbs provide a navigation, it's a good idea to add a meaningful label such as aria-label="breadcrumb" to describe the type of navigation provided in the <nav> element, as well as applying an aria-current="page" to the last item of the set to indicate that it represents the current page.

For more information, see the WAI-ARIA Authoring Practices for the breadcrumb pattern.

Buttons

Use buttons to let a user take an action. An example is when asking a user to create a new profile, or to register.

Buttons have a primary and secondary action. A primary action is the primary option or task for the user. The secondary action is available, but used less often.

Language used for button labels should reflect what action the user will take. We recommend using verbs or adverbs and limiting words to two or three. If you need more content to describe the button action, consider using a tooltip for clarity.

Hennepin County branded buttons uses the following colors:

Primary

  • Default

    • background-color: #113c66
    • color: #ffffff

  • :hover

    • background-color: #44c8f5
    • color: #000000
Secondary

  • Default

    • background-color: #ffffff
    • color: #113c66

  • :hover

    • background-color: #44c8f5
    • color: #000000
Disabled

  • Primary


  • Secondary

    • opacity: 0.65

Button options

Default

Example Buttons HTML
<button class="btn btn-primary">Primary</button>
<button class="btn btn-outline-primary">Secondary</button>

Disabled state

Example Buttons HTML
<button class="btn btn-primary" disabled>Primary disabled</button>
<button class="btn btn-outline-primary" disabled>Secondary disabled</button>

Button sizing

Large

Example Large Buttons HTML
<button class="btn btn-primary btn-lg">Primary</button>
<button class="btn btn-outline-primary btn-lg">Secondary</button>

Small

Example Small Buttons HTML
<button class="btn btn-primary btn-sm">Primary</button>
<button class="btn btn-outline-primary btn-sm">Secondary</button>
Example button CSS
.btn {
    display: inline-block;
    font-weight: 400;
    color: #000;
    text-align: center;
    vertical-align: middle;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    background-color: transparent;
    border: 1px solid transparent;
    padding: 0.375rem 0.75rem;
    font-size: 1rem;
    line-height: 1.5;
    border-radius: 0;
    transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow
    0.15s ease-in-out;
}
.btn.disabled,
.btn:disabled {
    opacity: 0.65;
}
.btn-lg {
    padding: 0.5rem 1rem;
    font-size: 1.25rem;
    line-height: 1.5;
    border-radius: 0;
}
.btn-sm {
    padding: 0.25rem 0.5rem;
    font-size: 0.875rem;
    line-height: 1.5;
    border-radius: 0;
}
@media (prefers-reduced-motion: reduce) {
    .btn {
        transition: none;
    }
}
.btn:hover {
    color: #000;
    text-decoration: none;
}
.btn:focus,
.btn.focus {
    outline-color: #000;
}
.btn:not(:disabled):not(.disabled) {
    cursor: pointer;
}
a.btn.disabled {
    pointer-events: none;
}
.btn-primary {
    color: #fff;
    background-color: #113c66;
    border-color: #113c66;
}
.btn-outline-primary {
    color: #113c66;
    border-color: #113c66;
}
.btn-primary:hover,
.btn-primary:focus,
.btn-primary.focus,
.btn-outline-primary:hover,
.btn-outline-primary:focus,
.btn-outline-primary.focus {
    background-color: #44c8f5;
    border-color: #000;
    color: #000;
}
Button accessibility considerations

It is important to use semantic HTML for buttons. Not using semantic HTML for buttons risks making the buttons less accessible.

If not using a <button> tag consider:

  • Having to define event handlers for keydown and click/tap events
  • (If needed) adding a class and aria-label to signify the button is disabled
  • Adding in role="button"
  • Ensuring the element is focusable for all assistive technology
  • The need for aria attributes and other complexities that comes with it, as outlined by the W3C

Buttons vs. links

Assistive technologies read buttons and links differently. Both are focusable when using a keyboard. Buttons trigger with a space or enter key, but a link only triggers with an enter key.

Buttons tell the user they can take an action. Links offer the user a way to get to a new page related to the context of their current page.

digitala11y offered two points that can serve as a reminder of when to use a button over a link:

  • Use buttons when the user- action causes a change in either back-end or the front-end of the website. For example, submitting a form, opening a pop-up or a modal or a panel on the same page.
  • Use links when the user- action doesn't affect the website at all. In this, the users are readers or spectators of the site. For example, to navigate to the next page or an external source after viewing the content of the page.

Collapse

Collapsible panels are a great way to toggle the visibility of content. Hennepin County often refers to these as Drawers.

Some placeholder content for the first collapse component of this multi-collapse example. This panel is hidden by default but revealed when the user activates the relevant trigger.

  • Lorem ipsum dolor sit amet
  • Consectetur adipiscing elit
  • Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua
  • Ut enim ad minim veniam
  • Quis nostrud exercitation ullamco laboris

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Some placeholder content for the second collapse component of this multi-collapse example. This panel is hidden by default but revealed when the user activates the relevant trigger.

  • Lorem ipsum dolor sit amet
  • Consectetur adipiscing elit
  • Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua
  • Ut enim ad minim veniam
  • Quis nostrud exercitation ullamco laboris

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Some placeholder content for the third collapse component of this multi-collapse example. This panel is hidden by default but revealed when the user activates the relevant trigger.

  • Lorem ipsum dolor sit amet
  • Consectetur adipiscing elit
  • Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua
  • Ut enim ad minim veniam
  • Quis nostrud exercitation ullamco laboris

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Some placeholder content for the fourth collapse component of this multi-collapse example. This panel is hidden by default but revealed when the user activates the relevant trigger.

  • Lorem ipsum dolor sit amet
  • Consectetur adipiscing elit
  • Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua
  • Ut enim ad minim veniam
  • Quis nostrud exercitation ullamco laboris

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Example Collapse HTML
<button class="collapsed" data-target="#multiCollapseExample1" role="button" aria-expanded="false" aria-controls="multiCollapseExample1">
    Toggle first element
</button>
<div class="collapse" id="multiCollapseExample1">
    ...
</div>
Example Collapse CSS
.collapse:not(.show) {
    display: none;
}
.collapsing {
    position: relative;
    height: 0;
    overflow: hidden;
    transition: height 0.35s ease;
}
@media (prefers-reduced-motion: reduce) {
    .collapsing {
        transition: none;
    }
}
.drawer > .btn,
.drawer > .btn.collapsed:hover,
.drawer > .btn.collapsed:focus {
    background: #44c8f5;
}
.drawer > .btn::before {
    content: '';
    background: url('minus.svg') no-repeat;
    height: 15px;
    width: 15px;
    display: inline-block;
    margin-right: 1rem;
}
.drawer > .btn.collapsed {
    background: #dee2e6;
}
.drawer > .btn.collapsed::before {
    background-image: url('plus.svg');
}
Example Collapse JavaScript
$('.drawer > .btn').on('click', function() {
    ...
    $(this).removeClass('collapsed').attr('aria-expanded', 'true');
    ...
});
Collapse accessibility considerations

Reduced motion: The animation effect of this component should depend on the prefers-reduced-motion media query. For more information see the accessibility section.

Accessible Role: If the control element's HTML element is not a <button>, you must add the attribute role="button" to the element.

Accessible state: This attribute conveys the state of the collapsible element to assistive technologies. Based on the open or closed state, toggle the aria-expanded attribute (true or false).

Accessible control: Add the aria-controls attribute to any control that targets a single collapsible element. The value of that attribute must contain the id of the collapsible element. Assistive technologies use this attribute to jump to the collapsible element.

Use footers to signify the end of a section especially the end of an HTML document.

For the sake of this document, we are only going to focus on footers used to signify the end of an HTML page.

Footers may contain contact information, navigation items, and social media links.

At a minimum, footers must display the Hennepin County "H" logo in the lower right corner. They also must link to the county's privacy policy and display copyright information.

Example Footer HTML
<footer class="footer">
    <a href="https://www.hennepin.us/">
        <img src="hennepin-county-minnesota-H-logo-blue.svg" alt="Hennepin County, Minnesota">
    </a>
    <p>
        <a href="https://www.hennepin.us/your-government/open-government/website-privacy-security">Privacy</a>
        <span>|</span>
        © 2022 Hennepin County, Minnesota
    </p>
</footer>

Forms

Textual controls

Textual controls include form elements that are textual inputs or textareas. For example: input[type="text|email|password|search|tel|url"] and textarea.

Example textual control HTML
<label for="exampleInput">First name</label>
<input type="text" class="form-control" id="exampleInput">
<label for="exampleTextarea">Comment</label>
<textarea class="form-control" id="exampleTextarea" rows="3"></textarea>
Example textual control CSS
.form-control {
    display: block;
    padding: 0.375rem 0.75rem;
    font-size: 1rem;
    font-weight: 400;
    line-height: 1.5;
    background-color: #fff;
    background-clip: padding-box;
    border: 1px solid #ced4da;
    border-radius: 0;
}

Checkboxes and radios

Custom checkboxes and radios create a consistent experience across browsers and devices.

Checkboxes

The focus indicator for checkboxes wraps around the entire label and control element.

Checkbox Examples:
Example checkbox HTML
<div class="form-group custom-control custom-checkbox">
    <div class="custom-focus">
        <input type="checkbox" class="custom-control-input" id="customCheck1">
        <label class="custom-control-label" for="customCheck1">Check this checkbox</label>
    </div>
</div>
<div class="form-group custom-control custom-checkbox">
    <div class="custom-focus">
        <input type="checkbox" class="custom-control-input" id="customCheck2">
        <label class="custom-control-label" for="customCheck2">Check this checkbox too</label>
    </div>
</div>
Example checkbox CSS
.custom-control-label {
    position: relative;
    margin-bottom: 0;
    vertical-align: top;
}
.custom-control-label::before {
    position: absolute;
    top: 0.25rem;
    left: -1.5rem;
    display: block;
    width: 1rem;
    height: 1rem;
    pointer-events: none;
    content: "";
    background-color: #fff;
    border: #adb5bd solid 1px;
}
.custom-control-label::after {
    position: absolute;
    top: 0.25rem;
    left: -1.5rem;
    display: block;
    width: 1rem;
    height: 1rem;
    content: "";
    background: no-repeat 50% / 50% 50%;
}
.custom-checkbox .custom-control-label::before {
    border-radius: 0;
}
.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after {
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e");
}
.custom-focus {
    display: inline-block;
    padding-left: 1.5rem;
    margin-left: -1.5rem;
}
@supports selector(:focus-within) {
    .custom-focus:focus-within {
        outline:4px solid;
        outline-offset:4px;
    }
    .custom-focus .custom-control-input:focus ~ .custom-control-label::before,
    .custom-focus .custom-control-input:focus ~ .custom-control-label::after {
        outline:none;
    }
}

Radios

The focus indicator for radios wraps around the entire label and control element.

Radio Examples:
Example radio HTML
<div class="form-group custom-control custom-radio">
    <div class="custom-focus">
        <input type="radio" id="customRadio1" name="customRadio" class="custom-control-input">
        <label class="custom-control-label" for="customRadio1">Toggle this custom radio</label>
    </div>
</div>
<div class="form-group custom-control custom-radio">
    <div class="custom-focus">
        <input type="radio" id="customRadio2" name="customRadio" class="custom-control-input">
        <label class="custom-control-label" for="customRadio2">Or toggle this other custom radio</label>
    </div>
</div>
Example radio CSS
.custom-control-label {
    position: relative;
    margin-bottom: 0;
    vertical-align: top;
}
.custom-control-label::before {
    position: absolute;
    top: 0.25rem;
    left: -1.5rem;
    display: block;
    width: 1rem;
    height: 1rem;
    pointer-events: none;
    content: "";
    background-color: #fff;
    border: #adb5bd solid 1px;
}
.custom-control-label::after {
    position: absolute;
    top: 0.25rem;
    left: -1.5rem;
    display: block;
    width: 1rem;
    height: 1rem;
    content: "";
    background: no-repeat 50% / 50% 50%;
}
.custom-radio .custom-control-label::before {
    border-radius: 50%;
}
.custom-radio .custom-control-input:checked ~ .custom-control-label::after {
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e");
}
.custom-focus {
    display: inline-block;
    padding-left: 1.5rem;
    margin-left: -1.5rem;
}
@supports selector(:focus-within) {
    .custom-focus:focus-within {
        outline:4px solid;
        outline-offset:4px;
    }
    .custom-focus .custom-control-input:focus ~ .custom-control-label::before,
    .custom-focus .custom-control-input:focus ~ .custom-control-label::after {
        outline:none;
    }
}

Selects

Example select HTML
<label for="selectOne">Choose an option</label>
<select id="selectOne" class="custom-select">
    <option selected>Open this select menu</option>
    <option value="1">One</option>
    <option value="2">Two</option>
    <option value="3">Three</option>
</select>
<label for="selectMulti">Choose multiple options</label>
<select id="selectMulti" class="custom-select" multiple>
    <option value="1">One</option>
    <option value="2">Two</option>
    <option value="3">Three</option>
</select>
Example select CSS
.custom-select {
    display: inline-block;
    width: 100%;
    height: calc(1.5em + 0.75rem + 2px);
    padding: 0.375rem 1.75rem 0.375rem 0.75rem;
    font-size: 1rem;
    font-weight: 400;
    line-height: 1.5;
    color: #495057;
    vertical-align: middle;
    background: #fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4
    5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px;
    border: 1px solid #ced4da;
    border-radius: 0;
    appearance: none;
}

File browser

Upload files
File drop
Example file browser HTML
<div class="label-secondary" data-for="inputFile">Upload files</div>
<div class="custom-file">
    <input type="file" id="inputFile" class="custom-file-input" multiple>
    <label for="inputFile" class="custom-file-label">
        <span>Choose files...</span>
    </label>
</div>

<div class="label-secondary" data-for="inputFileDrop">File drop</div>
<div class="custom-file file-drop">
    <input type="file" id="inputFileDrop" class="custom-file-input" multiple>
    <label for="inputFileDrop" class="custom-file-label">
        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" viewBox="0 0 20 17"><path fill="#113c66" d="M10 0l-5.2 4.9h3.3v5.1h3.8v-5.1h3.3l-5.2-4.9zm9.3 11.5l-3.2-2.1h-2l3.4 2.6h-3.5c-.1 0-.2.1-.2.1l-.8 2.3h-6l-.8-2.2c-.1-.1-.1-.2-.2-.2h-3.6l3.4-2.6h-2l-3.2 2.1c-.4.3-.7 1-.6 1.5l.6 3.1c.1.5.7.9 1.2.9h16.3c.6 0 1.1-.4 1.3-.9l.6-3.1c.1-.5-.2-1.2-.7-1.5z"></path></svg>
        <span>Click or drop files here</span>
    </label>
</div>
Example file browser CSS
.custom-file {
    position: relative;
    display: inline-block;
    width: 100%;
    height: calc(1.5em + 0.75rem + 2px);
    margin-bottom: 0;
}
.custom-file.file-drop {
    height: 20vh;
}
.custom-file.file-drop .custom-file-label {
    height: 100%;
    border-style: dashed;
    border-width: 2px;
    background-color: #f8f9fa;
    display: flex;
    align-items: center;
    justify-content: center;
    
}
.custom-file.file-drop .custom-file-label::after {
    display: none;
}
.custom-file.file-drop .custom-file-input {
    height: 100%;
}
.custom-file.file-drop .custom-file-input:hover + .custom-file-label {
    background-color: #fafbfc;
    border-color: #b8c1ca;
}
.custom-file-input {
    position: relative;
    z-index: 2;
    width: 100%;
    height: calc(1.5em + 0.75rem + 2px);
    margin: 0;
    opacity: 0;
}
.custom-file-label,
.custom-file-label::after {
    position: absolute;
    top: 0;
    right: 0;
    z-index: 1;
    height: calc(1.5em + 0.75rem + 2px);
    padding: 0.375rem 0.75rem;
    font-weight: 400;
    line-height: 1.5;
    color: #495057;
    background-color: #fff;
    border-radius: 0;

.custom-file-label {
    left: 0;
    overflow: hidden;
    border: 1px solid #ced4da;
}
.custom-file-label::after {
    bottom: 0;
    z-index: 3;
    display: block;
    height: calc(1.5em + 0.75rem);
    content: "Browse";
    background-color: #e9ecef;
    border-left: inherit;
}
.custom-control-label::before,
.custom-file-label,
.custom-select {
    transition: background-color 0.15s ease-in-out,
                border-color     0.15s ease-in-out,
                box-shadow       0.15s ease-in-out;
}
@media (prefers-reduced-motion: reduce) {
    .custom-control-label::before,
    .custom-file-label {
        transition: none;
    }
}
Example file browser JavaScript
$('input[type="file"]').on('change', function () {
    var $label = $(this).next('.custom-file-label').find('span');
    if (!this.files) return;
    var fileNames = '';
    if (this.files.length === 0) {
        if ($(this).parent('.file-drop').length) {
            fileNames = (this.multiple) ? 'Click or drop files here' : 'Click or drop file here';
        } else {
            fileNames = (this.multiple) ? 'Choose files...' : 'Choose file...';
        }
    } else if (this.files.length === 1) {
        fileNames = this.files[0].name;
    } else {
        fileNames = this.files.length + ' files';
    }
    $label.html(fileNames);
});

Form validation

Form validation should take place on the front and back end.

Include these features in form validation:

Please provide a first name.
Please provide a last name.
Please provide a city.
Please select a state.
Please provide a 5 digit zip code.
You must agree before submitting.
Example form validation HTML
<form id="validation" novalidate>
    <div class="form-row">
        <div class="col-md-6 mb-3">
            <label for="name1">First name</label>
            <input type="text" class="form-control" id="name1" aria-describedby="name1Feedback" required>
            <div data-for="name1" id="name1Feedback" class="label-secondary invalid-feedback">
                Please provide a first name.
            </div>
        </div>
        <div class="col-md-6 mb-3">
            <label for="name2">Last name</label>
            <input type="text" class="form-control" id="name2" aria-describedby="name2Feedback" required>
            <div data-for="name2" id="name2Feedback" class="label-secondary invalid-feedback">
                Please provide a last name.
            </div>
        </div>
    </div>
    <div class="form-row">
        <div class="col-md-6 mb-3">
            <label for="city">City</label>
            <input type="text" class="form-control" id="city" aria-describedby="cityFeedback" required>
            <div data-for="city" id="cityFeedback" class="label-secondary invalid-feedback">
                Please provide a city.
            </div>
        </div>
        <div class="col-md-3 mb-3">
            <label for="state">State</label>
            <select class="custom-select" id="state" aria-describedby="stateFeedback" required>
                <option selected disabled value="">Choose...</option>
                <option value="AL">Alabama</option>
                ...
                <option vlaue="WY">Wyoming</option>
            </select>
            <div data-for="state" id="stateFeedback" class="label-secondary invalid-feedback">
                Please select a state.
            </div>
        </div>
        <div class="col-md-3 mb-3">
            <label for="zip">Zip</label>
            <input type="text" class="form-control" id="zip" aria-describedby="zipFeedback" required>
            <div data-for="zip" id="zipFeedback" class="label-secondary invalid-feedback">
                Please provide a 5 digit zip code.
            </div>
        </div>
    </div>
    <div class="form-group custom-control custom-checkbox">
        <div class="custom-focus">
            <input type="checkbox" class="custom-control-input" id="agreement" aria-describedby="invalidCheck3Feedback"
                required>
            <label class="custom-control-label" for="agreement">Agree to terms and conditions</label>
        </div>
        <div data-for="agreement" id="invalidCheck3Feedback" class="label-secondary invalid-feedback">
            You must agree before submitting.
        </div>
    </div>
    <div class="form-group">
        <button class="btn btn-primary" type="submit">Submit form</button>
    </div>
    <div id="validationSuccess" class="alert alert-dismissible alert-success" role="alert" style="display:none"></div>
</form>
Example form validation CSS
.form-control.is-invalid {
  border-color: #ce1432;
  padding-right: calc(1.5em + 0.75rem);
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='28' height='28' viewBox='0 0 28 28'%3e%3cpath style='fill:%23ce1432;stroke:%23ce1432;stroke-width:4;stroke-miterlimit:10;' d='M25.5,26c-0.1,0-0.3,0-0.4-0.1l-23-23C2,2.7,2,2.3,2.1,2.1s0.5-0.2,0.7,0l23,23c0.2,0.2,0.2,0.5,0,0.7C25.8,26,25.6,26,25.5,26z'/%3e%3cpath style='fill:%23ce1432;stroke:%23ce1432;stroke-width:4;stroke-miterlimit:10;' d='M2.5,26c-0.1,0-0.3,0-0.4-0.1c-0.2-0.2-0.2-0.5,0-0.7l23-23c0.2-0.2,0.5-0.2,0.7,0s0.2,0.5,0,0.7l-23,23C2.8,26,2.6,26,2.5,26z'/%3e%3c/svg%3e");
  background-repeat: no-repeat;
  background-position: right calc(0.375em + 0.1875rem) center;
  background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
}
.form-control.is-invalid:focus {
  border-color: #ce1432;
  box-shadow: 0 0 0 0.2rem rgba(206, 20, 50, 0.25);
}
textarea.form-control.is-invalid {
  padding-right: calc(1.5em + 0.75rem);
  background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);
}
.custom-select.is-invalid {
  border-color: #ce1432;
  padding-right: calc(0.75em + 2.3125rem);
  background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='28' height='28' viewBox='0 0 28 28'%3e%3cpath style='fill:%23ce1432;stroke:%23ce1432;stroke-width:4;stroke-miterlimit:10;' d='M25.5,26c-0.1,0-0.3,0-0.4-0.1l-23-23C2,2.7,2,2.3,2.1,2.1s0.5-0.2,0.7,0l23,23c0.2,0.2,0.2,0.5,0,0.7C25.8,26,25.6,26,25.5,26z'/%3e%3cpath style='fill:%23ce1432;stroke:%23ce1432;stroke-width:4;stroke-miterlimit:10;' d='M2.5,26c-0.1,0-0.3,0-0.4-0.1c-0.2-0.2-0.2-0.5,0-0.7l23-23c0.2-0.2,0.5-0.2,0.7,0s0.2,0.5,0,0.7l-23,23C2.8,26,2.6,26,2.5,26z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
}
.form-check-input.is-invalid ~ .form-check-label {
  color: #ce1432;
}
.custom-control-input.is-invalid ~ .custom-control-label {
  color: #ce1432;
}
.custom-control-input.is-invalid ~ .custom-control-label::before {
  border-color: #ce1432;
}
.custom-control-input.is-invalid:checked ~ .custom-control-label::before {
  border-color: #ea2b4a;
  background-color: #ea2b4a;
}
.custom-control-input.is-invalid:focus ~ .custom-control-label::before {
  box-shadow: 0 0 0 0.2rem rgba(206, 20, 50, 0.25);
}
.custom-control-input.is-invalid:focus:not(:checked) ~ .custom-control-label::before {
  border-color: #ce1432;
}
custom-file-input.is-invalid ~ .custom-file-label {
  border-color: #ce1432;
}
.custom-file-input.is-invalid:focus ~ .custom-file-label {
  border-color: #ce1432;
  box-shadow: 0 0 0 0.2rem rgba(206, 20, 50, 0.25);
}
.custom-focus.is-invalid .custom-control-label::before {
  border-color: #ce1432;
}
Example form validation JavaScript
var $form = $('#validation'),
    $name1 = $('#name1'),
    $name2 = $('#name2'),
    $state = $('#state'),
    $city = $('#city'),
    $zip = $('#zip'),
    $agreement = $('#agreement'),
    $validationSuccess = $('#validationSuccess');

var validate = {
    name1: function () {
        if ($name1.val().length) {
            $name1.removeClass('is-invalid');
            $name1.attr('aria-invalid', 'false');
            return true;
        } else {
            $name1.addClass('is-invalid');
            $name1.attr('aria-invalid', 'true');
            return false;
        }
    },
    name2: function () {
        if ($name2.val().length) {
            $name2.removeClass('is-invalid');
            $name2.attr('aria-invalid', 'false');
            return true;
        } else {
            $name2.addClass('is-invalid');
            $name2.attr('aria-invalid', 'true');
            return false;
        }
    },
    city: function () {
        if ($city.val().length) {
            $city.removeClass('is-invalid');
            $city.attr('aria-invalid', 'false');
            return true;
        } else {
            $city.addClass('is-invalid');
            $city.attr('aria-invalid', 'true');
            return false;
        }
    },
    state: function () {
        if ($state.val() && $state.val().length === 2) {
            $state.removeClass('is-invalid');
            $state.attr('aria-invalid', 'false');
            return true;
        } else {
            $state.addClass('is-invalid');
            $state.attr('aria-invalid', 'true');
            return false;
        }
    },
    zip: function () {
        if (!/\D/.test($zip.val()) && $zip.val().length === 5) {
            $zip.removeClass('is-invalid');
            $zip.attr('aria-invalid', 'false');
            return true;
        } else {
            $zip.addClass('is-invalid');
            $zip.attr('aria-invalid', 'true');
            return false;
        }
    },
    agreement: function () {
        if ($agreement.is(":checked")) {
            $agreement.parent().removeClass('is-invalid');
            $agreement.parent().attr('aria-invalid', 'false');
            return true;
        } else {
            $agreement.parent().addClass('is-invalid');
            $agreement.parent().attr('aria-invalid', 'true');
            return false;
        }
    }
};
$(document).on('keyup', '#name1.is-invalid, #name2.is-invalid, #city.is-invalid, #zip.is-invalid', function () {
    if ($(this).is('#name1')) return validate.name1();
    if ($(this).is('#name2')) return validate.name2();
    if ($(this).is('#city')) return validate.city();
    if ($(this).is('#zip')) return validate.zip();
});
$(document).on('change', '#state.is-invalid, .is-invalid #agreement',  function () {
    if ($(this).is('#state')) return validate.state();
    if ($(this).is('#agreement')) return validate.agreement();
});
$form.on('submit', function (e) {
    e.preventDefault();
    var isInvalid = false;
    if (!validate.name1()) isInvalid = true;
    if (!validate.name2()) isInvalid = true;
    if (!validate.city()) isInvalid = true;
    if (!validate.state()) isInvalid = true;
    if (!validate.zip()) isInvalid = true;
    if (!validate.agreement()) isInvalid = true;

    if (isInvalid) {
        var $firstError = $form.find('.is-invalid').eq(0);
        var errorScroll = $firstError.offset().top - 100;
        var scrollSpeed = (window.matchMedia('(prefers-reduced-motion: reduce)')) ? 0 : 500;
        $("html, body").animate({
            scrollTop: errorScroll
        }, scrollSpeed);
        $firstError.focus();
    } else {
        $validationSuccess.append('<div class="alert-content">Form successfully submitted</div><button type="button" class="close" data-dismiss="alert">Close <span aria-hidden="true">×</span></button>').fadeIn();
    }
});

Date input

We use the native HTML5 input[type="date"] for date input types.

This input lets browsers handle the date picker function while following accessibility standards.

Example date input HTML
<label for="inputDate">Date</label>
<input type="date" id="inputDate" class="form-control">
Example select CSS
.form-control {
    display: block;
    padding: 0.375rem 0.75rem;
    font-size: 1rem;
    font-weight: 400;
    line-height: 1.5;
    background-color: #fff;
    background-clip: padding-box;
    border: 1px solid #ced4da;
    border-radius: 0;
}

Labels

Visible form labels are crucial to making forms accessible.

Form controls should always have a visible label. (Emphasis on the word "visible"). It's crucial for users with cognitive disabilities. It's also crucial for those who use speech recognition software. It also increases usability.

Example label HTML
<label for="labelExample">Example label</label>
<input type="text" id="labelExample" class="form-control">
Example select CSS
label {
    display: inline-block;
    margin-bottom: 0.5rem;
}

Help text

Your password must be 8-20 characters long, contain letters and numbers, and must not contain spaces, special characters, or emoji.
Example help text HTML
<label for="inputPassword">Password</label>
<input type="password" id="inputPassword" class="form-control" aria-describedby="passwordHelp">
<div id="passwordHelp" class="form-text">
    Your password must be 8-20 characters long, contain letters and numbers, and must not contain spaces, special
    characters, or emoji.
</div>
Example help text CSS
.form-text {
    margin-top: 0.25rem;
    color: #6c757d;
}
Form accessibility considerations

Labels:You must programmatically associate labels with their fields:

  • Label text must be meaningful.
  • You may use icons as visual labels instead of text. Make sure the icon is visually self-evident. Make sure to also associate it with the field for assistive technologies.
  • Don't use placeholder text as the only way to provide a label for a text input.
  • Place labels next to their corresponding elements.
  • A label should be next to its corresponding element in the DOM.

Form validation: Give focus to inputs that have an error. Give them an attribute of aria-invalid="true." Use aria-describedby to associate error messages with form fields to relay the error to screen readers. Make the error messages visible and next to the inputs.

Help text: Use aria-describedby to associate help text with the form control it relates to. Assistive technologies can then announce the help text when the control receives focus.

Modals dialogs require the user to take an action. They direct a user’s attention to important information.

Modals should include:

Example Modal HTML
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <div class="h5 modal-title" id="exampleModalLabel">Modal title</div>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">×</span>
                </button>
            </div>
            <div class="modal-body">
                ...
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-outline-primary" data-dismiss="modal">Close</button>
                <button type="button" class="btn btn-primary">Save changes</button>
            </div>
        </div>
    </div>
</div>
Example Modal CSS
.modal {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 1050;
  display: none;
  width: 100%;
  height: 100%;
  overflow: hidden;
  outline: 0;
}
.modal-dialog {
  position: relative;
  width: auto;
  margin: 0.5rem;
  pointer-events: none;
}
.modal.fade .modal-dialog {
  transition: transform 0.3s ease-out;
  transform: translate(0, -50px);
}
@media (prefers-reduced-motion: reduce) {
  .modal.fade .modal-dialog {
    transition: none;
  }
}
.modal.show .modal-dialog {
  transform: none;
}
.modal-content {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  pointer-events: auto;
  background-color: #fff;
  background-clip: padding-box;
  border: 1px solid rgba(0, 0, 0, 0.2);
  border-radius: 0;
  outline: 0;
}
.modal-backdrop {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 1040;
  width: 100vw;
  height: 100vh;
  background-color: #000;
}
.modal-backdrop.fade {
  opacity: 0;
}
.modal-backdrop.show {
  opacity: 0.5;
}
.modal-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  padding: 1rem 1rem;
  border-top-left-radius: 0;
  border-top-right-radius: 0;
  margin: 0;
}
.modal-header .close {
  padding: 1rem 1rem;
  margin: -1rem -1rem -1rem auto;
}
.modal-title {
  margin: 0;
  line-height: 1.5;
}
.modal-body {
  position: relative;
  flex: 1 1 auto;
  padding: 1rem;
}
.modal-footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  padding: 0.75rem;
  border-bottom-right-radius: 0;
  border-bottom-left-radius: 0;
}
.modal-footer > * {
  margin: 0.25rem;
}
.modal-scrollbar-measure {
  position: absolute;
  top: -9999px;
  width: 50px;
  height: 50px;
  overflow: scroll;
}
@media (min-width: 576px) {
  .modal-dialog {
    max-width: 500px;
    margin: 1.75rem auto;
  }
}
Example modal JS
_showElement(relatedTarget) {
    ...
    this._element.style.display = 'block';
    this._element.removeAttribute('aria-hidden');
    this._element.setAttribute('aria-modal', true);
    this._element.setAttribute('role', 'dialog');
    ...
    $(this._element).addClass('show');
    ...
}
Modal accessibility considerations

Accessible role: You need to add role="dialog" to the modal container.

Accessible ARIA attributes: Be sure to add aria-labelledby="...", referencing the modal title, to modal container. Additionally, you may give a description of your modal dialog with aria-describedby on the modal container. If the modal is only visually hidden from the page, you will need to include aria-hidden="true" as well. That value must be changed to false, or the attribute must be removed once the modal is opened.

Use of dismissible "close" button: It must have ample clickable spacing around it (at least 40px by 40px). The close button must be a button element, or make use of the button role (role="button"). If the close button does include the visible word "Close", then it must be present through alternative means (such as visually hidden text that is accessible to assistive technology).

Keyboard experience: There are a couple considerations you need to make for keyboard users.

  • Keyboard trap: Typically when developing with accessibility in mind, you want to avoid keyboard traps, but modals are an exception to the rule. You must create a keyboard trap when modals are open to force keyboard users to interact with the dialog, and prevent them from getting lost in the content behind the modal.
  • Close on esc keypress: It's always good UX practice to allow the esc key to close modal windows, but it's especially critical when we are creating a keyboard trap. This will allow keyboard users to close modals with ease.

These let keyboard users skip navigation or other repeated components across many pages.

To see a working example, tab around this site (especially around navigation controls). Skip links should be visually hidden until they receive focus.

Example of a static skip link (its position and display have been overridden):

Example Skip Link HTML
<body>
    <a class="skip-to-link" href="#main">
        Skip to main content
    </a>
    <nav id="nav">
        ...
    </nav>
    <main id="main">
        ...
    </main>
</body>
Example Skip Link CSS
.skip-to-link {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
    top: 4px;
    left: 4px;
    color: #fff;
    background: #ce1432;
    z-index: 999;
    opacity: 0;
    pointer-events: none;
    font-size: 1rem;
    display: inline-block;
}
.skip-to-link:focus,
.skip-to-link:hover,
.skip-to-link:active {
    color: #fff;
    opacity: 1;
    pointer-events: auto;
    padding: 8px;
    margin: 0;
    height: unset;
    width: unset;
    clip: unset;
    overflow: unset;
}

Tables

Tables are meant for tabular data that preserves relationships within the information.

Tabular information displays in columns and rows. It has logical relationships among data like text, numbers and images. Columns and rows must be identified for the logical relationships to be perceived.

Responsive tables

Responsive tables can be set to side scroll by setting overflow-x: auto; to the parent element of a table.

Example responsive table with side scroll
  Col 1 Col 2 Col 3 Col 4 Col 5
Row A A1 A2 A3 A4 A5
Row B B1 B2 B3 B4 B5
Row C C1 C2 C3 C4 C5

Responsive tables can change their display properties to stack on smaller screens. This second example is more complex and needs to follow all accessibility guidelines.

Example responsive table with collapsing rows
  Col 1 Col 2 Col 3 Col 4 Col 5
Row A A1 A2 A3 A4 A5
Row B B1 B2 B3 B4 B5
Row C C1 C2 C3 C4 C5

Table styles

Table stripes

Table stripes should be used to help guide a user's eyes across wide tables with a lot of data.

Example table with striped rows
  Col 1 Col 2 Col 3 Col 4 Col 5
Row A A1 A2 A3 A4 A5
Row B B1 B2 B3 B4 B5
Row C C1 C2 C3 C4 C5

Table borders

Borders are another acceptable approach to help guide a user's eyes across tables with a lot of data. This is especially true when using colspan.

Example table with borders
  Col 1 Col 2 Col 3 Col 4 Col 5
Row A A1 A2 A3 A4 A5
Row B B1 B2 B3 B4 B5
Row C C1 C2 - C4 C5
Example Table HTML
<div class="table-responsive">
    <table class="table">
        <caption class="sr-only">Example responsive table with side scroll</caption>
        <thead>
            <tr>
                <td scope="col">&nbsp;</td>
                <th scope="col">Col 1</th>
                <th scope="col">Col 2</th>
                <th scope="col">Col 3</th>
                <th scope="col">Col 4</th>
                <th scope="col">Col 5</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <th scope="row">Row A</th>
                <td>A1</td>
                <td>A2</td>
                <td>A3</td>
                <td>A4</td>
                <td>A5</td>
            </tr>
            <tr>
                <th scope="row">Row B</th>
                <td>B1</td>
                <td>B2</td>
                <td>B3</td>
                <td>B4</td>
                <td>B5</td>
            </tr>
            <tr>
                <th scope="row">Row C</th>
                <td>C1</td>
                <td>C2</td>
                <td>C3</td>
                <td>C4</td>
                <td>C5</td>
            </tr>
        </tbody>
    </table>
</div>
Example Table CSS
.table {
  width: 100%;
  margin-bottom: 1rem;
  color: #000;
}
.table th,
.table td {
  padding: 0.75rem;
  vertical-align: top;
  border-top: 1px solid #dee2e6;
}
.table thead th {
  vertical-align: bottom;
  border-bottom: 2px solid #dee2e6;
}
.table tbody + tbody {
  border-top: 2px solid #dee2e6;
}
.table-bordered {
  border: 1px solid #dee2e6;
}
.table-bordered th,
.table-bordered td {
  border: 1px solid #dee2e6;
}
.table-bordered thead th,
.table-bordered thead td {
  border-bottom-width: 2px;
}
.table-striped tbody tr:nth-of-type(odd) {
  background-color: rgba(0, 0, 0, 0.05);
}
@media (max-width: 575.98px) {
  .table-responsive-sm {
    display: block;
    width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  .table-responsive-sm > .table-bordered {
    border: 0;
  }
}
@media (max-width: 767.98px) {
  .table-responsive-md {
    display: block;
    width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  .table-responsive-md > .table-bordered {
    border: 0;
  }
}
@media (max-width: 991.98px) {
  .table-responsive-lg {
    display: block;
    width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  .table-responsive-lg > .table-bordered {
    border: 0;
  }
}
@media (max-width: 1199.98px) {
  .table-responsive-xl {
    display: block;
    width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  .table-responsive-xl > .table-bordered {
    border: 0;
  }
}
.table-responsive {
  display: block;
  width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.table-responsive > .table-bordered {
  border: 0;
}

Sortable Tables

Sortable tables should be used to help users sort data by the column headers, in ascending or descending order.

Example table with sortable column headers
1 Myk 33 Purple
2 Hannah 21 Blue
3 Salim 18 Green
4 Greg 45 Orange
5 Caitlin 58 Red
6 Cyan 35 Yellow
Example Sortable Table HTML
<div class="table-responsive">
    <table class="table table-sort ascending" role="grid" aria-readonly="true">
        <caption>Example table with sortable column headers</caption>
        <thead class="thead-light">
            <tr role="row">
                <th role="columnheader" scope="col" aria-sort="ascending">
                    <button class="btn btn-table-sort sorted">
                        Index
                        <svg version="1.1" class="sort-arrows" x="0px" y="0px" viewBox="0 0 401.998 401.998" style="enable-background:new 0 0 401.998 401.998;" xml:space="preserve" aria-hidden="true">
                            <path class="up" d="M73.092,164.452h255.813c4.949,0,9.233-1.807,12.848-5.424c3.613-3.616,5.427-7.898,5.427-12.847 c0-4.949-1.813-9.229-5.427-12.85L213.846,5.424C210.232,1.812,205.951,0,200.999,0s-9.233,1.812-12.85,5.424L60.242,133.331 c-3.617,3.617-5.424,7.901-5.424,12.85c0,4.948,1.807,9.231,5.424,12.847C63.863,162.645,68.144,164.452,73.092,164.452z" />
                            <path class="down" d="M328.905,237.549H73.092c-4.952,0-9.233,1.808-12.85,5.421c-3.617,3.617-5.424,7.898-5.424,12.847 c0,4.949,1.807,9.233,5.424,12.848L188.149,396.57c3.621,3.617,7.902,5.428,12.85,5.428s9.233-1.811,12.847-5.428l127.907-127.906 c3.613-3.614,5.427-7.898,5.427-12.848c0-4.948-1.813-9.229-5.427-12.847C338.139,239.353,333.854,237.549,328.905,237.549z" />
                        </svg>
                    </button>
                </th>
                <th role="columnheader" scope="col">
                    <button class="btn btn-table-sort">
                        Name
                        ...
                    </button>
                </th>
                <th role="columnheader" scope="col">
                    <button class="btn btn-table-sort">
                        Age
                        ...
                    </button>
                </th>
                <th role="columnheader" scope="col">
                    <button class="btn btn-table-sort">
                        Favorite Color
                        ...
                    </button>
                </th>
            </tr>
        </thead>
        <tbody>
            <tr role="row">
                <th scope="row" role="rowheader">1</th>
                <td role="gridcell">Myk</td>
                <td role="gridcell">33</td>
                <td role="gridcell">Purple</td>
            </tr>
            <tr role="row">
                ...
            </tr>
            <tr role="row">
                ...
            </tr>
            <tr role="row">
                ...
            </tr>
            <tr role="row">
                ...
            </tr>
            <tr role="row">
                ...
            </tr>
        </tbody>
    </table>
    <span id="tableSortlive" aria-live="polite" readCaptions="false" class="sr-only"></span>
</div>
Example Sortable Table CSS
.table-sort tr th[role=columnheader] {
    padding: 0;
    position: relative;
}
.table-sort tr th[role=columnheader]:focus-within {
    z-index: 9;
}
.table-sort .btn-table-sort {
    padding: .75rem;
    width: 100%;
    text-align: left;
    white-space: nowrap
}
.table-sort .btn-table-sort:hover {
    background-color: #44c8f5
}
.table-sort .btn-table-sort .sort-arrows {
    display: inline-block;
    width: 1em;
    height: 1em;
    margin-left: .5em;
}
.table-sort .btn-table-sort:hover .sort-arrows .down,
.table-sort .btn-table-sort:hover .sort-arrows .up,
.table-sort .btn-table-sort:focus .sort-arrows .down,
.table-sort .btn-table-sort:focus .sort-arrows .up,
.table-sort.ascending .btn-table-sort.sorted .sort-arrows .up,
.table-sort.descending .btn-table-sort.sorted .sort-arrows .down {
    opacity: 1;
}
.table-sort .btn-table-sort .sort-arrows .down,.table-sort .btn-table-sort .sort-arrows .up {
    opacity: .5;
}
.table-sort.ascending .btn-table-sort.sorted .sort-arrows .down,
.table-sort.descending .btn-table-sort.sorted .sort-arrows .up {
    opacity: 0;
}
Example Sortable Table JavaScript
var $tableSortlive = $('#tableSortlive');
$('.table-sort .btn-table-sort').on('click', function() {
    var $this = $(this);
    var $table = $this.closest('.table-sort');
    var caption = $table.find('caption') ? $table.find('caption').text() : 'Table';
    var i = $this.parent().index();
    var $sortCols = $table.find('tbody < tr < *:nth-child(' + (i + 1) + ')');
    var heading = $this.text().trim();
    var direction;

    if ($this.hasClass('sorted') && $table.hasClass('ascending')) {
        // sort desc
        $sortCols.sort(function (a, b) {
            return $(b).text().localeCompare($(a).text());
        });
        direction = 'descending';
        $table.removeClass('ascending').addClass(direction);

    } else {
        // sort asc
        $sortCols.sort(function (a, b) {
            return $(a).text().localeCompare($(b).text());
        });
        direction = 'ascending';
        $table.removeClass('descending').addClass(direction);

    }

    for (var j = 0; j < $sortCols.length; j++) {
        $table.find('tbody').append($($sortCols[j]).closest('tr'));
    }

    $('.btn-table-sort').removeClass('sorted')
                        .parent().removeAttr('aria-sort');
    $this.addClass('sorted')
         .parent().attr('aria-sort', direction);

    $tableSortlive.text(caption + ' is now sorted by ' + heading + ', ' + direction);
    
});
Table accessibility considerations

A <caption> functions like a heading for a table. It helps users with screen readers to find a table and understand what it's about and decide if they want to read it.

Table headers must be designated with <th>. It is also a good idea to make the scope explicit by adding a scope of one of the following: col, row, colgroup, or rowgroup.

Never use tables to lay out webpages. But if HTML is not editable, you must use role="presentation" to clearly identify its purpose.

Sorted tables should utilize buttons in the column headers to allow :focus and native keyboard controls to activate the sorting functionality. An aria-live region should also be used to alert screen readers when a table has been sorted, and how it was sorted.

Tabs

Add quick, dynamic tab function to move through panes of content.

Horizontal tabs

Tab 1

Sample content for tab 1. Sample link to exemplify how focus management should work with tabs (ie. keyboard navigation.

  • Sample bullet for tab 1
  • Sample bullet for tab 1
  • Sample bullet for tab 1

Tab 2

Sample content for tab 2. Sample link to exemplify how focus management should work with tabs (ie. keyboard navigation.

  • Sample bullet for tab 2
  • Sample bullet for tab 2
  • Sample bullet for tab 2

Tab 3

Sample content for tab 3. Sample link to exemplify how focus management should work with tabs (ie. keyboard navigation.

  • Sample bullet for tab 3
  • Sample bullet for tab 3
  • Sample bullet for tab 3

Vertical tabs

Tab 1

Sample content for tab 1. Sample link to exemplify how focus management should work with tabs (ie. keyboard navigation.

  • Sample bullet for tab 1
  • Sample bullet for tab 1
  • Sample bullet for tab 1

Tab 2

Sample content for tab 2. Sample link to exemplify how focus management should work with tabs (ie. keyboard navigation.

  • Sample bullet for tab 2
  • Sample bullet for tab 2
  • Sample bullet for tab 2

Tab 3

Sample content for tab 3. Sample link to exemplify how focus management should work with tabs (ie. keyboard navigation.

  • Sample bullet for tab 3
  • Sample bullet for tab 3
  • Sample bullet for tab 3
Example tabs HTML
<nav>
    <ul class="nav nav-tabs" id="tabs" role="tablist">
        <li class="nav-item" role="presentation">
            <a class="nav-link active" id="tab1" data-toggle="tab" href="#tab1-tab" role="tab" aria-controls="tab1" aria-selected="true" tabindex="0">Tab 1</a>
        </li>
        <li class="nav-item" role="presentation">
            <a class="nav-link" id="tab2" data-toggle="tab" href="#tab2-tab" role="tab" aria-controls="tab2" aria-selected="false" tabindex="-1">Tab 2</a>
        </li>
        <li class="nav-item" role="presentation">
            <a class="nav-link" id="tab3" data-toggle="tab" href="#tab3-tab" role="tab" aria-controls="tab3" aria-selected="false" tabindex="-1">Tab 3</a>
        </li>
    </ul>
</nav>
<div class="tab-content" id="tabConent">
    <div class="tab-pane fade show active" id="tab1-tab" role="tabpanel" aria-labelledby="tab1">
        ...
    </div>
    <div class="tab-pane fade" id="tab2-tab" role="tabpanel" aria-labelledby="tab2">
        ...
    </div>
    <div class="tab-pane fade" id="tab3-tab" role="tabpanel" aria-labelledby="tab3">
        ...
    </div>
</div>
Example Tabs CSS
.nav-tabs .nav-item {
    margin-bottom: -1px;
    margin-right: 0.33rem;
    position: relative;
}
.nav-tabs .nav-item:focus-within {
    z-index: 1;
}
.nav-tabs .nav-link {
    background: #113c66;
    color: #fff;
}
.nav-tabs .nav-link:focus,
.nav-tabs .nav-link:hover {
    background-color: #44c8f5;
    color: #113c66;
}
.nav-tabs .nav-link:focus {
    outline-color: #000;
}
.nav-tabs .nav-link.disabled {
    color: #6c757d;
    background-color: transparent;
    border-color: transparent;
}
.nav-tabs .nav-link.active,
.nav-tabs .nav-item.show .nav-link {
    color: #113c66;
    background-color: #44c8f5;
    border-color: #dee2e6 #dee2e6 #44c8f5;
    position: relative;
}
.nav-tabs .nav-link.active::after, 
.nav-tabs .nav-link.active::before,
.nav-tabs .nav-item.show .nav-link::after,
.nav-tabs .nav-item.show .nav-link::before {
    content: "";
    height: 0;
    position: absolute;
    width: 0;
    border: 10px solid transparent;
    border-top-color: #44c8f5;
    top: 100%;
    left: 50%;
    transform: translateX(-50%);
}
.nav-tabs .nav-link.active::before,
.nav-tabs .nav-item.show .nav-link::before {
    display: none;
}
.nav-tabs .nav-link.active:focus::before,
.nav-tabs .nav-item.show .nav-link:focus::before {
    display: block;
    border-width: 12px;
    border-top-color: #000;
    top: calc(100% + 4px);
}
.nav-tabs-column {
    flex-direction: column;
}
.nav-tabs-column .nav-link.active::after,
.nav-tabs-column .nav-link.active::before,
.nav-tabs-column .nav-item.show .nav-link::after,
.nav-tabs-column .nav-item.show .nav-link::before {
    border: 10px solid transparent;
    border-left-color: #44c8f5;
    top: 50%;
    left: 100%;
    transform: translateY(-50%);
}
.nav-tabs-column .nav-link.active:focus::before,
.nav-tabs-column .nav-item.show .nav-link:focus::before {
    display: block;
    border-width: 12px;
    border-top-color: transparent;
    border-left-color: #000;
    top: 50%;
    left: calc(100% + 4px);
}
Example Tabs JS
function s($selector) {
    return document.querySelectorAll($selector);
}
var $tabLists = s('[role="tablist"]');
var keys = {
    end: 35,
    home: 36,
    left: 37,
    up: 38,
    right: 39,
    down: 40
};
for (let i = 0; i < $tabLists.length; i++) {
    const $tablist = $tabLists[i];
    const $tabs = $tabLists[i].querySelectorAll('[role="tab"]');
    const $tabLength = $tabs.length;
    const isVertical = $tabLists[i].getAttribute('aria-orientation') === 'vertical';
    $tablist.addEventListener('keydown', function(e) {
        var key = e.which;
        var currIndex = Array.from(this.children).indexOf(this.querySelector(':focus').parentNode);
        var $newActiveTab = false;
        switch (key) {
            case keys.end:
                e.preventDefault();
                $newActiveTab = $tabs[$tabLength - 1];
                break;
            case keys.home:
                e.preventDefault();
                $newActiveTab = $tabs[0];
                break;
            case keys.left:
                e.preventDefault();
                $newActiveTab = (currIndex === 0) ? $tabs[$tabLength - 1] : $tabs[currIndex - 1];
                break;
            case keys.right:
                e.preventDefault();
                $newActiveTab = ((currIndex + 1) < $tabLength) ? $tabs[currIndex + 1] : $tabs[0];
                break;
            case keys.up:
                if (!isVertical) break;
                e.preventDefault();
                $newActiveTab = (currIndex === 0) ? $tabs[$tabLength - 1] : $tabs[currIndex - 1];
                break;
            case keys.down:
                if (!isVertical) break;
                e.preventDefault();
                $newActiveTab = ((currIndex + 1) < $tabLength) ? $tabs[currIndex + 1] : $tabs[0];
                break;
        };
        // activate new tab
        if ($newActiveTab) $newActiveTab.tab('show');
    });
    // shift tabindex for newly inactive tabs
    for (let j = 0; j < $tabs.length; j++) {
        const $tab = $tabs[j];
        $tab.addEventListener('hide.bs.tab', function (e) {
            e.relatedTarget.focus();
            e.relatedTarget.setAttribute('tabindex', 0);
            e.target.setAttribute('tabindex', -1);
        });
    }
}
Tab accessibility considerations

Accessible roles: Add role="navigation" to the most logical parent container of the <ul> Or wrap a <nav> element around the whole navigation. Do not add the role to the <ul> itself. This would prevent assistive technologies from announcing it as a list.

The containing tab element should be given an attribute of role="tablist". The tab element itself should be given the attribute role="tab". The tab serves as a label for the tab panels. The tab content associated with a given tab should be given the attribute role="tabpanel".

Vertical tabs should make use of aria-orientation="vertical". Horizontal tabs can use aria-orientation="horizontal". But it is not required since this is the default value.

Keyboard Interaction:

  • Tab: When focus moves into the tab list, places focus on the active tab element. When the tab list contains the focus, moves focus to the next element in the page tab sequence outside the tablist, which is typically either the first focusable element inside the tab panel or the tab panel itself.
  • When focus is on a tab in a tablist with either horizontal or vertical orientation:
    • Left Arrow: moves focus to the previous tab. If focus is on the first tab, moves focus to the last tab. Optionally, activates the newly focused tab.
    • Right Arrow: Moves focus to the next tab. If focus is on the last tab element, moves focus to the first tab. Optionally, activates the newly focused tab.
    • Space or Enter: Activates the tab if it was not activated automatically on focus.
    • Home (Optional): Moves focus to the first tab. Optionally, activates the newly focused tab.
    • End (Optional): Moves focus to the last tab. Optionally, activates the newly focused tab.
    • Shift + F10: If the tab has an associated pop-up menu, opens the menu.
    • Delete (Optional): If deletion is allowed, deletes (closes) the current tab element and its associated tab panel, sets focus on the tab following the tab that was closed, and optionally activates the newly focused tab. If there is not a tab that followed the tab that was deleted, e.g., the deleted tab was the right-most tab in a left-to-right horizontal tab list, sets focus on and optionally activates the tab that preceded the deleted tab. If the application allows all tabs to be deleted, and the user deletes the last remaining tab in the tab list, the application moves focus to another element that provides a logical work flow. As an alternative to Delete, or in addition to supporting Delete, the delete function is available in a context menu.
  • When focus is on a tab element in a vertical tab list:
    • Left Arrow: moves focus to the previous tab. If focus is on the first tab, moves focus to the last tab. Optionally, activates the newly focused tab.
    • Right Arrow: Moves focus to the next tab. If focus is on the last tab element, moves focus to the first tab. Optionally, activates the newly focused tab.

If a navigation is styled as tabs, but does not function like tabs (eg. navigation that goes to other pages), do not give the tabs role="tablist," role="tab" or role="tabpanel." Only use these for dynamic tabbed interfaces. Find details in the WAI ARIA Authoring Practices.

Tooltips

Use a tooltip to specify extra information about an element. The tooltip appears when the user mouses over an element, or the element receives focus.

Always make tooltips visible within a user's viewport on all screen sizes. The position of the tooltip should adapt to different screen sizes so it's always visible.

Example Tooltip HTML
<button type="button" class="btn btn-primary" data-toggle="tooltip" data-placement="top" title="Tooltip on top">
    Tooltip on top
</button>
Example Tooltip CSS
.tooltip {
  position: absolute;
  z-index: 1070;
  display: block;
  margin: 0;
  font-style: normal;
  font-weight: 400;
  line-height: 1.5;
  text-align: left;
  text-align: start;
  text-decoration: none;
  text-shadow: none;
  text-transform: none;
  letter-spacing: normal;
  word-break: normal;
  word-spacing: normal;
  white-space: normal;
  line-break: auto;
  font-size: 0.875rem;
  word-wrap: break-word;
  opacity: 0;
}
.tooltip.show {
  opacity: 0.9;
}
.tooltip .arrow {
  position: absolute;
  display: block;
  width: 0.8rem;
  height: 0.4rem;
}
.tooltip .arrow::before {
  position: absolute;
  content: "";
  border-color: transparent;
  border-style: solid;
}
.tooltip-top {
  padding: 0.4rem 0;
}
.tooltip-top .arrow {
  bottom: 0;
}
.tooltip-top .arrow::before {
  top: 0;
  border-width: 0.4rem 0.4rem 0;
  border-top-color: #000;
}
.tooltip-inner {
  max-width: 200px;
  padding: 0.25rem 0.5rem;
  color: #fff;
  text-align: center;
  background-color: #000;
  border-radius: 0;
}
Example Tooltip JS
setElementContent($element, content) {
    if (typeof content === 'object' && (content.nodeType || content.jquery)) {
        if (this.config.html) {
            if (!$(content).parent().is($element)) {
                $element.empty().append(content);
            }
        } else {
            $element.text($(content).text());
        }
        return;
    }
    if (this.config.html) {
        if (this.config.sanitize) {
            content = sanitizeHtml(content, this.config.whiteList, this.config.sanitizeFn);
        }
        $element.html(content);
    } else {
        $element.text(content);
    }
}
Tooltip accessibility considerations

Accessible role: You need to add role="tooltip" to the tooltip container.

Accessible ARIA attributes: Be sure to add aria-describedby="...", referencing the tooltip container. If the tooltip is visually hidden you must include aria-hidden="true." Once the tooltip is visible, change the value to false, or remove the attribute.

Typography

Font family

Consistent use of our corporate typefaces reinforces Hennepin's brand identity.

Primary font family - Myriad Pro

Use Myriad Pro for all text, including headings and body copy. When using Myriad Pro Light, use larger point sizes to maximize readability. As a general guide, headlines should be twice as large as subheads, which should be twice as large as body copy. Reserve bold and semi-bold should for subheads.

Font family fallbacks

When Myriad Pro does not load, always provide font fallbacks. Target similar system fonts for your fallback. This will give you the best alternative for the user's system.

Example san-serif font family CSS
body {
    font-family: "myriad-pro", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif,
    "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
}

Serif fonts

For formal communications use Adobe Garamond (Garamond). Always provide similar font fallbacks for serif text.

Example san-serif font family CSS
body {
    font-family: Garamond, serif;
}

Headings

Headings help organize the content on the page.

Headings are hierarchical, with <h1> representing the overall idea of the page. Each page should only have one <h1>. Headings from <h2> to <h6> help provide more organization and subsections to the content.

The rankings or level of the header relate to the number (for example, the number 2 in h2). We recommend not skipping headings. For example, from <h2> to <h4>. It is okay to skip headings if going backwards. For example, from <h4> to <h3>.

Appropriate headings help provide assistive technologies a way to provide in-page navigation.

h1 Heading

h2 Heading

h3 Heading

h4 Heading

h5 Heading
h6 Heading
Example Headings HTML
<h1>All about farm animals</h1>
    <h2>Food</h2>
        <h3>How do they eat?</h3>
        <h3>What do they eat?</h3>
    <h2>Home</h2>
Example Headings CSS
h1 {
    font-size: 2.5rem;
}
h2 {
    font-size: 2rem;
}
h3 {
    font-size: 1.66rem;
}
h4 {
    font-size: 1.25rem;
}
h5 {
    font-size: 1rem;
}
h6 {
    font-size: 0.85rem;
}

Body copy

Body copy should be 16px with a line-height of at least 1.5. This makes it easier for users to read, especially those with cognitive disabilities. Provide a line-height of 1.5 to 2. See WCAG specifying line spacing in CSS.

For example:

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit.

Sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?

Example body copy HTML
<p>...</p>
<p>...</p>
Example body copy CSS
p {
    font-size: 1rem;
    line-height: 1.5;
    margin-top: 0;
    margin-bottom: 1rem;
}