This codemod automatically adds standardized data-* attributes to all PatternFly components in your codebase, making them more AI-friendly and easier for AI tools to understand.
The codemod transforms your PatternFly components from:
<Card isClickable>
<CardBody>
I'm a card
<Button variant="danger">Cancel</Button>
</CardBody>
</Card>Into:
<Card
isClickable
data-role="card"
data-purpose="clickable"
data-variant="default"
data-context="default"
data-state="active"
>
<CardBody
data-role="card-body"
data-purpose="display"
data-variant="default"
data-context="default"
data-state="default"
>
I'm a card
<Button
variant="danger"
data-role="button"
data-purpose="action"
data-variant="danger"
data-context="default"
data-state="active"
>
Cancel
</Button>
</CardBody>
</Card>Rendered HTML in Browser:
<div class="pf-c-card" data-role="card" data-purpose="clickable" data-variant="default" data-context="default" data-state="active">
<div class="pf-c-card__body" data-role="card-body" data-purpose="display" data-variant="default" data-context="default" data-state="default">
I'm a card
<button class="pf-c-button pf-m-danger" data-role="button" data-purpose="action" data-variant="danger" data-context="default" data-state="active">
Cancel
</button>
</div>
</div>Every PatternFly component gets the same 5 attributes that appear on the rendered DOM elements:
| Attribute | Description | Example Values |
|---|---|---|
data-role |
What the component IS | button, card, input, modal |
data-purpose |
What it DOES | action, display, input, navigation |
data-variant |
How it LOOKS | primary, danger, secondary, text |
data-context |
Where it's USED | form, modal, table, toolbar |
data-state |
Current STATE | active, disabled, selected, readonly |
Important: These attributes will appear on the actual rendered HTML elements in your browser. React automatically forwards all data-* attributes to the underlying DOM elements, and PatternFly components respect this behavior.
# Transform all files in src/
./codemod/add-semantic-attributes.sh src/
# Transform a specific file
./codemod/add-semantic-attributes.sh src/components/MyComponent.tsx
# Transform current directory
./codemod/add-semantic-attributes.sh# Install jscodeshift globally (if not already installed)
npm install -g jscodeshift
# Run the transform
jscodeshift -t codemod/transform.js --extensions=ts,tsx,js,jsx --parser=tsx src/-
Component Detection: Scans import statements to identify PatternFly components
- Recognizes imports from
@patternfly/react-core,@patternfly/react-table, etc. - Handles both named and default imports
- Handles aliased imports (e.g.,
import { Button as PFButton }) - Works for ALL PatternFly components, not just wrapped ones
- Recognizes imports from
-
Static Inference: Analyzes component props to infer semantic properties
- Enhanced inference for components with specific logic (Button, Card, Modal, Form, Input, Select, etc.)
- Generic inference with fallbacks for other PF components (Alert, Breadcrumb, Tabs, etc.)
- Reads
variant,type,onClick,isDisabled, etc. - Determines purpose from component name and props
- Detects parent context for nested components
-
Attribute Injection: Adds standardized attributes without modifying existing code
- Preserves all existing props and formatting
- Skips components that already have semantic attributes (no duplicates)
- Maintains code style and comments
-
DOM Rendering: Attributes automatically appear on rendered HTML elements
- React forwards all
data-*attributes to DOM elements - PatternFly components respect React's attribute forwarding
- Attributes are queryable in browser DevTools
- React forwards all
Components with Enhanced Inference:
- Button, Card, Modal, Form, TextInput, TextArea, Select, Checkbox, Radio, Switch
- Flex, FlexItem, Table components (Th, Td, Tr, Thead, Tbody)
- Link, Drawer, MenuToggle, DropdownItem
- Accordion, ActionList, Alert, Avatar, Banner, Breadcrumb, ClipboardCopy, CodeBlock, Content
Components with Generic Inference:
- All other PatternFly components (Breadcrumb, Tabs, Popover, Tooltip, Wizard, etc.)
- Uses heuristics and fallbacks to provide reasonable defaults
- Still adds all 7 standardized attributes
Components We Skip:
- Static components without meaningful variants, states, or interactive behavior
- Specifically skipped: Backdrop, BackgroundImage, BackToTop, Brand
- These components always behave the same and don't benefit from semantic attributes
Components Not Currently Supported:
- CodeEditor - Not currently supported (may be added in the future)
Component Nesting Restrictions:
- Content - PatternFly recommends using the
componentprop (e.g.,component="h1") instead of nesting other components within<Content>. The codemod will add semantic attributes to Content components, but developers should follow PatternFly's nesting guidelines.
The codemod works with ALL components imported from these PatternFly packages:
@patternfly/react-core- All core components (Button, Card, Modal, Form, Input, Select, etc.)@patternfly/react-table- All table components (Table, Th, Td, Tr, Thead, Tbody, etc.)@patternfly/react-icons- Icon components@patternfly/react-charts- Chart components@patternfly/react-topology- Topology components
Important: The codemod processes any component imported from these packages, not just the ones we've wrapped. It uses intelligent inference with fallbacks for unknown components.
// Input
<Button variant="danger" onClick={handleDelete}>Delete</Button>
// Output (attributes added)
<Button
variant="danger"
onClick={handleDelete}
data-role="button"
data-purpose="action"
data-variant="danger"
data-context="default"
data-state="active"
>
Delete
</Button>Rendered HTML:
<button
class="pf-c-button pf-m-danger"
data-role="button"
data-purpose="action"
data-variant="danger"
data-context="default"
data-state="active"
>
Delete
</button>// Input
<Card isClickable isSelected>
<CardBody>Content</CardBody>
</Card>
// Output
<Card
isClickable
isSelected
data-role="card"
data-purpose="clickable"
data-variant="default"
data-context="default"
data-state="selected"
>
<CardBody
data-role="card-body"
data-purpose="display"
data-variant="default"
data-context="default"
data-state="default"
>
Content
</CardBody>
</Card>Rendered HTML:
<div class="pf-c-card" data-role="card" data-purpose="clickable" data-variant="default" data-context="default" data-state="selected">
<div class="pf-c-card__body" data-role="card-body" data-purpose="display" data-variant="default" data-context="default" data-state="default">
Content
</div>
</div>// Input
<TextInput type="email" isRequired />
// Output
<TextInput
type="email"
isRequired
data-role="text-input"
data-purpose="input"
data-variant="email"
data-context="form"
data-state="default"
/>Rendered HTML:
<input
type="email"
class="pf-c-form-control"
required
data-role="text-input"
data-purpose="input"
data-variant="email"
data-context="form"
data-state="default"
/>The codemod uses static analysis (what it can see in your source code), not runtime values:
✅ Works:
variant="danger"(literal string)isDisabled={true}(literal boolean)onClick={handler}(prop exists)
❌ Can't Detect:
variant={someVariable}(variable value unknown)onClick={condition ? handler1 : handler2}(runtime decision)- Dynamic props from state or context
Parent context is detected by analyzing the JSX tree structure, but:
- Only works for direct parent-child relationships
- Limited to 10 levels deep (prevents infinite loops)
- May not detect context from React Context API
Edit codemod/static-inference.js:
const PF_PACKAGES = [
'@patternfly/react-core',
'@patternfly/react-table',
'@patternfly/react-icons',
'@patternfly/react-charts',
'@patternfly/react-topology',
'@patternfly/react-new-package', // Add here
];Edit codemod/static-inference.js:
function inferPurpose(componentName, props) {
// Add your custom logic here
if (componentName === 'MyNewComponent') {
return 'custom-purpose';
}
// ... existing logic
}Edit codemod/transform.js to add or modify attributes:
const newAttributes = [
j.jsxAttribute(
j.jsxIdentifier('data-semantic-role'),
j.literal(role)
),
// Add more attributes here
j.jsxAttribute(
j.jsxIdentifier('data-custom-attr'),
j.literal('custom-value')
),
];- Check imports: Make sure components are imported from PatternFly packages
- Check file extensions: Only
.ts,.tsx,.js,.jsxfiles are processed - Check existing attributes: Components with existing
data-semantic-*attributes are skipped
The codemod uses heuristics to infer values. If results are incorrect:
- Manually add attributes to override inferred values
- Update inference logic in
static-inference.js - Report issues with specific component patterns
The codemod preserves your existing formatting. If you see formatting changes:
- Run your formatter (Prettier, ESLint) after the transform
- The codemod uses single quotes and trailing commas by default
- Run on Clean Code: Transform before adding custom logic
- Version Control: Commit before running, review changes after
- Test After: Verify your app still works after transformation
- Incremental: Transform one directory at a time for large codebases
- Review Changes: Use
git diffto review what was changed
Once attributes are added and rendered, AI tools can query the DOM:
- Query by purpose: Find all action buttons:
[data-purpose="action"] - Query by context: Find all form inputs:
[data-context="form"] - Query by state: Find all disabled components:
[data-state="disabled"] - Query by role: Find all cards:
[data-role="card"] - Query by variant: Find all danger buttons:
[data-variant="danger"]
Example Browser Queries:
// Find all action buttons
document.querySelectorAll('[data-purpose="action"]')
// Find all form inputs
document.querySelectorAll('[data-context="form"]')
// Find all danger variants
document.querySelectorAll('[data-variant="danger"]')To improve the codemod:
- Add new inference rules in
static-inference.js - Add support for new PatternFly components
- Improve parent context detection
- Add more sophisticated static analysis
MIT License - same as the main project