Skip to content

Instantly share code, notes, and snippets.

@mturley
Last active November 17, 2022 18:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mturley/a0a7a7d7e9b8b144b8479e24a188346f to your computer and use it in GitHub Desktop.
Save mturley/a0a7a7d7e9b8b144b8479e24a188346f to your computer and use it in GitHub Desktop.
PatternFly v4 / v5 / next coexisting with versioned interop classes on components/layouts

I just wanted to write down my thoughts about a possibly-naive but simple approach we could theoretically take to support PF4/PF5/Next component styles existing on the same page.

To be clear: I have not learned much about CSS layers or CSS modules, so this may be better achieved with something fancier. However, this would work without any browser compatibility concerns.

Proposal:

  • We release a transitional compatibility-version of PF4 that is identical to the last PF4 version except for the addition of .pf-v-4 version classes on every component/layout style rule.
    • This is technically a breaking change for core CSS/HTML consumers, but React consumers could simply upgrade the packages and not have to change their usage at all (component APIs would remain identical, the rendered DOM would match the new CSS).
    • If we are adhering to proper BEM classes for descenant elements of a component/layout, we should only need to apply this special class to the top of each component/layout unless we need to support nesting multiple versions of the exact same component/layout (for example placing a v5 Flex layout inside a v4 Flex layout). If this is a concern we could apply the version class to every single element that has a PF class and still abstract that away in React (but that would be a pain for core consumers). The examples below don't worry about that concern.
    • Maybe this is some special 4.x.x-interop release that deviates from the semantic version history, or maybe we just make 5.0.0 a "stepping stone release" and we skip to 6.0.0 for future style changes. That would be true semantic and unsentimental versioning. React did this same thing: React 17 contained no new features and only added mechanisms to ease future incremental upgrades of React. We could communicate this interoperability plan in the release notes for 5.0.0 and give people time to make this stepping-stone upgrade before we release the real breaking changes in 6.0.0.
    • Anyone who does not need to combine PF4 and PF5/6 on the same page can skip this release. Those who do need to combine versions would first be required to upgrade everything to this compatibility release, which in theory would be very easy.
  • Future versions of PF include these version classes on every component/layout-level style rule.
  • An app that imports both the stylesheets for the 4.x.x-interop version and 5.0/6.0+ versions can simply use both side by side with no conflicts as long as every component/layout has a version class on it, which we could do automatically for React consumers.

I threw together a CodePen to demonstrate the basic concept: https://codepen.io/mturley-the-bold/pen/RwJjbdb

This v4-interop version of PatternFly might look something like this, with .pf-v-4 added to each component-level style rule:

.pf-v-4.pf-l-flex {
  /* Existing v4 Flex layout styles here */
}

.pf-v-4.pf-l-flex .pf-l-flex__item {
  /* Existing v4 Flex-item styles here (the version class is only applied at the layout/component level, not to children) */
}

.pf-v-4.pf-c-button {
  /* Existing v4 Button component styles in here */
}

This version could also include an additional class on every style rule that applies to "next" component versions, and apply those styles as overrides:

.pf-v-4.pf-v-next.pf-c-button {
  /* v4-next overrides for Button component here, to be applied along with non-next styles */
}

Or, if we want a fresh slate for "next" component styles instead of using overrides, we could use :not(.pf-v-next) on every non-next style rule:

.pf-v-4:not(.pf-v-next).pf-l-flex {
  /* Existing v4 Flex layout styles here */
}

.pf-v-4:not(.pf-v-next).pf-l-flex .pf-l-flex__item {
  /* Existing v4 Flex-item styles here */
}

.pf-v-4:not(.pf-v-next).pf-c-button {
  /* Existing v4 Button component styles in here */
}

.pf-v-4.pf-v-next.pf-c-button {
  /* v4 "next" styles for Button here, applied alone without the non-next styles */
}

Some rendered HTML that uses this version of PF might look like this:

<div class="pf-v-4 pf-l-flex">
  <div class="pf-l-flex__item">
    <button class="pf-v-4 pf-c-button pf-m-primary" type="button">I'm a v4 button in a v4 layout!</button>
  </div>
  <div class="pf-l-flex__item">
    <button class="pf-v-4 pf-v-next pf-c-button pf-m-primary" type="button">I'm a v4-next button in a v4 layout!</button>
  </div>
</div>

For future releases (5.x, or 6.x if 5.x is a stepping-stone release), we would do the same with a different version class. If using overrides for next components:

.pf-v-5.pf-l-flex {
  /* v5 Flex layout styles here */
}

.pf-v-5.pf-l-flex .pf-l-flex__item {
  /* v5 Flex-item styles here */
}

.pf-v-5.pf-c-button {
  /* v5 Button component styles in here */
}

.pf-v-5.pf-v-next.pf-c-button {
  /* v5-next overrides for Button component here, to be applied along with non-next styles */
}

Or if using the :not pseudoclass to avoid overrides for next components:

.pf-v-5:not(.pf-v-next).pf-l-flex {
  /* v5 Flex layout styles here */
}

.pf-v-5:not(.pf-v-next).pf-l-flex .pf-l-flex__item {
  /* v5 Flex-item styles here */
}

.pf-v-5.pf-c-button {
  /* v5 Button component styles in here */
}

.pf-v-5.pf-v-next.pf-c-button {
  /* v5-next overrides for Button component here, to be applied along with non-next styles */
}

Some rendered HTML that uses both PF4 and PF5 (including some components inside a layout from another version) might look like this:

<div class="pf-v-4 pf-l-flex">
  <div class="pf-l-flex__item">
    <button class="pf-v-4 pf-c-button pf-m-primary" type="button">I'm a v4 button in a v4 layout!</button>
  </div>
  <div class="pf-l-flex__item">
    <button class="pf-v-4 pf-v-next pf-c-button pf-m-primary" type="button">I'm a v4-next button in a v4 layout!</button>
  </div>
  <div class="pf-l-flex__item">
    <button class="pf-v-5 pf-c-button pf-m-primary" type="button">I'm a v5 button in a v4 layout!</button>
  </div>
  <div class="pf-l-flex__item">
    <button class="pf-v-5 pf-v-next pf-c-button pf-m-primary" type="button">I'm a v5-next button in a v4 layout!</button>
  </div>
</div>
<div class="pf-v-5 pf-l-flex">
  <div class="pf-l-flex__item">
    <button class="pf-v-4 pf-c-button pf-m-primary" type="button">I'm a v4 button in a v5 layout!</button>
  </div>
  <div class="pf-l-flex__item">
    <button class="pf-v-4 pf-v-next pf-c-button pf-m-primary" type="button">I'm a v4-next button in a v5 layout!</button>
  </div>
  <div class="pf-l-flex__item">
    <button class="pf-v-5 pf-c-button pf-m-primary" type="button">I'm a v5 button in a v5 layout!</button>
  </div>
  <div class="pf-l-flex__item">
    <button class="pf-v-5 pf-v-next pf-c-button pf-m-primary" type="button">I'm a v5-next button in a v5 layout!</button>
  </div>
</div>

Other thoughts:

  • Maybe instead of using a separate .pf-v-next and making the overrides or :not() necessary, we could just use a totally separate .pf-v-4-next class and not apply .pf-v-4 to the "next" components at all.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment