Guidelines

Please enforce these guidelines at all times. Small or large, call out what's incorrect.

Every line of code should appear to be written by a single person, no matter the number of contributors.

This set of rules generate some constraints and conventions. If you run into instances where a convention isn’t obvious or a solution could be handled in a few different ways, contact the PatternFly community, have a conversation about how to handle it and update this guidelines when needed.

Separation of UI structure concerns

PatternFly is made out of isolated and modular structures that fall into 2 categories:

  • Layouts
  • Components

Layouts

Layouts are the containers that allow for organizing and grouping its immediate children on the page.

  • A layout never imposes padding or element styles on its children.

The classes are prefixed with -l (after the PatternFly prefix pf-), for example: .pf-v6-l-split or .pf-v6-l-stack.

Components

Components are modular and independent structures concerned with how a thing looks.

  • A component always touches all four sides of its parent container.
  • The component itself never has widths, floats or margins.
  • The first element in a component should never use top margins and should touch the top of its component.
  • Components should include semantic markup and necessary ARIA tags to implement the accessibility guidelines.

The parent container of a component is prefixed with -c (after the PatternFly prefix pf- and version v6-), for example: .pf-v6-c-alert or .pf-v6-c-button.

When to create a new component

As a general rule, create an extension to an element with BEM modifiers if it’s a change of color or scale. If the change generates a new entity, then create a new component.

Repetition is better than the wrong abstraction.

Additional features

Utilities

PatternFly is made up of isolated components that don't allow dependencies. Therefore, the use of helpers or utility classes is discouraged.

However, from time to time it is recognized that an exception to the PatternFly styling may be needed for a special case. For those instances, utility classes are supplied to assist in allowing minor styling changes without creating the need for adding custom CSS.

A utility class is prefixed with -u (after the PatternFly prefix pf-), for example: .pf-v6-u-align-content-center.

Demos

Demos show how components and layouts can be put together to build more complex structures.

  • A demo never includes its own styles. If styling is necessary to implement a desired demo, then new components or layouts, or variants of the components or layouts used, should be created instead.
  • A demo doesn't add any accessibility tags that aren't already in the components. All accessibility should be handled at the component level.

Variables

PatternFly follows a two-layer theming system where global variables always inform component variables. Each one of those layers follow a set of very specific rules.

Global variables

The main reason to have global variables is to maintain consistency. They adhere to the following rules:

  • They are prefixed with the word global and follow the formula --pf-global--concept--PropertyCamelCase--modifier--state.
    • a concept is something like a spacer or main-title.
    • a PropertyCamelCase is something like BackgroundColor or FontSize.
    • a modifier is something like sm, or lg.
    • and a state is something like hover, or expanded.
  • They are concepts, never tied to an element or component. This is incorrect: --pf-global--h1--FontSize, this is correct: --pf-global--FontSize--3xl.

For example a global variable setup would look like:

// --pf-global--concept--size
  --pf-global--spacer--lg: .5rem;
  --pf-global--spacer--xl: 1rem;
  --pf-global--spacer--2xl: 2rem;

  // --pf-global--PropertyCamelCase--modifier
  --pf-global--FontSize--3xl: 2rem;
  --pf-global--FontSize--2xl: 1.8rem;
  --pf-global--FontSize--lg: 1rem;

  // --pf-global--PropertyCamelCase--state
  --pf-global--link--TextDecoration--hover: #ccc;

  // --pf-global--PropertyCamelCase--modifier
  --pf-global--Color--100: #000;

  // --pf-global--concept--modifier
  --pf-global--primary-color--100: #00f;

Component variables

The second layer is scoped to themeable component custom properties. This setup allows for consistency across components, generates a common language between designers and developers, and gives granular control to authors. The rules are as follows:

  • They follow this general formula --pf-v6-c-block[__element][--modifier][--state][--breakpoint][--pseudo-element][[--child]|[--tag]|[--c-component]]--PropertyCamelCase.
    • --pf-v6-c-block refers to the block, usually the component or layout name (i.e., --pf-v6-c-alert).
    • __element refers to the element inside of the block (i.e., __title).
    • --modifier refers to a modifier class such as .pf-m-danger, and is prefixed with m- in the component variable (i.e., --m-danger).
    • --state is something like hover or active.
    • --breakpoint is a media query breakpoint such as sm for $pf-global--breakpoint--xs.
    • --pseudo-element is one of either before or after.
    • --child, --tag, or --c-component refers to a child element. It could be a tag or component name, like --c-menu or --svg, or it could use --child to refer to any child element. If any modifiers, states, breakpoints, or pseudo-elements are on the child, include those after this portion of the name.
  • The value of a component variable is always defined by a global variable.
  • It's possible to include multiple elements, modifiers, states, and breakpoints in a single component variable.
  • The order of elements, modifiers, states, and breakpoints in the variable name should match the selector order.

For example:

// Component scoped variables are always defined by global variables
--pf-v6-c-alert--Padding: var(--pf-global--spacer--xl);
--pf-v6-c-alert--hover--BackgroundColor: var(--pf-global--BackgroundColor--200);
--pf-v6-c-alert__title--FontSize: var(--pf-global--FontSize--2xl);

// --block--PropertyCamelCase
.pf-v6-c-alert {
  padding: var(--pf-v6-c-alert--Padding);
}

// --block--state--PropertyCamelCase
.pf-v6-c-alert {
  &:hover {
    background-color: var(--pf-v6-c-alert--hover--BackgroundColor);
  }
}

