@scarlett-player/audio-ui 0.5.1 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -250,6 +250,8 @@ function createStyles(prefix, theme) {
250
250
  align-items: center;
251
251
  justify-content: center;
252
252
  transition: background 0.2s, transform 0.1s;
253
+ min-width: 44px;
254
+ min-height: 44px;
253
255
  }
254
256
 
255
257
  .${prefix}__btn:hover {
@@ -399,6 +401,8 @@ function createAudioUIPlugin(config) {
399
401
  document.head.appendChild(styleElement);
400
402
  container = document.createElement("div");
401
403
  container.className = `${prefix} ${prefix}--${layout}`;
404
+ container.setAttribute("role", "region");
405
+ container.setAttribute("aria-label", "Audio player");
402
406
  if (layout === "full") {
403
407
  container.innerHTML = buildFullLayout();
404
408
  } else if (layout === "compact") {
@@ -433,24 +437,24 @@ function createAudioUIPlugin(config) {
433
437
  ${mergedConfig.showArtist ? `<div class="${prefix}__artist">-</div>` : ""}
434
438
  </div>
435
439
  <div class="${prefix}__progress">
436
- ${mergedConfig.showTime ? `<span class="${prefix}__time ${prefix}__time--current">0:00</span>` : ""}
437
- <div class="${prefix}__progress-bar">
440
+ ${mergedConfig.showTime ? `<span class="${prefix}__time ${prefix}__time--current" aria-label="Current time">0:00</span>` : ""}
441
+ <div class="${prefix}__progress-bar" role="slider" aria-label="Seek" aria-valuemin="0" aria-valuemax="0" aria-valuenow="0" aria-valuetext="0:00" tabindex="0">
438
442
  <div class="${prefix}__progress-fill" style="transform: scaleX(0)"></div>
439
443
  </div>
440
- ${mergedConfig.showTime ? `<span class="${prefix}__time ${prefix}__time--duration">0:00</span>` : ""}
444
+ ${mergedConfig.showTime ? `<span class="${prefix}__time ${prefix}__time--duration" aria-label="Duration">0:00</span>` : ""}
441
445
  </div>
442
- <div class="${prefix}__controls">
443
- ${mergedConfig.showShuffle ? `<button class="${prefix}__btn ${prefix}__btn--shuffle" title="Shuffle">${ICONS.shuffle}</button>` : ""}
444
- ${mergedConfig.showNavigation ? `<button class="${prefix}__btn ${prefix}__btn--prev" title="Previous">${ICONS.previous}</button>` : ""}
445
- <button class="${prefix}__btn ${prefix}__btn--primary ${prefix}__btn--play" title="Play">${ICONS.play}</button>
446
- ${mergedConfig.showNavigation ? `<button class="${prefix}__btn ${prefix}__btn--next" title="Next">${ICONS.next}</button>` : ""}
447
- ${mergedConfig.showRepeat ? `<button class="${prefix}__btn ${prefix}__btn--repeat" title="Repeat">${ICONS.repeatOff}</button>` : ""}
446
+ <div class="${prefix}__controls" role="group" aria-label="Playback controls">
447
+ ${mergedConfig.showShuffle ? `<button class="${prefix}__btn ${prefix}__btn--shuffle" title="Shuffle" aria-label="Shuffle" aria-pressed="false">${ICONS.shuffle}</button>` : ""}
448
+ ${mergedConfig.showNavigation ? `<button class="${prefix}__btn ${prefix}__btn--prev" title="Previous" aria-label="Previous track">${ICONS.previous}</button>` : ""}
449
+ <button class="${prefix}__btn ${prefix}__btn--primary ${prefix}__btn--play" title="Play" aria-label="Play">${ICONS.play}</button>
450
+ ${mergedConfig.showNavigation ? `<button class="${prefix}__btn ${prefix}__btn--next" title="Next" aria-label="Next track">${ICONS.next}</button>` : ""}
451
+ ${mergedConfig.showRepeat ? `<button class="${prefix}__btn ${prefix}__btn--repeat" title="Repeat" aria-label="Repeat" aria-pressed="false">${ICONS.repeatOff}</button>` : ""}
448
452
  </div>
449
453
  ${mergedConfig.showVolume ? `
450
454
  <div class="${prefix}__secondary-controls">
451
- <div class="${prefix}__volume">
452
- <button class="${prefix}__btn ${prefix}__btn--volume" title="Volume">${ICONS.volumeHigh}</button>
453
- <div class="${prefix}__volume-slider">
455
+ <div class="${prefix}__volume" role="group" aria-label="Volume controls">
456
+ <button class="${prefix}__btn ${prefix}__btn--volume" title="Volume" aria-label="Mute">${ICONS.volumeHigh}</button>
457
+ <div class="${prefix}__volume-slider" role="slider" aria-label="Volume" aria-valuemin="0" aria-valuemax="100" aria-valuenow="100" aria-valuetext="100%" tabindex="0">
454
458
  <div class="${prefix}__volume-fill" style="width: 100%"></div>
455
459
  </div>
456
460
  </div>
@@ -469,21 +473,21 @@ function createAudioUIPlugin(config) {
469
473
  ${mergedConfig.showTitle ? `<div class="${prefix}__title">-</div>` : ""}
470
474
  ${mergedConfig.showArtist ? `<div class="${prefix}__artist">-</div>` : ""}
471
475
  <div class="${prefix}__progress">
472
- <div class="${prefix}__progress-bar">
476
+ <div class="${prefix}__progress-bar" role="slider" aria-label="Seek" aria-valuemin="0" aria-valuemax="0" aria-valuenow="0" aria-valuetext="0:00" tabindex="0">
473
477
  <div class="${prefix}__progress-fill" style="transform: scaleX(0)"></div>
474
478
  </div>
475
479
  </div>
476
480
  </div>
477
- <div class="${prefix}__controls">
478
- ${mergedConfig.showNavigation ? `<button class="${prefix}__btn ${prefix}__btn--prev" title="Previous">${ICONS.previous}</button>` : ""}
479
- <button class="${prefix}__btn ${prefix}__btn--primary ${prefix}__btn--play" title="Play">${ICONS.play}</button>
480
- ${mergedConfig.showNavigation ? `<button class="${prefix}__btn ${prefix}__btn--next" title="Next">${ICONS.next}</button>` : ""}
481
+ <div class="${prefix}__controls" role="group" aria-label="Playback controls">
482
+ ${mergedConfig.showNavigation ? `<button class="${prefix}__btn ${prefix}__btn--prev" title="Previous" aria-label="Previous track">${ICONS.previous}</button>` : ""}
483
+ <button class="${prefix}__btn ${prefix}__btn--primary ${prefix}__btn--play" title="Play" aria-label="Play">${ICONS.play}</button>
484
+ ${mergedConfig.showNavigation ? `<button class="${prefix}__btn ${prefix}__btn--next" title="Next" aria-label="Next track">${ICONS.next}</button>` : ""}
481
485
  </div>
482
486
  `;
483
487
  };
484
488
  const buildMiniLayout = () => {
485
489
  return `
486
- <button class="${prefix}__btn ${prefix}__btn--primary ${prefix}__btn--play" title="Play">${ICONS.play}</button>
490
+ <button class="${prefix}__btn ${prefix}__btn--primary ${prefix}__btn--play" title="Play" aria-label="Play">${ICONS.play}</button>
487
491
  ${mergedConfig.showArtwork ? `
488
492
  <div class="${prefix}__artwork">
489
493
  <img src="${mergedConfig.defaultArtwork || ""}" alt="Album art" />
@@ -492,7 +496,7 @@ function createAudioUIPlugin(config) {
492
496
  <div class="${prefix}__info">
493
497
  ${mergedConfig.showTitle ? `<div class="${prefix}__title-wrapper"><div class="${prefix}__title">-</div></div>` : ""}
494
498
  <div class="${prefix}__progress">
495
- <div class="${prefix}__progress-bar">
499
+ <div class="${prefix}__progress-bar" role="slider" aria-label="Seek" aria-valuemin="0" aria-valuemax="0" aria-valuenow="0" aria-valuetext="0:00" tabindex="0">
496
500
  <div class="${prefix}__progress-fill" style="transform: scaleX(0)"></div>
497
501
  </div>
498
502
  </div>
@@ -558,6 +562,7 @@ function createAudioUIPlugin(config) {
558
562
  if (playPauseBtn) {
559
563
  playPauseBtn.innerHTML = playing ? ICONS.pause : ICONS.play;
560
564
  playPauseBtn.title = playing ? "Pause" : "Play";
565
+ playPauseBtn.setAttribute("aria-label", playing ? "Pause" : "Play");
561
566
  }
562
567
  const currentTime = api.getState("currentTime") || 0;
563
568
  const duration = api.getState("duration") || 0;
@@ -580,6 +585,12 @@ function createAudioUIPlugin(config) {
580
585
  if (durationEl) {
581
586
  durationEl.textContent = formatTime(duration);
582
587
  }
588
+ const progressBar = container?.querySelector(`.${prefix}__progress-bar`);
589
+ if (progressBar) {
590
+ progressBar.setAttribute("aria-valuemax", String(Math.floor(duration)));
591
+ progressBar.setAttribute("aria-valuenow", String(Math.floor(currentTime)));
592
+ progressBar.setAttribute("aria-valuetext", formatTime(currentTime));
593
+ }
583
594
  const title = api.getState("title");
584
595
  const poster = api.getState("poster");
585
596
  if (titleEl && title) {
@@ -604,21 +615,34 @@ function createAudioUIPlugin(config) {
604
615
  }
605
616
  if (volumeBtn) {
606
617
  volumeBtn.innerHTML = muted || volume === 0 ? ICONS.volumeMuted : ICONS.volumeHigh;
618
+ volumeBtn.setAttribute("aria-label", muted || volume === 0 ? "Unmute" : "Mute");
619
+ }
620
+ const volumeSlider = container?.querySelector(`.${prefix}__volume-slider`);
621
+ if (volumeSlider) {
622
+ const displayVolume = Math.round((muted ? 0 : volume) * 100);
623
+ volumeSlider.setAttribute("aria-valuenow", String(displayVolume));
624
+ volumeSlider.setAttribute("aria-valuetext", `${displayVolume}%`);
607
625
  }
608
626
  const playlist = api.getPlugin("playlist");
609
627
  if (playlist) {
610
628
  const state = playlist.getState();
611
629
  if (shuffleBtn) {
612
630
  shuffleBtn.classList.toggle(`${prefix}__btn--active`, state.shuffle);
631
+ shuffleBtn.setAttribute("aria-pressed", String(state.shuffle));
632
+ shuffleBtn.setAttribute("aria-label", state.shuffle ? "Shuffle on" : "Shuffle off");
613
633
  }
614
634
  if (repeatBtn) {
615
635
  repeatBtn.classList.toggle(`${prefix}__btn--active`, state.repeat !== "none");
636
+ repeatBtn.setAttribute("aria-pressed", String(state.repeat !== "none"));
616
637
  if (state.repeat === "one") {
617
638
  repeatBtn.innerHTML = ICONS.repeatOne;
639
+ repeatBtn.setAttribute("aria-label", "Repeat one");
618
640
  } else if (state.repeat === "all") {
619
641
  repeatBtn.innerHTML = ICONS.repeatAll;
642
+ repeatBtn.setAttribute("aria-label", "Repeat all");
620
643
  } else {
621
644
  repeatBtn.innerHTML = ICONS.repeatOff;
645
+ repeatBtn.setAttribute("aria-label", "Repeat off");
622
646
  }
623
647
  }
624
648
  }
package/dist/index.js CHANGED
@@ -225,6 +225,8 @@ function createStyles(prefix, theme) {
225
225
  align-items: center;
226
226
  justify-content: center;
227
227
  transition: background 0.2s, transform 0.1s;
228
+ min-width: 44px;
229
+ min-height: 44px;
228
230
  }
229
231
 
230
232
  .${prefix}__btn:hover {
@@ -374,6 +376,8 @@ function createAudioUIPlugin(config) {
374
376
  document.head.appendChild(styleElement);
375
377
  container = document.createElement("div");
376
378
  container.className = `${prefix} ${prefix}--${layout}`;
379
+ container.setAttribute("role", "region");
380
+ container.setAttribute("aria-label", "Audio player");
377
381
  if (layout === "full") {
378
382
  container.innerHTML = buildFullLayout();
379
383
  } else if (layout === "compact") {
@@ -408,24 +412,24 @@ function createAudioUIPlugin(config) {
408
412
  ${mergedConfig.showArtist ? `<div class="${prefix}__artist">-</div>` : ""}
409
413
  </div>
410
414
  <div class="${prefix}__progress">
411
- ${mergedConfig.showTime ? `<span class="${prefix}__time ${prefix}__time--current">0:00</span>` : ""}
412
- <div class="${prefix}__progress-bar">
415
+ ${mergedConfig.showTime ? `<span class="${prefix}__time ${prefix}__time--current" aria-label="Current time">0:00</span>` : ""}
416
+ <div class="${prefix}__progress-bar" role="slider" aria-label="Seek" aria-valuemin="0" aria-valuemax="0" aria-valuenow="0" aria-valuetext="0:00" tabindex="0">
413
417
  <div class="${prefix}__progress-fill" style="transform: scaleX(0)"></div>
414
418
  </div>
415
- ${mergedConfig.showTime ? `<span class="${prefix}__time ${prefix}__time--duration">0:00</span>` : ""}
419
+ ${mergedConfig.showTime ? `<span class="${prefix}__time ${prefix}__time--duration" aria-label="Duration">0:00</span>` : ""}
416
420
  </div>
417
- <div class="${prefix}__controls">
418
- ${mergedConfig.showShuffle ? `<button class="${prefix}__btn ${prefix}__btn--shuffle" title="Shuffle">${ICONS.shuffle}</button>` : ""}
419
- ${mergedConfig.showNavigation ? `<button class="${prefix}__btn ${prefix}__btn--prev" title="Previous">${ICONS.previous}</button>` : ""}
420
- <button class="${prefix}__btn ${prefix}__btn--primary ${prefix}__btn--play" title="Play">${ICONS.play}</button>
421
- ${mergedConfig.showNavigation ? `<button class="${prefix}__btn ${prefix}__btn--next" title="Next">${ICONS.next}</button>` : ""}
422
- ${mergedConfig.showRepeat ? `<button class="${prefix}__btn ${prefix}__btn--repeat" title="Repeat">${ICONS.repeatOff}</button>` : ""}
421
+ <div class="${prefix}__controls" role="group" aria-label="Playback controls">
422
+ ${mergedConfig.showShuffle ? `<button class="${prefix}__btn ${prefix}__btn--shuffle" title="Shuffle" aria-label="Shuffle" aria-pressed="false">${ICONS.shuffle}</button>` : ""}
423
+ ${mergedConfig.showNavigation ? `<button class="${prefix}__btn ${prefix}__btn--prev" title="Previous" aria-label="Previous track">${ICONS.previous}</button>` : ""}
424
+ <button class="${prefix}__btn ${prefix}__btn--primary ${prefix}__btn--play" title="Play" aria-label="Play">${ICONS.play}</button>
425
+ ${mergedConfig.showNavigation ? `<button class="${prefix}__btn ${prefix}__btn--next" title="Next" aria-label="Next track">${ICONS.next}</button>` : ""}
426
+ ${mergedConfig.showRepeat ? `<button class="${prefix}__btn ${prefix}__btn--repeat" title="Repeat" aria-label="Repeat" aria-pressed="false">${ICONS.repeatOff}</button>` : ""}
423
427
  </div>
424
428
  ${mergedConfig.showVolume ? `
425
429
  <div class="${prefix}__secondary-controls">
426
- <div class="${prefix}__volume">
427
- <button class="${prefix}__btn ${prefix}__btn--volume" title="Volume">${ICONS.volumeHigh}</button>
428
- <div class="${prefix}__volume-slider">
430
+ <div class="${prefix}__volume" role="group" aria-label="Volume controls">
431
+ <button class="${prefix}__btn ${prefix}__btn--volume" title="Volume" aria-label="Mute">${ICONS.volumeHigh}</button>
432
+ <div class="${prefix}__volume-slider" role="slider" aria-label="Volume" aria-valuemin="0" aria-valuemax="100" aria-valuenow="100" aria-valuetext="100%" tabindex="0">
429
433
  <div class="${prefix}__volume-fill" style="width: 100%"></div>
430
434
  </div>
431
435
  </div>
@@ -444,21 +448,21 @@ function createAudioUIPlugin(config) {
444
448
  ${mergedConfig.showTitle ? `<div class="${prefix}__title">-</div>` : ""}
445
449
  ${mergedConfig.showArtist ? `<div class="${prefix}__artist">-</div>` : ""}
446
450
  <div class="${prefix}__progress">
447
- <div class="${prefix}__progress-bar">
451
+ <div class="${prefix}__progress-bar" role="slider" aria-label="Seek" aria-valuemin="0" aria-valuemax="0" aria-valuenow="0" aria-valuetext="0:00" tabindex="0">
448
452
  <div class="${prefix}__progress-fill" style="transform: scaleX(0)"></div>
449
453
  </div>
450
454
  </div>
451
455
  </div>
452
- <div class="${prefix}__controls">
453
- ${mergedConfig.showNavigation ? `<button class="${prefix}__btn ${prefix}__btn--prev" title="Previous">${ICONS.previous}</button>` : ""}
454
- <button class="${prefix}__btn ${prefix}__btn--primary ${prefix}__btn--play" title="Play">${ICONS.play}</button>
455
- ${mergedConfig.showNavigation ? `<button class="${prefix}__btn ${prefix}__btn--next" title="Next">${ICONS.next}</button>` : ""}
456
+ <div class="${prefix}__controls" role="group" aria-label="Playback controls">
457
+ ${mergedConfig.showNavigation ? `<button class="${prefix}__btn ${prefix}__btn--prev" title="Previous" aria-label="Previous track">${ICONS.previous}</button>` : ""}
458
+ <button class="${prefix}__btn ${prefix}__btn--primary ${prefix}__btn--play" title="Play" aria-label="Play">${ICONS.play}</button>
459
+ ${mergedConfig.showNavigation ? `<button class="${prefix}__btn ${prefix}__btn--next" title="Next" aria-label="Next track">${ICONS.next}</button>` : ""}
456
460
  </div>
457
461
  `;
458
462
  };
459
463
  const buildMiniLayout = () => {
460
464
  return `
461
- <button class="${prefix}__btn ${prefix}__btn--primary ${prefix}__btn--play" title="Play">${ICONS.play}</button>
465
+ <button class="${prefix}__btn ${prefix}__btn--primary ${prefix}__btn--play" title="Play" aria-label="Play">${ICONS.play}</button>
462
466
  ${mergedConfig.showArtwork ? `
463
467
  <div class="${prefix}__artwork">
464
468
  <img src="${mergedConfig.defaultArtwork || ""}" alt="Album art" />
@@ -467,7 +471,7 @@ function createAudioUIPlugin(config) {
467
471
  <div class="${prefix}__info">
468
472
  ${mergedConfig.showTitle ? `<div class="${prefix}__title-wrapper"><div class="${prefix}__title">-</div></div>` : ""}
469
473
  <div class="${prefix}__progress">
470
- <div class="${prefix}__progress-bar">
474
+ <div class="${prefix}__progress-bar" role="slider" aria-label="Seek" aria-valuemin="0" aria-valuemax="0" aria-valuenow="0" aria-valuetext="0:00" tabindex="0">
471
475
  <div class="${prefix}__progress-fill" style="transform: scaleX(0)"></div>
472
476
  </div>
473
477
  </div>
@@ -533,6 +537,7 @@ function createAudioUIPlugin(config) {
533
537
  if (playPauseBtn) {
534
538
  playPauseBtn.innerHTML = playing ? ICONS.pause : ICONS.play;
535
539
  playPauseBtn.title = playing ? "Pause" : "Play";
540
+ playPauseBtn.setAttribute("aria-label", playing ? "Pause" : "Play");
536
541
  }
537
542
  const currentTime = api.getState("currentTime") || 0;
538
543
  const duration = api.getState("duration") || 0;
@@ -555,6 +560,12 @@ function createAudioUIPlugin(config) {
555
560
  if (durationEl) {
556
561
  durationEl.textContent = formatTime(duration);
557
562
  }
563
+ const progressBar = container?.querySelector(`.${prefix}__progress-bar`);
564
+ if (progressBar) {
565
+ progressBar.setAttribute("aria-valuemax", String(Math.floor(duration)));
566
+ progressBar.setAttribute("aria-valuenow", String(Math.floor(currentTime)));
567
+ progressBar.setAttribute("aria-valuetext", formatTime(currentTime));
568
+ }
558
569
  const title = api.getState("title");
559
570
  const poster = api.getState("poster");
560
571
  if (titleEl && title) {
@@ -579,21 +590,34 @@ function createAudioUIPlugin(config) {
579
590
  }
580
591
  if (volumeBtn) {
581
592
  volumeBtn.innerHTML = muted || volume === 0 ? ICONS.volumeMuted : ICONS.volumeHigh;
593
+ volumeBtn.setAttribute("aria-label", muted || volume === 0 ? "Unmute" : "Mute");
594
+ }
595
+ const volumeSlider = container?.querySelector(`.${prefix}__volume-slider`);
596
+ if (volumeSlider) {
597
+ const displayVolume = Math.round((muted ? 0 : volume) * 100);
598
+ volumeSlider.setAttribute("aria-valuenow", String(displayVolume));
599
+ volumeSlider.setAttribute("aria-valuetext", `${displayVolume}%`);
582
600
  }
583
601
  const playlist = api.getPlugin("playlist");
584
602
  if (playlist) {
585
603
  const state = playlist.getState();
586
604
  if (shuffleBtn) {
587
605
  shuffleBtn.classList.toggle(`${prefix}__btn--active`, state.shuffle);
606
+ shuffleBtn.setAttribute("aria-pressed", String(state.shuffle));
607
+ shuffleBtn.setAttribute("aria-label", state.shuffle ? "Shuffle on" : "Shuffle off");
588
608
  }
589
609
  if (repeatBtn) {
590
610
  repeatBtn.classList.toggle(`${prefix}__btn--active`, state.repeat !== "none");
611
+ repeatBtn.setAttribute("aria-pressed", String(state.repeat !== "none"));
591
612
  if (state.repeat === "one") {
592
613
  repeatBtn.innerHTML = ICONS.repeatOne;
614
+ repeatBtn.setAttribute("aria-label", "Repeat one");
593
615
  } else if (state.repeat === "all") {
594
616
  repeatBtn.innerHTML = ICONS.repeatAll;
617
+ repeatBtn.setAttribute("aria-label", "Repeat all");
595
618
  } else {
596
619
  repeatBtn.innerHTML = ICONS.repeatOff;
620
+ repeatBtn.setAttribute("aria-label", "Repeat off");
597
621
  }
598
622
  }
599
623
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scarlett-player/audio-ui",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Audio UI Plugin for Scarlett Player - Compact audio player interface with album art and waveform progress",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -22,14 +22,14 @@
22
22
  "dist"
23
23
  ],
24
24
  "peerDependencies": {
25
- "@scarlett-player/core": "^0.5.1"
25
+ "@scarlett-player/core": "^0.5.2"
26
26
  },
27
27
  "devDependencies": {
28
28
  "tsup": "^8.0.0",
29
29
  "typescript": "^5.3.0",
30
30
  "vitest": "^1.6.0",
31
31
  "jsdom": "^24.0.0",
32
- "@scarlett-player/core": "0.5.1"
32
+ "@scarlett-player/core": "0.5.2"
33
33
  },
34
34
  "keywords": [
35
35
  "audio",