name: pf-compare description: >- This skill should be used when comparing a PatternFly component against its PatternFly Java (PFJ) implementation. Triggers include /pf-compare, "compare PF component", "check PFJ completeness", "compare button component", "what's missing in the Java card", "gap analysis for alert", "generate comparison report for tabs", "find missing PF variations", "PF coverage report", "DOM differences", or any request to identify variation coverage gaps or DOM/CSS differences between PatternFly and PatternFly Java. metadata: version: "0.2.0"
/pf-compare — PatternFly Component Comparison
Compares a PatternFly (React) component against its PatternFly Java implementation. Uses two phases: section coverage (which demo sections exist?) and DOM comparison (how do matched sections differ structurally?).
Tools
Uses Chrome DevTools MCP tools for browser interaction (navigate_page, new_page, select_page, evaluate_script, close_page, list_pages, take_snapshot). Requires approval on first use.
Arguments
/pf-compare <component> [--port <port>]
- component (required) — URL slug of a single component (e.g.,
button,card,data-list). One component per invocation. Rejecttemplate— it is a blueprint component, not a real UI component. - --port (optional) — PFJ local dev server port (default:
1234)
Parse from the ARGUMENTS string. If no component is provided, ask the user which component to compare.
Step 1: Pre-flight Checks
Run in order. Stop on failure.
Parse arguments: Extract
COMPONENTand optional--port(default1234). If no component is provided, ask the user.Resolve PFJ showcase URL — try in order:
- Run:
curl -s -o /dev/null -w "%{http_code}" http://localhost:<port>/components/<COMPONENT> - If HTTP 200, set
PFJ_URL = http://localhost:<port>/components/<COMPONENT> - Otherwise, use the published showcase: set
PFJ_URL = https://patternfly-java.github.io/components/<COMPONENT>- Note: The published PFJ showcase is a single-page app with client-side routing.
curlwill return 404 for deep links, but the page loads correctly in a browser. Do NOT use curl to verify this URL — trust it and verify via browser navigation in Step 2.
- Note: The published PFJ showcase is a single-page app with client-side routing.
- Run:
Verify PF site:
- Run:
curl -s -o /dev/null -w "%{http_code}" -L https://www.patternfly.org/components/<COMPONENT> - Note the
-Lflag to follow redirects (PF site uses 302 redirects to add trailing slashes). - If not 200, stop with: "Could not reach PF showcase at
https://www.patternfly.org/components/<COMPONENT>. Verify the component name."
- Run:
Set variables:
PF_URL = https://www.patternfly.org/components/<COMPONENT>,PFJ_URL,COMPONENTPrint: "Pre-flight OK. Comparing PF (
PF_URL) against PFJ (PFJ_URL)."
Step 2: Extract Sections
Extract example sections from both pages using jump links navigation.
2a: PF sections
- Open
PF_URLin a new Chrome tab vianew_page. - Read the script from
references/extract-sections.jsand pass it toevaluate_scriptwithargs: ["pf", COMPONENT]. The component slug is needed to validate that each section has a correspondingws-react-c-{component}-{id}preview container. - Store the result as
PF_SECTIONS— an array of{ id, title, group }. - Report: "Extracted N PF sections: (list of titles)"
2b: PFJ sections
- Open
PFJ_URLin a new Chrome tab vianew_page. - Read the script from
references/extract-sections.jsand pass it toevaluate_scriptwithargs: ["pfj"]. - Store the result as
PFJ_SECTIONS— an array of{ id, title, group }. - If
PFJ_SECTIONSis empty, warn the user that the PFJ page may not have loaded correctly and suggest checkingPFJ_URLin a browser manually before continuing. - Report: "Extracted M PFJ sections: (list of titles)"
Step 3: Match Sections
Perform AI-assisted semantic matching between PF_SECTIONS and PFJ_SECTIONS. Apply rules in order:
- Exact match (case-insensitive title comparison) — automatic match.
- Semantic match (e.g., "Aria-disabled examples" matches "Aria-disabled") — match with a note explaining the reasoning.
- No match — mark as
missing_in_pfj.
After matching, check for unmatched PFJ sections — mark as extra_in_pfj.
Produce three lists:
- MATCHED — array of
{ pf: { id, title, group }, pfj: { id, title, group } } - MISSING_IN_PFJ — PF sections with no PFJ counterpart
- EXTRA_IN_PFJ — PFJ sections with no PF counterpart
Report the matching table showing each PF section, its matched PFJ section (or "---"), the group, and the match status.
Step 4: DOM Comparison
For each entry in MATCHED, compare the rendered preview HTML.
4a: Extract preview HTML
For each matched pair:
PF preview: Switch to the PF tab. Run
evaluate_scriptto extract innerHTML from the preview container. The container hasid="ws-react-c-<COMPONENT>-<pf.id>"(e.g.,ws-react-c-button-variant-examples). Extract itsinnerHTML.(sectionId) => { const el = document.getElementById(sectionId); return el ? el.innerHTML : null; }Pass
args: ["ws-react-c-" + COMPONENT + "-" + pf.id].PFJ preview: Switch to the PFJ tab. Run
evaluate_scriptto find the[data-pfj-demo]element within the section identified by the PFJ heading ID. The heading hasid="<pfj.id>". Walk up to the parent stack, find the[data-pfj-demo]element, and extract itsinnerHTML.(headingId) => { const heading = document.getElementById(headingId); if (!heading) return null; const stack = heading.parentElement?.parentElement?.parentElement; if (!stack) return null; const demo = stack.querySelector('[data-pfj-demo]'); return demo ? demo.innerHTML : null; }Pass
args: [pfj.id].
Store both HTML strings as PF_HTML and PFJ_HTML.
4b: Normalize both sides
For each matched pair:
- Read the script from
references/normalize-dom.js. - Run it via
evaluate_scripttwice — once withPF_HTMLand once withPFJ_HTMLas the argument. - Store the results as
PF_ELEMENTSandPFJ_ELEMENTS— arrays of element signature objects.
4c: AI comparison
For each matched section, compare PF_ELEMENTS and PFJ_ELEMENTS. Classify each difference by priority:
| Priority | Category | What to look for |
|---|---|---|
| P1 | Missing component elements | PF has an element with a pf-v6-c-* class that has no counterpart in PFJ (by class signature). This usually means a sub-component or structural element is missing. |
| P2 | Modifier differences | Same element exists on both sides (matching pf-v6-c-* class) but different pf-m-* modifiers. A modifier combination in PF has no match in PFJ, or vice versa. |
| P3 | Attribute differences | Same element, but different aria-* attributes, role, type, disabled, or tabindex values. Missing or different aria-label is a common finding. |
| P4 | Icon differences | SVG elements with different viewBox values. This usually indicates different icon sets (FontAwesome vs Red Hat icons). |
| P5 | Cosmetic / showcase-only | Element count differences from showcase layout (flex wrappers, spacing elements), text content differences, or differences in non-functional attributes. |
For detailed normalization rules and what normalize-dom.js strips vs. keeps, see references/ignore-patterns.md.
Guidelines for the AI comparison:
- Compare elements by their
classesarray (component class signature). Two elements "match" if they share the same primarypf-v6-c-*class. - Do NOT report differences in element count alone — only report what specifically differs.
- When PF has more elements than PFJ, identify which specific elements are missing, not just the count delta.
- Consider showcase layout context: flex wrappers (
pf-v6-l-flex,pf-v6-l-stack) are often showcase layout, not component structure. - If both sides have the same component elements with the same modifiers and attributes, report "ok".
4d: Store results
For each matched section, produce:
{
"section": "<PF title>",
"status": "ok | differences_found",
"differences": [
{
"priority": "P1",
"description": "..."
}
]
}
Store as COMPARISON_RESULTS.
Step 5: Print Inline Summary
Print the following summary directly in the conversation:
## PF Compare: <COMPONENT>
### Section Coverage: <MATCHED count>/<PF total> sections (<percentage>%)
Missing in PFJ:
- title 1
- title 2
Extra in PFJ:
- title 1
### DOM Differences:
section: P2 (2 modifier diffs), P3 (1 attribute diff)
section: OK
section: P4 (1 icon diff)
Full report: reports/pf-compare/<COMPONENT>.md
Step 6: Write Detailed Report
Create the output directory:
mkdir -p reports/pf-compareDetect the PF version by switching to the PF tab and running the script from
references/detect-pf-version.jsviaevaluate_script.Generate the report file using the template from
references/report-template.md. Read that file and fill in the placeholders with actual data.Write the report to
reports/pf-compare/<COMPONENT>.md.Write the JSON companion report to
reports/pf-compare/<COMPONENT>.json. This file contains the same data in structured form. Use the schema fromreferences/report-schema.jsonand match the format inexamples/button.json. The JSON includes:- Metadata: skillVersion (from
metadata.versionin this SKILL.md), component, date, pfVersion, pfUrl, pfjUrl - Section coverage: pfCount, pfjCount, matched, missingInPfj, extraInPfj
- Full
variationsarray from Step 4a — each entry has{ slug, title, html }whereslugis the PF section ID,titleis the section heading, andhtmlis the raw (pre-normalization) PF preview innerHTML. This array is consumed by/pf-alignfor reference HTML. - Action items with number, type, priority, title, description, category, and affected variations
- Metadata: skillVersion (from
Report: "Reports saved to
reports/pf-compare/<COMPONENT>.mdandreports/pf-compare/<COMPONENT>.json"
Step 7: Cleanup
- Use
list_pagesto find the PF and PFJ tabs opened during the workflow. - Close them with
close_page(keep at least one tab open in the browser). - Print completion message with next steps:
- Review the report at
reports/pf-compare/<COMPONENT>.md - JSON data available at
reports/pf-compare/<COMPONENT>.json(used by/pf-align) - Use the report as input for a future
/pf-alignskill - Run
/pf-compareon another component
- Review the report at
Error Handling
- Component not found: If curl returns non-200 for both PFJ URLs, report clearly and suggest checking the component slug or starting the dev server.
- No sections extracted: If PF or PFJ returns zero sections, warn the user that the page structure may have changed and the jump links selectors may need updating.
- Chrome DevTools unavailable: If MCP tools fail, report the error and suggest ensuring Chrome is running with DevTools MCP connected.
- Preview HTML not found: If a preview container is not found for a matched section, skip that section's DOM comparison and note it in the report. This can happen if the PF page uses a different preview container pattern for that section.
- Partial data: If only some comparisons succeed, still produce the report with available data and note which comparisons failed.
Anti-Patterns
- Blocking on cosmetic differences: Do not report P5 cosmetic differences as blocking issues.
- Comparing non-PF classes: Only compare classes prefixed with
pf-v6-orpf-m-. Ignore application-specific or utility classes. - Deep SVG comparison: Do not traverse SVG internals. Only compare the
viewBoxattribute. - Overwriting reports without asking: If a report already exists at the target path, note it in the output but proceed with overwriting (the file is regenerable).
- Reporting element counts as issues: An element count difference alone is not a finding. Identify what specifically is missing or extra.