CSS :picture-in-picture Pseudo-Class

The CSS :picture-in-picture pseudo-class selects a video element while it is displayed in Picture-in-Picture mode. This allows developers to change the appearance of the original video element when its playback is shown in a separate floating window.

:picture-in-picture Syntax

selector:picture-in-picture {
  property: value;
}

The :picture-in-picture pseudo-class matches a video element while it is the active Picture-in-Picture video.

Basic Example

video:picture-in-picture {
  opacity: 0.5;
}

Reduces the opacity of the original video element while its video is displayed in a Picture-in-Picture window.

Border Example

video:picture-in-picture {
  border: 3px solid #006699;
}

Adds a blue border to the video element in the original document while Picture-in-Picture mode is active.

Class Selector

.tutorial-video:picture-in-picture {
  outline: 2px solid #006699;
}

Matches a video with the tutorial-video class while it is displayed in Picture-in-Picture mode.

No Parentheses

The :picture-in-picture pseudo-class does not accept arguments and is written without parentheses.

How :picture-in-picture Works

Picture-in-Picture mode allows a video to appear in a separate floating window while the user continues interacting with other content. The :picture-in-picture pseudo-class matches the originating video element during this mode.

Normal Video Playback

When a video plays inside its normal position on the web page, it does not match :picture-in-picture.

Picture-in-Picture Playback

When the browser places the video into Picture-in-Picture mode, the originating video element begins matching :picture-in-picture.

Automatic State Updates

The browser updates selector matching automatically when Picture-in-Picture mode begins or ends.

Original Element vs. Floating Window

The selector styles the video element in the original document. It does not directly control the floating window's position, dimensions, or browser-provided controls.

Not a Playback-State Selector

A video can enter Picture-in-Picture mode while playing or paused. The Picture-in-Picture state is separate from the media's playback state.

Video Elements

The :picture-in-picture pseudo-class is primarily used with HTML <video> elements.

HTML Example

<video class="tutorial-video" controls width="300" preload="metadata">
  <source src="video/sample.mp4" type="video/mp4">
  Your browser does not support the video element.
</video>

CSS Example

.tutorial-video {
  border: 3px solid #ccc;
}
.tutorial-video:picture-in-picture {
  border-color: #006699;
  opacity: 0.6;
}

The video receives a blue border and reduced opacity in the original page while Picture-in-Picture mode is active.

Native Video Controls

Some browsers provide a Picture-in-Picture button in their native video controls or media menus. Availability depends on the browser and device.

Audio Elements

Ordinary audio elements do not provide the same video Picture-in-Picture behavior. Use this selector for video elements that can enter Picture-in-Picture mode.

Multiple Videos

When a document contains multiple videos, the selector can identify the video currently displayed in Picture-in-Picture mode.

Entering Picture-in-Picture Mode

A supported video can enter Picture-in-Picture mode through browser controls or the Picture-in-Picture API.

JavaScript Example

const video = document.querySelector("video");
video.requestPictureInPicture().catch((error) => {
  console.log("Picture-in-Picture unavailable:", error);
});

The requestPictureInPicture() method requests that the browser display the video in a floating window. The request can fail if Picture-in-Picture is unsupported, disabled, or not permitted.

User Interaction

Browsers generally require a user gesture, such as clicking a button, before allowing a script to enter Picture-in-Picture mode.

Automatic CSS Matching

video:picture-in-picture {
  opacity: 0.5;
}

When the request succeeds, the video matches the selector without requiring JavaScript to add a class.

Permission and Availability

Picture-in-Picture availability can depend on browser support, document permissions, video readiness, and media settings.

Disabled Picture-in-Picture

<video controls disablepictureinpicture>
  <source src="video/sample.mp4" type="video/mp4">
</video>

The disablepictureinpicture attribute requests that the browser not offer Picture-in-Picture functionality for the video. Browser behavior can vary.

Exiting Picture-in-Picture Mode

Picture-in-Picture mode ends when the user closes the floating window, returns the video to its page, or the document requests an exit.

JavaScript Example

if (document.pictureInPictureElement) {
  document.exitPictureInPicture();
}

The exitPictureInPicture() method requests that the browser close the active Picture-in-Picture window.

CSS After Exiting

video {
  opacity: 1;
}
video:picture-in-picture {
  opacity: 0.5;
}

When Picture-in-Picture mode ends, the video stops matching the pseudo-class and returns to its normal opacity.

User-Controlled Exit

The user can often exit Picture-in-Picture mode directly through controls in the floating window.

No Manual CSS Reset

CSS automatically stops applying the pseudo-class rule when the Picture-in-Picture state ends.

Styling the Video Container

The :picture-in-picture pseudo-class matches the video element itself. The :has() pseudo-class can be used to style a container based on the state of its video.

HTML Example

