Up and running with Patternfly 4

kenmoini

Ken Moini

Posted on June 14, 2020

Up and running with Patternfly 4

If you've used any Red Hat technologies in the last few years, you may have noticed a common theme. This standardized design framework is called Patternfly and is based on the Bootstrap framework. Patternfly is one of the over 1 million Open-Source projects that are backed by Red Hat.

Patternfly 4 Page Example

I've used Patternfly 3 before, which was basically a Bootstrap 3 theme. Migrating to Patternfly 4 had a few changes that broke my previous workflow and assets. It's nothing really difficult to address, though the documentation is sometimes difficult to navigate at first - it makes sense once you start to use Patternfly and understand how it was redesigned.

The Cliff Notes

  • Patternfly 4 now has two variants: a core HTML/CSS only version, and one based on React. I have been working on updating a PF3 site to PF4, so I used the core HTML/CSS only version - also because I don't care for Facebook and their technologies.

  • Note that the core HTML/CSS version does NOT come with the Javascript needed to make all those assets and components work! Accordions, dropdowns, modals - they don't work! PF4 is BYOJS or Bring-Your-Own-JavaScript. This was confusing at first while I wondered where the hell the JS files were...

  • Development instructions tend to be unclear, with the "missing" JS this spent many cycles.

Thankfully for you, I've got the cheat codes and notes so you don't have to spend as many cycles getting started with Patternfly 4!

Building with Patternfly 4

In this guide we'll be going over how to use the core HTML/CSS version of Patternfly 4 in a new web project and how to bring in the needed JavaScript and glue to make it all work!

1) Create new environment

First off, let's start with a blank canvas - assuming you already have Git, NodeJS, and NPM installed:

mkdir my-awesome-site && cd my-awesome-site
git init
npm init

After that, feel free to modify the package.json file that is created to match your new project specfications. I'd also suggest making your first commit after the init as well!


2) Pull in needed packages

Now that we have a new environment, let's bring in the Patternfly 4 framework and a few other packages that'll make it all work nicely.

npm i @patternfly/patternfly --save
npm i bootstrap --save
npm i jquery --save
npm i jquery-slimscroll --save #optional

That'll pull in the Patternfly 4 framework, Bootstrap and its dependencies such as jQuery, and there's an optional SlimScroll package to make things look a bit nicer.


3) Using the Patternfly 4 Styles

So at this point we have the assets needed to include Patternfly 4 into our project. All we need to do is include the CSS files from the node_modules/@patternfly/patternfly/ directory:

<link rel="stylesheet" href="/node_modules/@patternfly/patternfly/patternfly.min.css" />
<link rel="stylesheet" href="/node_modules/@patternfly/patternfly/patternfly-addons.css" />

You'll also see the SCSS files available - you can include the Patternfly package in your own SCSS file, override your desired styles, and compile the CSS assets as part of your own build process if you already have something like Webpack already set up with an SCSS compiler.

Alternatively, you can also modify the Patternfly package and build a new custom packaged version of Patternfly by running npm run build in the Patternfly package directory - note that this is generally an anti-pattern, you don't really want to modify the package as much as you'd want to extend it because your changes could easily be overwritten next time you run npm to manage packages.


4) Adding the JavaScript Glue

Patternfly is a design framework - the functionality of the designed components isn't defined (which is weird from a UX perspective...). This basically means that the controlling functions aren't defined and we need to bring in our own. Thankfully, Patternfly is built on Bootstrap so many of the same JavaScript bits will work with little modification.

Next, we'll include the needed JavaScript files from jQuery and Bootstrap - note that we're not including the Bootstrap {S}CSS, just the JavaScript.

<!-- jQuery -->
<script type="text/javascript" src="/node_modules/jquery/dist/jquery.min.js"></script>

<!-- Bootstrap -->
<script type="text/javascript" src="/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>

<!-- SlimScroll -->
<script type="text/javascript" src="/node_modules/jquery-slimscroll/jquery.slimscroll.min.js"></script>

5) Real-world Example

Now that we have all our needed components, let's go ahead and build a couple pages with Patternfly 4.

The demos on the main public Patternfly 4 site have the source minified so it's difficult to build off those examples. However, the build available at https://pf4.patternfly.org/ is not minified and perfect for glancing from the source!

Page with Side Nav and Cards