// --block__element--PropertyCamelCase
.pf-v6-c-alert__title {
  font-size: var(--pf-v6-c-alert__title--FontSize);
}

// A more complex example
.pf-v6-c-switch {
  @media (max-width: $pf-global--breakpoint--sm) {
    .pf-v6-c-switch__input {
      &:disabled {
        ~ .pf-v6-c-switch__toggle {
          &::before {
            background-color: var(--pf-v6-c-switch--sm__input--disabled__toggle--before--BackgroundColor);
          }
        }
      }
    }
  }
}

Comment all magic values

If a situation arises where a value needs entering into the style sheets that isn't already defined in the global variables this should serve as a red flag to you.

In the case where a 'magic' value needs entering, ensure a comment is added on the line above to explain its relevance.

Harry Robert's Guidelines

PatternFly follows Harry Robert's CSS Guidelines with some exceptions, deviations and additions.

Exceptions

PatternFly doesn't follow these rules:

Deviations from Harry Robert's Guidelines

HTML

Practicality over purity. Strive to maintain HTML standards and semantics, but not at the expense of practicality. Use the least amount of markup with the fewest intricacies whenever possible.

Comment and organization

Code is written and maintained by people. Ensure your code is descriptive, well commented, and approachable by others. Great code comments convey context or purpose. Do not simply reiterate a component or class name.

Be sure to write in complete sentences for larger comments and succinct phrases for general notes.

Follow this comment structure:

  1. Block
  2. Sections
  3. Line
// Section level comment
.selector {
  line-height: 1.5; // Line level comment
  color: #333;
}
1. Section

This comment is a section divider or describes a block of code.

  • Leave one blank lines above.
2. Line

Describes a specific line of code.

Additions to Harry Robert's Guidelines

Lintable CSS rules

The CSS linter is PatternFly's single source of truth for CSS syntax, declaration order, and other CSS rules like disallow type selectors, disallow vendor prefixes, and more.

Shorthand notation

Limit the use of shorthand declarations to instances where you must explicitly set all the available values. Common overused shorthand properties include:

  • padding
  • margin
  • font
  • background
  • border
  • border-radius
// Bad
.element {
  margin: 0 0 10px;
  background: #f00;
  background: url("image.jpg");
  border-radius: 3px 3px 0 0;
}

// Good
.element {
  margin-bottom: 10px;
  background-color: #f00;
  background-image: url("image.jpg");
  border-top-left-radius: 3px;
  border-top-right-radius: 3px;
}

The Mozilla Developer Network and Harry Robert both have great articles on shorthand properties for those unfamiliar with notation and behaviour.

Sass

PatternFly uses Sass to preprocess CSS.

As a general rule don't overcomplicate Sass, keep it easy to parse for a normal human.

Nesting

As a general rule avoid Sass nesting to increase specificity. Try not to nest more than three layers deep.

Limit nesting to the following use cases:

  1. Modifiers
  2. Media queries
  3. States, pseudo-classes, and pseudo-elements
  4. Combinators
1. Modifiers and elements of a block
  • Do not use Sass's parent selector mechanism to construct BEM elements.
  • Do use that method to create compound selectors with modifier classes.
// Good
.pf-nav {
  // styles

  &.pf-m-vertical {
    // styles
  }
}

.pf-nav__item {
  // styles
}

// Bad
.pf-nav {
  // styles

  &__item {
    // styles
  }
}

.pf-m-nav.pf-m-vertical {
  // styles
}
2. Media queries

Component-specific media queries should be nested inside the component block. Remember that PatternFly is mobile first. Do progressive enhancement, not gracefully degradation.

PatternFly has 5 breakpoints:

$pf-global-breakpoint--xs: 0;
  $pf-global-breakpoint--sm: 576px;
  $pf-global-breakpoint--md: 768px;
  $pf-global-breakpoint--lg: 992px;
  $pf-global-breakpoint--xl: 1200px;

To make sure you are writing mobile first, always do min-width:

.pf-nav {
  // mobile styles

  // Styles for small view ports and up
  @media (min-width: $pf-global-breakpoint--xs) {
    // non-mobile styles
  }
}
4. States, pseudo-classes and pseudo-elements

States of a component should be included as a nested element. This includes hover, focus, and active states:

.pf-v6-c-button {
  background: var(--pf-v6-c-button--Background);

  &:hover {
    background: var(--pf-v6-c-button--hover--Background);
  }
}

Sass variables

We create global Sass variables to keep a Bootstrap theme in sync. These values also inform our component level variables.

@mixin and @extend

Since our components are isolated and modular try to avoid @mixin and @extend because they generate a dependency.

Nested calc() functions

There is currently a bug in cssnano (issue #64 on postcss-calc) that causes nested calc() CSS functions to be removed. So a function like calc(5 * calc(3 - 1)) becomes calc(5 * 3 - 1). It's worth noting that this problem only impacts our distribution package. Nested calc() functions work fine in the development environment.

PatternFly developers should avoid nested calc() CSS functions until this bug is resolved and the package is updated in the patternfly repository. If you're interested in following this issue, you can do so in issue #1295 on patternfly.

Hover styles

While the default styles applied to an element might not provide a visual indication of target area, the styles that display on hover should. To ensure that these styles accurately convey the target area of an element where the user can click, :hover styles should be applied to the clickable element of a component, and not to a larger wrapping element.

References

This guide is inspired by people we follow and respect: