Skip to content

Conversation

@luwes
Copy link
Collaborator

@luwes luwes commented Jan 22, 2026

fix #265

This pull request introduces a comprehensive implementation plan for the new PlayButton UI component, which will serve as a model for future UI components across the project. The plan details a multi-layered approach, covering core logic, DOM integration, React and HTML custom element implementations, and accessibility/testing requirements. Additionally, a minor fix is made to the commitlint script in package.json.

Component architecture and implementation plan:

  • Added a detailed multi-phase plan for the PlayButton component, outlining interfaces, implementation details, and file structure across the Core, DOM, React, and HTML packages. This plan establishes conventions for component state, props, event handling, accessibility, and testing. (.claude/plans/components/play-button.md)

Accessibility and testing standards:

  • Defined accessibility requirements (ARIA roles, keyboard navigation, disabled state) and a shared testing strategy to ensure behavioral parity across React and HTML implementations. (.claude/plans/components/play-button.md)

Development workflow improvement:

  • Fixed the commitlint script in package.json to remove the unused $1 argument, ensuring consistent commit message linting. (package.json)

@luwes luwes self-assigned this Jan 22, 2026
@vercel
Copy link

vercel bot commented Jan 22, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Review Updated (UTC)
vjs-10-website Ignored Ignored Preview Jan 23, 2026 7:05pm

Request Review

interface PlayButtonDataAttributes {
'data-paused'?: '';
'data-ended'?: '';
'data-disabled'?: '';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of interest, why not use the disabled attribute?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see there's actually some more nuance here. This part in Base UI does manage disabled but carefully. They're actively removing native disabled in composite widgets (toolbars, menus, tabs).

Native disabled:

  • Removes from tab order entirely
  • Unfocusable
  • Screen reader might skip it completely

Based on what I'm gathering, user should be able to tab through and hear "play button, disabled" rather than it vanishing from keyboard navigation. So we would set aria-disabled="true" to provide a hint, and I guess data-disabled is for styling/consistency/docs.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That all makes sense! Good points. A related nit though; if we're going with aria-disabled, why don't we just use that for styling etc too?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback. Updated to remove data-disabled here.
We can use disabled or aria-disabled to style.

- `aria-label: state.label`
- `data-paused`, `data-ended`, `data-disabled` (boolean attributes)
- `onClick` handler
- `onKeyDown` handler (Enter/Space)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Enter and space will trigger a click event when using a <button> so would we end up with two events?

Copy link
Member

@mihar-22 mihar-22 Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trade off with the above on managing disabled ourselves is that we need to hook into these events and manage disabled ourselves.

If we agree on how we treat disabled, then we'll need to abstract this somehow for all buttons.

Comment on lines +164 to +165
- Uses `SnapshotController` for state
- Uses `RequestController` for play/pause
Copy link
Member

@mihar-22 mihar-22 Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I simplified some of the controllers/hooks here, we'll just need Claude to re-run implementation plan.

I imagine we have to wait for #323 partially so we have feature accessors (e.g., getFeature(store)) before implementing this component. I'll create a PR tomorrow prob to get that smaller piece through so UI is unblocked.

### Registration

```ts
customElements.define('media-play-button', PlayButtonElement);
Copy link
Member

@mihar-22 mihar-22 Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth discussing with the team to confirm what prefix we're going with. I'll need to update existing Claude plans and what not to match.

Few arguments come to mind for vjs- over media-:

  1. You don't end up with funny names like media-frosted-video-skin (media+video).
  2. We used media- in Media Chrome and Vidstack. Prevent collision with either. Look distinct too.
  3. vjs is iconic. I would recognize it instantly. It also popups when you inspect old player in class list.

Beyond that if the team decides media-, I'm down either way.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, I think I still prefer media- for it's more native, generic feeling.

I'm wondering now if we could pull off prefix-less if there are 2 or more words in the name.
Why not just play-button, time-slider?

With scoped registries on the horizon.

https://caniuse.com/wf-scoped-custom-element-registries
https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry
https://github.com/justinfagnani/webcomponents/blob/scoped-registries/proposals/Scoped-Custom-Element-Registries.md

Copy link
Collaborator

@decepulis decepulis Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can only offer my opinion as a matter of taste. I don't really have any facts to bring to the table. But. Of the three options, I really like vjs-.

To me, using media- on media chrome felt like a branding exercise. "Web components are native HTML. They're timeless. Free from the constraints of component libraries. The media components you wish the platform had built in. Let's use timeless, platform-seeming names, too."

Meanwhile, from a brand standpoint, video.js 10 feels like more its own thing, rather than a thing of the platform. It has a bright brand on its website. It has an eye-catching skin. It demands that you learn the provider, the skin, and the media source. Everything about it feels opinionated. That just feels very vjs- to me.

MIND YOU. I haven't done truly native HTML dev in almost a decade. I don't know what naming conventions are idiomatic in web components. What's more important to me than a brand expression is idiomatic web component behavior. Good DX. If vjs- stands in the way of that, I take my opinion back. (And, maybe in the near future, scoped registries with generic names will be good DX. So. That's another potential wrench in my spiel.)

Comment on lines +94 to +98
function getPlayButtonProps(params: {
props: PlayButtonProps;
state: PlayButtonState;
onToggle: () => void;
}): PlayButtonDOMProps;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering if we should go a step further to simplify and just have getRootProps on core component class.

Similar to Zag.js, we can just define our own ElementProps in core (jsx), then map accordingly in html/react packages? Just a thought to co-locate more code and simplify.

onPauseRequest?: () => void;
onPlay?: () => void;
onPause?: () => void;
onError?: (error: Error) => void;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't think of a reason for error handling at this level. Is there a clear one? If not I suggest we drop things we don't know we need.

@mihar-22
Copy link
Member

Closing this one out as we're re-assigning work. I'll take the feedback from here and apply to internal design doc.

@mihar-22 mihar-22 closed this Jan 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

UI: Play Button Component

6 participants