Skip to content

feat(packages): add time display toggle#1669

Open
sampotts wants to merge 1 commit into
mainfrom
feat/time-inversion
Open

feat(packages): add time display toggle#1669
sampotts wants to merge 1 commit into
mainfrom
feat/time-inversion

Conversation

@sampotts

@sampotts sampotts commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

Slightly different to the issue in that:

  • type="duration" toggle switches duration ↔ remaining.
  • type="remaining" toggle still switches remaining ↔ duration.
  • type="current" toggle still switches current ↔ remaining.

Why? I noticed Apple Music toggles between duration and remaining and that pattern also worked well with the default skin. Minimal's time layout suited the standard toggle between current and remaining. Happy to run through it on a call if need be.

  • Add a toggle prop/attribute for time displays across core, React, and HTML.
  • Update docs and add focused coverage for core, React, and HTML time behavior.

Validation

  • pnpm -F @videojs/core test src/core/ui/time
  • pnpm -F @videojs/react test src/ui/time
  • pnpm -F @videojs/html test src/ui/time
  • pnpm -F @videojs/skins build
  • git diff --check

Closes #1422


Note

Medium Risk
Changes default and minimal player chrome (trailing time is now remaining-with-toggle) and introduces interactive button semantics on time elements; behavior is covered by tests and remains opt-in for custom UIs.

Overview
Adds an opt-in toggle prop/attribute on time displays so users can switch between paired modes: current ↔ remaining when type="current", or remaining ↔ duration when type is remaining or duration.

TimeCore gains toggle (default false), toggle-specific aria-label strings, and when enabled exposes role="button" and tabIndex={0}. media-time / Time.Value track a local display type, handle click plus Enter/Space (with repeat and defaultPrevented guards), reset when type/toggle change, and strip ARIA when media state is missing.

Built-in minimal skins turn toggle on for the current-time control; default skins keep separate current and slider labels but replace the trailing clock with toggle type="remaining" (was static duration). Skin CSS adds pointer/focus styles for [role="button"] time elements. Docs and core/React/HTML tests cover the new behavior.

Reviewed by Cursor Bugbot for commit 255dbe0. Bugbot is set up for automated code reviews on this repo. Configure here.

@vercel

vercel Bot commented Jun 9, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
v10-sandbox Ready Ready Preview, Comment Jun 10, 2026 12:31am

Request Review

@netlify

netlify Bot commented Jun 9, 2026

Copy link
Copy Markdown

Deploy Preview for vjs10-site ready!

Name Link
🔨 Latest commit 255dbe0
🔍 Latest deploy log https://app.netlify.com/projects/vjs10-site/deploys/6a28b051c81d5900082df31e
😎 Deploy Preview https://deploy-preview-1669--vjs10-site.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle Size Report

🎨 @videojs/html