<!DOCTYPE html>
  <html lang="en-us">
    <head>
      <meta charSet="utf-8"/>
      <meta http-equiv="x-ua-compatible" content="ie=edge"/>
      <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/>
      <title data-react-helmet="true">HTML - Page default nav</title>
      <link rel="stylesheet" href="/node_modules/@patternfly/patternfly/patternfly.min.css" />
      <link rel="stylesheet" href="/node_modules/@patternfly/patternfly/patternfly-addons.css" />
      <style rel="stylesheet" type="text/css">
        #mainSideNav.collapse:not(.show) {
          display: none;
        }
        #mainSideNav.collapse.show {
          display:grid;
          overflow: hidden;
        }
        #mainSideNav.collapsing{
          height: 100vh !important;
        }
        .pf-c-dropdown__menu {
          display:none;
        }
        .pf-c-dropdown__menu.show {
          display:inherit;
        }
      </style>
    </head>
    <body>
      <div id="___gatsby">
        <div style="outline:none" tabindex="-1" id="gatsby-focus-wrapper">
          <main class="ws-site-root">
          <div class="ws-fullscreen-example">
            <div class="pf-c-page" id="page-default-nav-example">
              <a class="pf-c-skip-to-content pf-c-button pf-m-primary" href="#main-content-page-default-nav-example">Skip to content</a>
              <header class="pf-c-page__header">
                <div class="pf-c-page__header-brand">
                  <div class="pf-c-page__header-brand-toggle">
                    <button class="pf-c-button pf-m-plain" type="button" id="page-default-nav-example-nav-toggle" aria-label="Global navigation" aria-expanded="true" data-toggle="collapse" data-target="#mainSideNav" aria-controls="mainSideNav">
                      <i class="fas fa-bars" aria-hidden="true"></i>
                    </button>
                  </div>
                  <a class="pf-c-page__header-brand-link">
                    <img class="pf-c-brand" src="/assets/images/PF-Masthead-Logo.svg" alt="PatternFly logo" />
                  </a>
                </div>
                <div class="pf-c-page__header-tools">
                  <div class="pf-c-page__header-tools-group">
                    <div class="pf-c-page__header-tools-item pf-m-hidden pf-m-visible-on-lg">
                      <button class="pf-c-button pf-m-plain" type="button" aria-label="Settings">
                        <i class="fas fa-cog" aria-hidden="true"></i>
                      </button>
                    </div>
                    <div class="pf-c-page__header-tools-item pf-m-hidden pf-m-visible-on-lg">
                      <button class="pf-c-button pf-m-plain" type="button" aria-label="Help">
                        <i class="pf-icon pf-icon-help" aria-hidden="true"></i>
                      </button>
                    </div>
                  </div>
                  <div class="pf-c-page__header-tools-group">
                    <div class="pf-c-page__header-tools-item pf-m-hidden-on-lg">
                      <div class="pf-c-dropdown">
                        <button class="pf-c-dropdown__toggle pf-m-plain" type="button" id="page-default-nav-example-dropdown-kebab-right-aligned-1-button" aria-expanded="false" aria-label="Actions">
                          <i class="fas fa-ellipsis-v" aria-hidden="true"></i>
                        </button>
                        <ul class="pf-c-dropdown__menu pf-m-align-right" aria-labelledby="page-default-nav-example-dropdown-kebab-right-aligned-1-button" hidden>
                          <li>
                            <a class="pf-c-dropdown__menu-item" href="#">Link</a>
                          </li>
                          <li>
                            <button class="pf-c-dropdown__menu-item" type="button">Action</button>
                          </li>
                          <li>
                            <a class="pf-c-dropdown__menu-item pf-m-disabled" href="#" aria-disabled="true" tabindex="-1">Disabled link</a>
                          </li>
                          <li>
                            <button class="pf-c-dropdown__menu-item" type="button" disabled>Disabled action</button>
                          </li>
                          <li class="pf-c-divider" role="separator"></li>
                          <li>
                            <a class="pf-c-dropdown__menu-item" href="#">Separated link</a>
                          </li>
                        </ul>
                      </div>
                    </div>
                    <div class="pf-c-page__header-tools-item pf-m-hidden pf-m-visible-on-md">
                      <div class="pf-c-dropdown">
                        <button class="pf-c-dropdown__toggle pf-m-plain" type="button" id="page-default-nav-example-dropdown-button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                          <span class="pf-c-dropdown__toggle-text">John Smith</span>
                          <span class="pf-c-dropdown__toggle-icon">
                            <i class="fas fa-caret-down" aria-hidden="true"></i>
                          </span>
                        </button>
                        <div class="pf-c-dropdown__menu" aria-labelledby="page-default-nav-example-dropdown-button">[Panel contents here]</div>
                      </div>
                    </div>
                  </div>
                  <img class="pf-c-avatar" src="/assets/images/img_avatar.svg" alt="Avatar image" />
                </div>
              </header>
              <div class="pf-c-page__sidebar collapse show" id="mainSideNav">
                <div class="pf-c-page__sidebar-body">
                  <nav class="pf-c-nav" id="page-default-nav-example-primary-nav" aria-label="Global">
                    <ul class="pf-c-nav__list">
                      <li class="pf-c-nav__item">
                        <a href="#" class="pf-c-nav__link">System panel</a>
                      </li>
                      <li class="pf-c-nav__item">
                        <a href="#" class="pf-c-nav__link pf-m-current" aria-current="page">Policy</a>
                      </li>
                      <li class="pf-c-nav__item">
                        <a href="#" class="pf-c-nav__link">Authentication</a>
                      </li>
                      <li class="pf-c-nav__item">
                        <a href="#" class="pf-c-nav__link">Network services</a>
                      </li>
                      <li class="pf-c-nav__item">
                        <a href="#" class="pf-c-nav__link">Server</a>
                      </li>
                    </ul>
                  </nav>
                </div>
              </div>
              <main class="pf-c-page__main" tabindex="-1" id="main-content-page-default-nav-example">
                <section class="pf-c-page__main-breadcrumb">
                  <nav class="pf-c-breadcrumb" aria-label="breadcrumb">
                    <ol class="pf-c-breadcrumb__list">
                      <li class="pf-c-breadcrumb__item">
                        <a href="#" class="pf-c-breadcrumb__link">Section home</a>
                      </li>
                      <li class="pf-c-breadcrumb__item">
                        <span class="pf-c-breadcrumb__item-divider">
                          <i class="fas fa-angle-right" aria-hidden="true"></i>
                        </span>
                        <a href="#" class="pf-c-breadcrumb__link">Section title</a>
                      </li>
                      <li class="pf-c-breadcrumb__item">
                        <span class="pf-c-breadcrumb__item-divider">
                          <i class="fas fa-angle-right" aria-hidden="true"></i>
                        </span>
                        <a href="#" class="pf-c-breadcrumb__link">Section title</a>
                      </li>
                      <li class="pf-c-breadcrumb__item">
                        <span class="pf-c-breadcrumb__item-divider">
                          <i class="fas fa-angle-right" aria-hidden="true"></i>
                        </span>
                        <a href="#" class="pf-c-breadcrumb__link pf-m-current" aria-current="page">Section landing</a>
                      </li>
                    </ol>
                  </nav>
                </section>
                <section class="pf-c-page__main-section pf-m-light">
                  <div class="pf-c-content">
                    <h1>Main title</h1>
                    <p>This is a demo of the Page component.</p>
                  </div>
                </section>
                <section class="pf-c-page__main-section">
                  <div class="pf-l-gallery pf-m-gutter">
                    <div class="pf-l-gallery__item">
                      <div class="pf-c-card">
                        <div class="pf-c-card__body">This is a card</div>
                      </div>
                    </div>
                    <div class="pf-l-gallery__item">
                      <div class="pf-c-card">
                        <div class="pf-c-card__body">This is a card</div>
                      </div>
                    </div>
                    <div class="pf-l-gallery__item">
                      <div class="pf-c-card">
                        <div class="pf-c-card__body">This is a card</div>
                      </div>
                    </div>
                  </div>
                </section>
              </main>
            </div>
          </div>
        </main>
      </div>
    </div>
    <!-- jQuery -->
    <script type="text/javascript" src="/node_modules/jquery/dist/jquery.min.js"></script>

    <!-- Bootstrap -->
    <script type="text/javascript" src="/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>

    <!-- SlimScroll -->
    <script type="text/javascript" src="/node_modules/jquery-slimscroll/jquery.slimscroll.min.js"></script>

    <!-- Custom Javascript -->
    <script type="text/javascript">
    <!--
      jQuery(document).ready(function() {
        jQuery('.pf-c-page__sidebar-body').slimScroll({
          height: '100%',
          width: 'var(--pf-c-page__sidebar--Width)',
        });
      });
    //-->
    </script>
  </body>
