Skip to content

feat(banner)!: port to pf-v6-banner#3143

Merged
bennypowers merged 20 commits into
staging/pfv6from
feat/pf-v6-banner
Jun 15, 2026
Merged

feat(banner)!: port to pf-v6-banner#3143
bennypowers merged 20 commits into
staging/pfv6from
feat/pf-v6-banner

Conversation

@zeroedin

@zeroedin zeroedin commented May 12, 2026

Copy link
Copy Markdown
Collaborator

Summary

Closes #2983

  • Removes pf-v5-banner
  • Ports pf-v5-banner to pf-v6-banner and aligned with the React v6 component
  • Replaces v5 variant attribute with separate color (8 decorative colors) and status (5 semantic statuses) attributes, matching React's split API
  • Adds screen-reader-text attribute for visually-hidden status announcements (WCAG 1.3.1)
  • Retains sticky attribute (maps to React isSticky)
  • Removes v5 icon attribute/slot — icons are now composed in the default slot, matching React v6
  • All CSS uses v6 design tokens with hex fallbacks via light-dark() for standalone usage

Deviations from React

  • Status demo uses pf-v5-icon: The status demo composes icons in the default slot using pf-v5-icon because pf-v6-icon is not yet merged to staging/pfv6. Once pf-v6-icon lands, the status demo should be updated to use pf-v6-icon with the rh-ui-*-fill icon set to match the React demos visually.
  • Sticky demo: React has isSticky as a prop but no dedicated demo on patternfly.org. We include a standalone sticky demo since the behavior requires scrollable content to verify — this was carried forward from the v5 element.

Test plan

  • 36 unit tests passing (instantiation, upgrade, all 8 colors, all 5 statuses, sticky positioning, slotted link a11y, screen-reader-text presence/absence)
  • Visual comparison against patternfly.org in both light and dark mode
  • /review-a11y — no critical issues
  • /review-api — no critical issues
  • /review-demos — all demos match patternfly.org parity
  • cem health — 87/100 (above 80% threshold)
  • Verify demos at http://localhost:8000/elements/banner/demo/{slug}?rendering=chromeless

🤖 Generated with Claude Code

@changeset-bot

changeset-bot Bot commented May 12, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a4f27c9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@patternfly/elements Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

github-actions Bot commented May 12, 2026

Copy link
Copy Markdown
Contributor

✅ Commitlint tests passed!

More Info
{
  "valid": true,
  "errors": [],
  "warnings": [],
  "input": "feat(banner)!: port to pf-v6-banner"
}

@github-actions

github-actions Bot commented May 12, 2026

Copy link
Copy Markdown
Contributor

Deploy Preview for patternfly-elements ready!

Name Link
🔨 Latest commit 038589d
😎 Deploy Preview https://deploy-preview-3143--patternfly-elements.netlify.app/

To edit notification comments on pull requests, go to your Netlify site settings.

@github-actions github-actions Bot added the AT passed Automated testing has passed label May 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

SSR Test Run for 4a848b0: Report

Comment thread elements/pf-v6-banner/pf-v6-banner.ts Outdated
Comment thread elements/pf-v6-banner/pf-v6-banner.ts Outdated
Comment thread elements/pf-v6-banner/pf-v6-banner.ts Outdated
@bennypowers

Copy link
Copy Markdown
Member

this should delete pf-v5-banner and update all references

@zeroedin

Copy link
Copy Markdown
Collaborator Author

this should delete pf-v5-banner and update all references

Ok, good, that answers a question I had about the exports previously. We could update the /update-element skill to do this upon completion, unless you find value in keeping the folder around for any reason post-agent update?

@github-actions

Copy link
Copy Markdown
Contributor

SSR Test Run for e2d426f: Report

@github-actions

Copy link
Copy Markdown
Contributor

SSR Test Run for 561aaec: Report

@zeroedin zeroedin changed the title feat(banner): add pf-v6-banner feat(banner): port pf-v5-banner to pf-v6-banner May 13, 2026
@github-actions

Copy link
Copy Markdown
Contributor

SSR Test Run for 4d23357: Report

@github-actions

Copy link
Copy Markdown
Contributor

SSR Test Run for 31f2e4d: Report

@github-actions

Copy link
Copy Markdown
Contributor

SSR Test Run for ce8094a: Report

@zeroedin

Copy link
Copy Markdown
Collaborator Author