Path Base PR Diff %
/video/skin.tailwind 40.21 kB 40.51 kB +311 B +0.8% 🔺
/audio/minimal-skin 31.04 kB 31.38 kB +348 B +1.1% 🔺
/audio/minimal-skin.tailwind 31.31 kB 31.69 kB +387 B +1.2% 🔺
/audio/skin.tailwind 34.32 kB 34.66 kB +346 B +1.0% 🔺
/live-video/minimal-skin 34.16 kB 34.48 kB +333 B +1.0% 🔺
/live-video/minimal-skin.tailwind 34.21 kB 34.60 kB +394 B +1.1% 🔺
/live-video/skin 34.19 kB 34.54 kB +364 B +1.0% 🔺
/live-video/skin.tailwind 34.25 kB 34.58 kB +338 B +1.0% 🔺
/live-audio/minimal-skin 25.97 kB 26.35 kB +395 B +1.5% 🔺
/live-audio/minimal-skin.tailwind 25.88 kB 26.19 kB +314 B +1.2% 🔺
/live-audio/skin 28.54 kB 28.91 kB +374 B +1.3% 🔺
/live-audio/skin.tailwind 28.46 kB 28.76 kB +308 B +1.1% 🔺
/ui/time 2.23 kB 2.30 kB +73 B +3.2% 🔺
/video (default) 39.70 kB 40.04 kB +347 B +0.9% 🔺
/video (default + hls) 173.23 kB 173.67 kB +455 B +0.3% 🔺
/video (minimal) 39.22 kB 39.54 kB +329 B +0.8% 🔺
/video (minimal + hls) 172.74 kB 173.33 kB +611 B +0.3% 🔺
/audio (minimal) 31.05 kB 31.38 kB +338 B +1.1% 🔺
Presets (7)
Entry Size
/video (default) 40.04 kB
/video (default + hls) 173.67 kB
/video (minimal) 39.54 kB
/video (minimal + hls) 173.33 kB
/audio (default) 34.36 kB
/audio (minimal) 31.38 kB
/background 4.22 kB
Media (9)
Entry Size
/media/background-video 1.04 kB
/media/container 1.72 kB
/media/dash-video 236.69 kB
/media/hls-video 134.98 kB
/media/mux-audio 160.96 kB
/media/mux-video 160.95 kB
/media/native-hls-video 4.63 kB
/media/simple-hls-audio-only 15.19 kB
/media/simple-hls-video 16.96 kB
Players (5)
Entry Size
/video/player 7.18 kB
/audio/player 5.18 kB
/background/player 3.92 kB
/live-video/player 7.20 kB
/live-audio/player 5.19 kB
Skins (30)
Entry Type Size
/video/minimal-skin.css css 4.87 kB
/video/skin.css css 4.93 kB
/video/minimal-skin js 39.55 kB
/video/minimal-skin.tailwind js 40.01 kB
/video/skin js 40.00 kB
/video/skin.tailwind js 40.51 kB
/audio/minimal-skin.css css 3.06 kB
/audio/skin.css css 3.08 kB
/audio/minimal-skin js 31.38 kB
/audio/minimal-skin.tailwind js 31.69 kB
/audio/skin js 34.32 kB
/audio/skin.tailwind js 34.66 kB
/background/skin.css css 133 B
/background/skin js 1.16 kB
/live-video/minimal-skin.css css 4.87 kB
/live-video/skin.css css 4.93 kB
/live-video/minimal-skin js 34.48 kB
/live-video/minimal-skin.tailwind js 34.60 kB
/live-video/skin js 34.54 kB
/live-video/skin.tailwind js 34.58 kB
/live-audio/minimal-skin.css css 3.06 kB
/live-audio/skin.css css 3.08 kB
/live-audio/minimal-skin js 26.35 kB
/live-audio/minimal-skin.tailwind js 26.19 kB
/live-audio/skin js 28.91 kB
/live-audio/skin.tailwind js 28.76 kB
/global.css css 176 B
/shared.css css 88 B
/tailwind.css css 228 B
/skin-element js 1.37 kB
UI Components (36)
Entry Size
/ui/airplay-button 2.09 kB
/ui/alert-dialog 740 B
/ui/alert-dialog-close 336 B
/ui/alert-dialog-description 318 B
/ui/alert-dialog-title 320 B
/ui/buffering-indicator 2.10 kB
/ui/captions-button 2.14 kB
/ui/cast-button 2.13 kB
/ui/compounds 5.72 kB
/ui/controls 2.10 kB
/ui/error-dialog 2.53 kB
/ui/fullscreen-button 2.11 kB
/ui/hotkey 2.80 kB
/ui/menu 2.78 kB
/ui/mute-button 2.12 kB
/ui/pip-button 2.13 kB
/ui/play-button 2.09 kB
/ui/playback-rate-button 2.24 kB
/ui/playback-rate-menu 3.36 kB
/ui/popover 1.65 kB
/ui/poster 1.90 kB
/ui/seek-button 2.13 kB
/ui/seek-indicator 2.73 kB
/ui/seek-indicator-value 269 B
/ui/slider 1.19 kB
/ui/status-announcer 2.53 kB
/ui/status-indicator 2.53 kB
/ui/status-indicator-value 194 B
/ui/thumbnail 2.52 kB
/ui/time 2.30 kB
/ui/time-slider 3.12 kB
/ui/tooltip 1.86 kB
/ui/volume-indicator 2.83 kB
/ui/volume-indicator-fill 267 B
/ui/volume-indicator-value 253 B
/ui/volume-slider 3.60 kB

Sizes are marginal over the root entry point.

