PanelControl accessibility
PanelControl is the accessibility layer: it implements the tablist keyboard pattern and reflects state, so you do not hand-write it.
Selection state
Selection of triggers is managed by PanelSet. So if a user clicks a real tab (a role="tab" inside a role="tablist"), it gets [aria-selected="true"] while the previous tab gets [aria-selected="false"]. Triggers that are not tabs get [aria-current="true"] on the active one instead, since aria-selected is only valid on tabs. That is all automatic. PanelControl adds a bit to that: it makes sure the [tabindex] is also correct during tabbing.
Locking tabs
Your own code (a wizard, a multi-step form) decides when a step should be unreachable. It calls setTabState(); PanelControl only applies it.
control.setTabState('step-3', 'disabled'); // lock
control.setTabState('step-3', 'enabled'); // unlock
'disabled' sets aria-disabled on the tab(s) controlling that panel: keyboard navigation skips it, clicks / Enter no longer activate it, and it cannot hold the roving tab stop (the stop hands off to an enabled tab). Tab locking always uses aria-disabled rather than the native disabled attribute, so a locked tab stays focusable and a screen-reader user can still land on it and hear why.
Disabled hints
Because a locked tab stays focusable, tell the user why it is locked. Point data-pc-disabled-hint at a hidden element, and PanelControl adds it to the tab’s aria-describedby only while the tab is locked, removing it again when unlocked.
<button role="tab" aria-controls="step-3" data-pc-disabled-hint="step3-hint">Confirm</button><span id="step3-hint" hidden>Complete the previous step first</span>
Note: a locked tab’s click is silent (it simply does not activate). The hint covers keyboard and screen-reader users, who can focus the locked tab; the Next button in a wizard, which goes through the ps:beforeactivate check, is what surfaces the reason on a forward attempt.
See the Wizard example for locking and hints working together.