</html>

That should get you something that looks like this:

Patternfly 4 Example


Dropdown and Side Nav Toggle

There are a few things you should note in the example above:

  1. The #page-default-nav-example-nav-toggle element has the needed data- and aria- attributes defined to control the display toggle of #mainSideNav
  2. There is CSS defined to add some extra smooth-ness to the side navigation toggle.
  3. The Joshua Smith dropdown has the needed data- and aria- attributes defined to control the display of the dropdown.
  4. There is some CSS to control when the dropdown is displayed.
  5. There is additional JavaScript to give the side navigation a nice SlimScroll.

Basically, the Bootstrap component actions can be directly implemented in Patternfly 4 with the use of the data- attributes, or with initialization in JavaScript.

Next Steps

From here, you can go about referencing the Patternfly 4 core examples available here: https://pf4.patternfly.org/

With the HTML set in your document, add the needed glue from the Bootstrap 4 documentation: https://getbootstrap.com/docs/4.5/components/alerts/

After you get comfortable with the CSS prefixes and how things are built and set up you can move onto building it as a native Gatsby site or including the SCSS in your own builds to extend the style guidelines in your own ways!

πŸ’– πŸ’ͺ πŸ™… 🚩
kenmoini
Ken Moini

Posted on June 14, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

All Simple Dev tutorials are now FREE!
Navbar in Bootstrap 5 Tutorial
webdev Navbar in Bootstrap 5 Tutorial

April 3, 2021

How to Build a Website in 5 Minutes
tutorial How to Build a Website in 5 Minutes

January 18, 2021

Up and running with Patternfly 4
tutorial Up and running with Patternfly 4

June 14, 2020

Full-width YouTube video embed
tutorial Full-width YouTube video embed

December 18, 2019