⚛️ @videojs/react — no changes
Presets (7)
Entry Size
/video (default) 33.52 kB
/video (default + hls) 165.92 kB
/video (minimal) 33.63 kB
/video (minimal + hls) 166.04 kB
/audio (default) 27.59 kB
/audio (minimal) 27.64 kB
/background 754 B
Media (8)
Entry Size
/media/background-video 575 B
/media/dash-video 235.21 kB
/media/hls-video 133.61 kB
/media/mux-audio 159.74 kB
/media/mux-video 159.75 kB
/media/native-hls-video 3.13 kB
/media/simple-hls-audio-only 13.86 kB
/media/simple-hls-video 15.54 kB
Skins (27)
Entry Type Size
/tailwind.css css 228 B
/video/minimal-skin.css css 4.78 kB
/video/skin.css css 4.84 kB
/video/minimal-skin js 33.54 kB
/video/minimal-skin.tailwind js 38.57 kB
/video/skin js 33.49 kB
/video/skin.tailwind js 38.55 kB
/audio/minimal-skin.css css 2.93 kB
/audio/skin.css css 2.94 kB
/audio/minimal-skin js 27.54 kB
/audio/minimal-skin.tailwind js 27.57 kB
/audio/skin js 27.48 kB
/audio/skin.tailwind js 30.77 kB
/background/skin.css css 90 B
/background/skin js 272 B
/live-video/minimal-skin.css css 4.78 kB
/live-video/skin.css css 4.84 kB
/live-video/minimal-skin js 24.54 kB
/live-video/minimal-skin.tailwind js 29.13 kB
/live-video/skin js 24.50 kB
/live-video/skin.tailwind js 29.22 kB
/live-audio/minimal-skin.css css 2.93 kB
/live-audio/skin.css css 2.94 kB
/live-audio/minimal-skin js 20.35 kB
/live-audio/minimal-skin.tailwind js 23.06 kB
/live-audio/skin js 20.39 kB
/live-audio/skin.tailwind js 23.18 kB
UI Components (30)
Entry Size
/ui/airplay-button 2.12 kB
/ui/alert-dialog 1.13 kB
/ui/buffering-indicator 1.89 kB
/ui/captions-button 2.14 kB
/ui/cast-button 2.13 kB
/ui/controls 1.89 kB
/ui/error-dialog 2.38 kB
/ui/fullscreen-button 2.12 kB
/ui/gesture 1.32 kB
/ui/hotkey 1.98 kB
/ui/live-button 2.11 kB
/ui/menu 4.73 kB
/ui/mute-button 2.12 kB
/ui/pip-button 2.08 kB
/ui/play-button 2.10 kB
/ui/playback-rate-button 2.16 kB
/ui/playback-rate-menu 4.52 kB
/ui/popover 2.50 kB
/ui/poster 1.79 kB
/ui/seek-button 2.14 kB
/ui/seek-indicator 1.85 kB
/ui/slider 3.29 kB
/ui/status-announcer 1.71 kB
/ui/status-indicator 1.86 kB
/ui/thumbnail 2.07 kB
/ui/time 2.07 kB
/ui/time-slider 2.95 kB
/ui/tooltip 2.63 kB
/ui/volume-indicator 1.91 kB
/ui/volume-slider 2.91 kB

Sizes are marginal over the root entry point.

🧩 @videojs/core — no changes
Entries (10)
Entry Size
. 7.64 kB
/dom 15.70 kB
/dom/media/custom-media-element 1.90 kB
/dom/media/dash 234.36 kB
/dom/media/google-cast 4.07 kB
/dom/media/hls 132.99 kB
/dom/media/mux 158.95 kB
/dom/media/native-hls 2.52 kB
/dom/media/simple-hls 14.89 kB
/dom/media/simple-hls-audio-only 13.14 kB
🏷️ @videojs/element — no changes
Entries (2)
Entry Size
. 996 B
/context 943 B
📦 @videojs/store — no changes
Entries (3)
Entry Size
. 1.39 kB
/html 696 B
/react 360 B
🔧 @videojs/utils — no changes
Entries (10)
Entry Size
/array 104 B
/dom 2.06 kB
/events 319 B
/function 327 B
/object 275 B
/predicate 265 B
/string 192 B
/style 190 B
/time 478 B
/number 158 B
📦 @videojs/spf — no changes
Entries (4)
Entry Size
. 4.45 kB
/dom 6.31 kB
/hls 14.37 kB
/background-looping-video 12.29 kB

ℹ️ How to interpret

All sizes are standalone totals (minified + brotli).

Icon Meaning
No change
🔺 Increased ≤ 10%
🔴 Increased > 10%
🔽 Decreased
🆕 New (no baseline)

Run pnpm size locally to check current sizes.

@sampotts sampotts marked this pull request as ready for review June 9, 2026 05:24
@sampotts sampotts requested review from luwes and mihar-22 June 9, 2026 05:24
Comment on lines +43 to +46
const TOGGLE_LABELS: Partial<Record<TimeType, string>> = {
current: 'Current time, press to show remaining time',
remaining: 'Remaining time, press to show current time',
};

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Will need to update i18n PRs when this is merged.

Comment thread packages/react/src/ui/time/time-value.tsx
/** Custom label for accessibility. */
label?: string | ((state: TimeState) => string) | undefined;
/** Whether current and remaining time can be toggled. */
toggle?: boolean | undefined;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Happy to rename this prop but ideally I'd like to keep it a single word if possible since longer feels weird to me. Other possible options I thought of:

  • interactive although it's a bit broad
  • toggleable not a real word
  • ?

<div class="${time.controls}">
<media-time-group class="${time.group}">
<media-time type="current" class="${time.current}"></media-time>
<media-time toggle type="current" class="${time.current}"></media-time>

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I only added it to the minimal skin as the default skin felt a bit better with the remaining in place of the duration. We'll revisit this before GA though I guess as we may want to make minimal a bit more minimal in terms of UI. I'm just waiting for the menus stuff to merge so we're reasonably feature stable for the skins.

Comment thread packages/html/src/ui/time/time-element.ts
Comment thread packages/html/src/ui/time/time-element.ts
Comment thread packages/html/src/ui/time/time-element.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 436267d. Configure here.

Comment thread packages/html/src/ui/time/time-element.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Feature: Time Display Inversion (Elapsed / Remaining)

1 participant