demo ยท v130
Part State Machine
Chrome 130 allows pseudo-classes like :hover, :focus, :disabled and pseudo-elements like ::before, ::after, ::placeholder after ::part(). Toggle the exported shadow parts below; the visuals come from host-page CSS rules, not inline styles.
Detecting CSS ::part() + pseudo support...
Component states
Live shadow component:
selector boundary
The new grammar accepts pseudo-classes and pseudo-elements after ::part(), but it still rejects re-targeting or tree-structural selectors after a part. These probes run in the current browser.
What Chrome 130 enables
::part(button):hover
Before: mirror hover with host attributes or JS listeners
Chrome 130: direct host stylesheet selector
Style hover state of a shadow part directly from the host document stylesheet.
::part(button):focus
Before: component had to own every focus treatment
Chrome 130: host can add focus ring
Host page can inject a custom focus indicator without changing the component internals.
::part(field)::placeholder
Before: host could expose the field part, but not chain ::placeholder
Chrome 130: host can style placeholder text
The placeholder pseudo-element can now be chained onto the exported input part.
::part(button)::before
Before: no generated content after the part selector
Chrome 130: generated content from host CSS
Add decorative generated content to a shadow part from outside the shadow tree.
::part(button):disabled
Before: component had to expose a custom disabled styling hook
Chrome 130: CSS-only disabled style
Apply dimmed appearance when a shadow button is semantically disabled.
::part(check):checked
Before: checked state styling needed component-owned CSS
Chrome 130: checked state from host CSS
Style a checked checkbox or radio inside shadow DOM from the host stylesheet.
/* Host-page CSS generated by the state toggles. */
part-state-machine[data-states~="hover"]::part(button),
part-state-machine::part(button):hover {
background: color-mix(in srgb, var(--accent-blue) 22%, var(--bg-paper));
}
part-state-machine::part(button):disabled {
opacity: 0.45;
cursor: not-allowed;
}
part-state-machine::part(check):checked {
outline: 2px solid var(--accent-emerald);
}
part-state-machine[data-states~="checked"]::part(button)::before {
content: "checked ";
}