<div class="video-container">
  <video controls width="300">
    <source src="video/sample.mp4" type="video/mp4">
  </video>
  <p class="pip-message">Video is playing in a separate window.</p>
</div>

CSS Example

.pip-message {
  display: none;
}
.video-container:has(video:picture-in-picture) .pip-message {
  display: block;
  color: #006699;
}

Displays a message in the original page while its video is in Picture-in-Picture mode.

Container Border

.video-container:has(video:picture-in-picture) {
  border: 2px solid #006699;
}

Highlights the video container while its video is displayed in a floating window.

Why Use :has()?

The :has() pseudo-class allows a parent element to be selected based on a matching descendant. Without it, :picture-in-picture directly targets only the video element.

JavaScript and Picture-in-Picture

The Picture-in-Picture API provides methods, properties, and events for managing supported video Picture-in-Picture windows.

Checking API Support

if ("pictureInPictureEnabled" in document && document.pictureInPictureEnabled) {
  console.log("Picture-in-Picture is available");
}

The pictureInPictureEnabled property indicates whether the document is permitted to use the API in browsers that implement it.

Checking the Active Video

const activeVideo = document.pictureInPictureElement;
console.log(activeVideo);

The pictureInPictureElement property identifies the video currently in Picture-in-Picture mode or returns null when no video is active.

Entering Picture-in-Picture

video.addEventListener("enterpictureinpicture", () => {
  console.log("Picture-in-Picture started");
});

The enterpictureinpicture event fires when the video enters Picture-in-Picture mode.

Exiting Picture-in-Picture

video.addEventListener("leavepictureinpicture", () => {
  console.log("Picture-in-Picture ended");
});

The leavepictureinpicture event fires when the video exits Picture-in-Picture mode.

CSS and JavaScript Roles

JavaScript can request Picture-in-Picture mode and respond to events. CSS uses :picture-in-picture to style the originating video while the mode is active.

Related Pseudo-Classes

Picture-in-Picture mode is different from fullscreen display and media playback states.

Comparison

Pseudo-Class Matching Behavior
:picture-in-pictureMatches a video displayed in Picture-in-Picture mode.
:fullscreenMatches an element displayed in fullscreen mode.
:playingMatches supported media elements in a playing state.
:pausedMatches supported media elements in a paused state.
:seekingMatches supported media elements while seeking.
:mutedMatches supported media elements while muted.

Picture-in-Picture vs. Fullscreen

video:picture-in-picture {
  opacity: 0.5;
}
video:fullscreen {
  width: 100%;
  height: 100%;
}

Picture-in-Picture displays video in a separate floating window. Fullscreen mode expands an element into a fullscreen presentation.

Picture-in-Picture vs. Playing

A video can be in Picture-in-Picture mode while playing or paused. The :playing pseudo-class describes playback activity, not whether a floating window is open.

Picture-in-Picture vs. Paused

Pausing a video does not automatically exit Picture-in-Picture mode. These are separate states.

:picture-in-picture Specificity

The :picture-in-picture pseudo-class contributes a specificity of 0-1-0, like other standard pseudo-classes.

Specificity Examples

Selector Specificity
:picture-in-picture0-1-0
video:picture-in-picture0-1-1
.tutorial-video:picture-in-picture0-2-0
.video-container video:picture-in-picture0-2-1
video:playing:picture-in-picture0-2-1
#main-video:picture-in-picture1-1-0

Specificity Example

video {
  opacity: 1;
}
video:picture-in-picture {
  opacity: 0.5;
}

When both rules match and other cascade factors are equal, the :picture-in-picture rule takes precedence because it has greater specificity.

Combined Pseudo-Classes

video:paused:picture-in-picture {
  outline: 2px solid #006699;
}

This selector matches a video that is both paused and displayed in Picture-in-Picture mode.

Practical Uses

The :picture-in-picture pseudo-class can provide visual feedback in the original document when a video is displayed separately.

Dim the Original Video

video:picture-in-picture {
  opacity: 0.5;
}

Reduces the prominence of the original video element while its video is shown in a floating window.

Highlight the Video Area

video:picture-in-picture {
  outline: 3px solid #006699;
}

Provides a visible indicator around the original video area.

Display a Status Message

.video-container:has(video:picture-in-picture) .pip-message {
  display: block;
}

Displays a message explaining that the video is in a separate window.

Change Container Background

.video-container:has(video:picture-in-picture) {
  background-color: #eaf6fc;
}

Highlights the original video container while Picture-in-Picture mode is active.

Show a Return Button

.return-video {
  display: none;
}
.video-container:has(video:picture-in-picture) .return-video {
  display: inline-block;
}

Displays a button that can be connected to JavaScript to exit Picture-in-Picture mode.

Limitations

The :picture-in-picture pseudo-class applies only when a supported video is actually in Picture-in-Picture mode.