Need to update README.md to include divergences still.

@github-actions

Copy link
Copy Markdown
Contributor

SSR Test Run for 8a916d6: Report

@github-actions

Copy link
Copy Markdown
Contributor

SSR Test Run for e97d6df: Report

@github-actions

Copy link
Copy Markdown
Contributor

SSR Test Run for 0e56ec6: Report

@github-actions

Copy link
Copy Markdown
Contributor

SSR Test Run for 788a0e3: Report

@github-actions

Copy link
Copy Markdown
Contributor

SSR Test Run for c5f6df2: Report

@markcaron markcaron requested a review from bennypowers June 12, 2026 13:18
@markcaron markcaron changed the title feat(banner): port pf-v5-banner to pf-v6-banner feat(banner)!: port to pf-v6-banner Jun 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

SSR Test Run for 91237fe: Report

@github-actions

Copy link
Copy Markdown
Contributor

SSR Test Run for cb5b0bb: Report

@bennypowers

Copy link
Copy Markdown
Member

The React Banner uses a visually-hidden <span> to prepend screen reader
text before the banner content:

{screenReaderText && <span className="pf-v6-screen-reader">{screenReaderText}</span>}
{children}

For the web component, I'd like to use ElementInternals instead of a sr-only span. Here's what I've found exploring the options.

The problem with ariaLabel

ariaLabel via ElementInternals replaces the computed accessible name. Given:

<pf-v6-banner status="success" accessible-label="Success:">
  Job 25 completed
</pf-v6-banner>

With internals.ariaLabel = "Success:", a screen reader announces

"Success:, status"

The slotted content "Job 25 completed" is hidden from the name computation entirely.

There's also a spec issue: aria-label has no effect on elements with role="generic" (the default for custom elements), so without adding a role it's silently ignored.

Proposed approach: role="status" + ariaDescription

#internals = InternalsController.of(this);

@observes('status')
protected statusChanged(): void {
  this.#internals.role = this.status ? 'status' : null;
}

@observes('accessibleLabel')
protected accessibleLabelChanged(): void {
  this.#internals.ariaDescription = this.accessibleLabel || null;
}

ariaDescription is announced after the content, not instead of it. Screen reader would announce:

"Job 25 completed" ... pause ... "Success" ... "status"

Content first, then context. No DOM hacks, no sr-only spans.

Why role="status"

  • Not a landmark (no landmark pollution from multiple banners)
  • Implicit aria-live="polite" -- dynamically inserted status banners get announced automatically, which is the correct behavior
  • Already-present banners don't re-announce on page load (live regions only announce subsequent changes)
  • Only applied when status attribute is set -- color-only decorative banners stay role="generic", no ARIA needed
  • Supports ariaDescription

Conditional: only on status banners

Color-only banners (<pf-v6-banner color="blue">) are decorative -- they get no role and no description. The docs already say accessible-label SHOULD only be used with status banners, so tying role + description to the status attribute enforces that guidance at the API level.

Comparison

Approach SR announces Pros Cons
sr-only span (React) "Success: Job 25 completed" Matches React exactly, simple Extra DOM node, no live region, no semantic role
ariaLabel "Success:, status" No extra DOM Content hidden from AT, wrong
ariaDescription + role="status" "Job 25 completed ... Success ... status" Content first, live region, no extra DOM, semantic Different ordering than React, ariaDescription is newer (baseline 2024)

Looking for a11y review on whether the ariaDescription ordering (content first, then status context) is acceptable, or whether the React-style prepended text is preferred.

cc @adamjohnson @hellogreg

@github-actions

Copy link
Copy Markdown
Contributor

SSR Test Run for e163d52: Report

@bennypowers

Copy link
Copy Markdown
Member

in badge, we removed screenReaderText and advised users to slot visually hidden text into the anonymous slot. Should do that here for API simplicity, or is the difference intentional -- banner is more opinionated (status banners SHOULD have sr text, so the element provides a shortcut) while badge is simpler (just a pill, user decides)?

@github-actions

Copy link
Copy Markdown
Contributor

SSR Test Run for 038589d: Report

@bennypowers bennypowers merged commit cec0ca3 into staging/pfv6 Jun 15, 2026
12 checks passed
@bennypowers bennypowers deleted the feat/pf-v6-banner branch June 15, 2026 13:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AT passed Automated testing has passed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants