# Adding buttons to the UI

This example adds an Info menu, a Skip intro button and seek back/forward buttons to the player.

## Open Video UI

Tip: open your browser's developer console and use the `player` and `THEOplayer` variables to interact with this demo.

The Info button opens a menu with live player values. Skip intro seeks to 10 seconds and disappears after that point. Two `<theoplayer-seek-button>` elements in the `bottom-control-bar` seek 10 seconds back and forward (hidden on mobile, where the centered seek buttons already do this).

Code

demo.html

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Adding buttons to the UI</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link href="https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet" />
    <style>
      html,
      body {
        margin: 0;
        padding: 0;
      }

      theoplayer-default-ui:not(:defined) {
        display: inline-block;
        box-sizing: border-box;
      }

      theoplayer-default-ui {
        width: 100%;
        aspect-ratio: 16 / 9;
        font-family: 'Noto Sans', sans-serif;
        background: #000;
      }

      #info-menu {
        box-sizing: border-box;
        max-width: min(22rem, calc(100vw - 2rem));
        padding: 1rem;
        color: #fff;
      }

      #info-menu [slot='heading'] {
        font-weight: 700;
      }

      .info-values {
        display: grid;
        grid-template-columns: auto 1fr;
        gap: 0.35rem 1rem;
        margin: 0.75rem 0 0;
      }

      .info-values dt {
        opacity: 0.75;
      }

      .info-values dd {
        margin: 0;
        text-align: right;
      }

      .info-icon {
        width: 1.25rem;
        height: 1.25rem;
        vertical-align: middle;
      }
    </style>
    <script src="https://cdn.theoplayer.com/dash/theoplayer/THEOplayer.js"></script>
    <script nomodule src="https://unpkg.com/@theoplayer/web-ui@2/polyfills"></script>
    <script async src="https://unpkg.com/@theoplayer/web-ui@2"></script>
  </head>
  <body>
    <!--
      libraryLocation: For demonstration purposes, we use the THEOplayer CDN.
      For production use, host the library yourself and update this URL and the script above.

      licenseUrl: Point this to your license file, or replace it with a license property.
    -->
    <theoplayer-default-ui
      id="player-ui"
      configuration='{"libraryLocation":"https://cdn.theoplayer.com/dash/theoplayer/","licenseUrl":"../../../../theoplayer-license.txt"}'
      source='{"sources":{"src":"https://cdn.theoplayer.com/video/big_buck_bunny/big_buck_bunny_metadata.m3u8","type":"application/x-mpegurl"},"poster":"https://cdn.theoplayer.com/video/big_buck_bunny/poster.jpg","metadata":{"title":"Big Buck Bunny"}}'
    >
      <theoplayer-button id="skip-intro" slot="top-control-bar">Skip intro</theoplayer-button>
      <theoplayer-seek-button slot="bottom-control-bar" seek-offset="-10" mobile-hidden></theoplayer-seek-button>
      <theoplayer-seek-button slot="bottom-control-bar" seek-offset="10" mobile-hidden></theoplayer-seek-button>
      <theoplayer-menu-button slot="bottom-control-bar" menu="info-menu" aria-label="Info">
        <svg class="info-icon" viewBox="0 0 24 24" fill="none" aria-hidden="true">
          <circle cx="12" cy="12" r="9" fill="none" stroke="currentColor" stroke-width="2" />
          <path d="M12 11v6m0-10h.01" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
        </svg>
      </theoplayer-menu-button>
      <theoplayer-menu id="info-menu" slot="menu" hidden>
        <span slot="heading">About this video</span>
        <p>Big Buck Bunny is a short animated film by the Blender Foundation.</p>
        <dl class="info-values">
          <dt>Quality</dt>
          <dd id="current-quality">Loading</dd>
          <dt>Duration</dt>
          <dd id="duration">Loading</dd>
        </dl>
      </theoplayer-menu>
    </theoplayer-default-ui>
    <script src="../utils.js"></script>
    <script>
      const ui = document.querySelector('#player-ui');
      const skipButton = document.querySelector('#skip-intro');
      const qualityValue = document.querySelector('#current-quality');
      const durationValue = document.querySelector('#duration');

      function formatDuration(duration) {
        if (!Number.isFinite(duration)) return 'Live';
        const minutes = Math.floor(duration / 60);
        const seconds = Math.floor(duration % 60);
        return `${minutes}:${String(seconds).padStart(2, '0')}`;
      }

      onPlayerReady((player) => {
        function updateInfo() {
          const track = Array.from(player.videoTracks).find((videoTrack) => videoTrack.enabled);
          const quality = track && track.activeQuality;
          qualityValue.textContent = quality ? `${quality.height}p` : '—';
          durationValue.textContent = formatDuration(player.duration);
          skipButton.hidden = player.currentTime > 10;
        }

        skipButton.addEventListener('click', () => {
          player.currentTime = 10;
        });
        player.addEventListener(['durationchange', 'timeupdate'], updateInfo);
        player.videoTracks.addEventListener('addtrack', (event) => {
          event.track.addEventListener('activequalitychanged', updateInfo);
          updateInfo();
        });
        updateInfo();
      });
    </script>
  </body>
</html>
```

## Default UI

The [Default UI](https://optiview.dolby.com/docs/theoplayer/ui/web/default-ui.md) is created with `new THEOplayer.Player()`. Its Info button toggles an overlay inside the player, and two video.js `Button` components inserted after the play toggle seek 10 seconds back and forward, clamped to the content duration.

Code

default-ui.html

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Adding buttons to the UI (Default UI)</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="https://cdn.theoplayer.com/dash/theoplayer/ui.css" />
    <script src="https://cdn.theoplayer.com/dash/theoplayer/THEOplayer.js"></script>
    <style>
      html,
      body {
        margin: 0;
        padding: 0;
      }

      #player {
        position: relative;
        width: 100%;
      }

      .custom-info-button {
        width: 3rem;
      }

      .custom-info-button svg {
        width: 1.25rem;
        height: 1.25rem;
        vertical-align: middle;
      }

      .custom-info-overlay {
        position: absolute;
        z-index: 10;
        right: 1rem;
        bottom: 4.5rem;
        box-sizing: border-box;
        width: min(22rem, calc(100% - 2rem));
        padding: 1rem;
        border-radius: 0.4rem;
        background: rgb(0 0 0 / 90%);
        color: #fff;
        font:
          14px/1.5 Arial,
          sans-serif;
      }

      .custom-info-overlay[hidden] {
        display: none;
      }

      .custom-seek-button {
        width: 3rem;
        cursor: pointer;
      }

      .custom-seek-button svg {
        width: 1.25rem;
        height: 1.25rem;
        vertical-align: middle;
      }

      .custom-info-overlay h2 {
        margin: 0 2rem 0.5rem 0;
        font-size: 1rem;
      }

      .custom-info-overlay p {
        margin: 0;
      }

      .custom-info-close {
        position: absolute;
        top: 0.5rem;
        right: 0.5rem;
        border: 0;
        background: transparent;
        color: inherit;
        font: inherit;
        font-size: 1.25rem;
        cursor: pointer;
      }
    </style>
  </head>
  <body>
    <div id="player" class="video-js theoplayer-skin vjs-16-9"></div>
    <script>
      var playerElement = document.querySelector('#player');
      var player = new THEOplayer.Player(playerElement, {
        libraryLocation: 'https://cdn.theoplayer.com/dash/theoplayer/',
        licenseUrl: '../../../../theoplayer-license.txt',
      });
      player.source = {
        sources: [
          {
            src: 'https://cdn.theoplayer.com/video/big_buck_bunny/big_buck_bunny_metadata.m3u8',
            type: 'application/x-mpegurl',
          },
        ],
        poster: 'https://cdn.theoplayer.com/video/big_buck_bunny/poster.jpg',
        metadata: { title: 'Big Buck Bunny' },
      };

      var videojs = THEOplayer.videojs;
      var overlay = document.createElement('div');
      overlay.className = 'custom-info-overlay';
      overlay.hidden = true;
      overlay.innerHTML =
        '<button class="custom-info-close" type="button" aria-label="Close">×</button>' +
        '<h2>About this video</h2>' +
        '<p>Big Buck Bunny is a short animated film by the Blender Foundation.</p>';

      var InfoButton = videojs.extend(videojs.getComponent('Button'), {
        constructor: function (player, options) {
          videojs.getComponent('Button').call(this, player, options);
          this.controlText('Info');
        },
        buildCSSClass: function () {
          return 'custom-info-button vjs-control vjs-button';
        },
        handleClick: function () {
          overlay.hidden = !overlay.hidden;
        },
      });
      videojs.registerComponent('InfoButton', InfoButton);

      // Seek back/forward 10 seconds, clamped to the content duration.
      function registerSeekButton(name, offset, label, iconPath) {
        var SeekButton = videojs.extend(videojs.getComponent('Button'), {
          constructor: function (player, options) {
            videojs.getComponent('Button').call(this, player, options);
            this.controlText(label);
          },
          buildCSSClass: function () {
            return 'custom-seek-button vjs-control vjs-button';
          },
          handleClick: function () {
            player.currentTime = Math.max(0, Math.min(player.duration || Infinity, player.currentTime + offset));
          },
        });
        videojs.registerComponent(name, SeekButton);
        var button = player.ui.getChild('controlBar').addChild(name, {});
        var icon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
        icon.setAttribute('viewBox', '0 0 24 24');
        icon.setAttribute('fill', 'none');
        icon.setAttribute('aria-hidden', 'true');
        icon.innerHTML = iconPath;
        button.el().insertBefore(icon, button.el().firstChild);
        button.el().setAttribute('aria-label', label);
        return button;
      }

      var seekBack = registerSeekButton(
        'SeekBackButton',
        -10,
        'Seek back 10 seconds',
        '<path d="M12 5V2L7 7l5 5V8c3.3 0 6 2.7 6 6s-2.7 6-6 6-6-2.7-6-6H4c0 4.4 3.6 8 8 8s8-3.6 8-8-3.6-8-8-8z" fill="currentColor"></path>'
      );
      var seekForward = registerSeekButton(
        'SeekForwardButton',
        10,
        'Seek forward 10 seconds',
        '<path d="M12 5V2l5 5-5 5V8c-3.3 0-6 2.7-6 6s2.7 6 6 6 6-2.7 6-6h2c0 4.4-3.6 8-8 8s-8-3.6-8-8 3.6-8 8-8z" fill="currentColor"></path>'
      );
      // Place the seek buttons right after the play toggle in the control bar.
      var controlBar = player.ui.getChild('controlBar');
      controlBar.el().insertBefore(seekForward.el(), controlBar.el().children[1]);
      controlBar.el().insertBefore(seekBack.el(), controlBar.el().children[1]);

      var infoButton = player.ui.getChild('controlBar').addChild('InfoButton', {});
      var icon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
      icon.setAttribute('viewBox', '0 0 24 24');
      icon.setAttribute('fill', 'none');
      icon.setAttribute('aria-hidden', 'true');
      icon.innerHTML =
        '<circle cx="12" cy="12" r="9" fill="none" stroke="currentColor" stroke-width="2"></circle><path d="M12 11v6m0-10h.01" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"></path>';
      infoButton.el().insertBefore(icon, infoButton.el().firstChild);
      infoButton.el().setAttribute('aria-label', 'Info');
      playerElement.appendChild(overlay);
      overlay.querySelector('.custom-info-close').addEventListener('click', function () {
        overlay.hidden = true;
      });
    </script>
  </body>
</html>
```