Browser Support Varies

Picture-in-Picture availability and API support differ across browsers, operating systems, and devices.

Does Not Open Picture-in-Picture

The pseudo-class only styles the active state. It cannot request or activate Picture-in-Picture mode.

Does Not Style the Floating Window

CSS applied to the originating video element does not directly control the browser-managed floating window or its controls.

Requires an Eligible Video

The video must satisfy the browser's requirements for Picture-in-Picture mode. Availability can depend on media readiness, permissions, and user interaction.

Not Equivalent to Fullscreen

Fullscreen mode and Picture-in-Picture mode use different browser features and pseudo-classes.

Not a Playback-State Selector

A video may remain in Picture-in-Picture mode while paused. Use media-state pseudo-classes when styling must depend on playback activity.

Embedded Documents

Picture-in-Picture requests from an iframe can be restricted by Permissions Policy. Embedded demonstrations may require an appropriate iframe allow attribute.

Best Practices

  • Use :picture-in-picture to style the originating video while Picture-in-Picture mode is active.
  • Combine it with video or a video-specific class to target the intended element.
  • Use :has() when styling a container based on its video's Picture-in-Picture state.
  • Do not assume Picture-in-Picture mode is available on every browser or device.
  • Provide a clear button or native control for entering Picture-in-Picture mode when supported.
  • Handle rejected Picture-in-Picture requests when using JavaScript.
  • Keep playback controls accessible and usable.
  • Do not confuse Picture-in-Picture mode with fullscreen or playback states.
  • Check iframe permissions when using embedded media demonstrations.
  • Test both entering and exiting Picture-in-Picture mode in supported browsers.

:picture-in-picture Example

This example uses a video and a Picture-in-Picture button. When the video enters Picture-in-Picture mode, the original video becomes partially transparent and its container displays a status message. The styling returns to normal when Picture-in-Picture mode ends.

HTML

<div class="pip-demo">
  <video id="demo-video" controls width="280" preload="metadata">
    <source src="https://www.w3schools.com/html/mov_bbb.mp4" type="video/mp4">
    Your browser does not support the video element.
  </video>
  <p class="pip-message">Video is in Picture-in-Picture mode.</p>
  <button type="button" id="pip-button">Enter Picture-in-Picture</button>
  <p id="pip-status" role="status" aria-live="polite"></p>
</div>

CSS

.pip-demo video {
  display: block;
  max-width: 100%;
  box-sizing: border-box;
  border: 3px solid #ccc;
}
.pip-demo video:picture-in-picture {
  opacity: 0.5;
  border-color: #006699;
}
.pip-demo .pip-message {
  display: none;
  color: #006699;
}
.pip-demo:has(video:picture-in-picture) .pip-message {
  display: block;
}
.pip-demo button {
  padding: 8px 12px;
  margin-top: 10px;
  cursor: pointer;
}
.pip-demo #pip-status {
  font-size: 0.875rem;
}

JavaScript

const video = document.getElementById("demo-video");
const button = document.getElementById("pip-button");
const status = document.getElementById("pip-status");
if (!("pictureInPictureEnabled" in document) || !document.pictureInPictureEnabled || !("requestPictureInPicture" in video)) {
  button.disabled = true;
  status.textContent = "Picture-in-Picture is unavailable.";
}
button.addEventListener("click", async () => {
  try {
    if (document.pictureInPictureElement === video) {
      await document.exitPictureInPicture();
    } else {
      await video.requestPictureInPicture();
    }
  } catch (error) {
    status.textContent = "Picture-in-Picture could not be activated.";
  }
});
video.addEventListener("enterpictureinpicture", () => {
  button.textContent = "Exit Picture-in-Picture";
  status.textContent = "";
});
video.addEventListener("leavepictureinpicture", () => {
  button.textContent = "Enter Picture-in-Picture";
  status.textContent = "";
});

Browser Result

Play in Editor

Note: Click Enter Picture-in-Picture to request a floating video window. The video must be loaded and the browser must support the feature. If the embedded example is restricted, open it using Play in Editor. The floating window itself is controlled by the browser.

Browser Support

Baseline: Limited availability. The :picture-in-picture pseudo-class and related Picture-in-Picture functionality are not consistently supported across all major browsers and devices. Browser-specific behavior and API availability can vary.

For current compatibility information, visit Can I Use? and search for :picture-in-picture.

Compatibility Alternative

For browsers that support Picture-in-Picture events but not the CSS pseudo-class, JavaScript can add or remove a class such as .is-picture-in-picture when the video enters or exits the mode.

Specifications

The :picture-in-picture pseudo-class is defined in the Picture-in-Picture specification. It matches a video element while the video is displayed in Picture-in-Picture mode.

Picture-in-Picture Specification - CSS Pseudo-Class