diff --git a/site/src/components/docs/demos/controls/html/css/BasicUsage.astro b/site/src/components/docs/demos/controls/html/css/BasicUsage.astro
new file mode 100644
index 00000000..e7e95f49
--- /dev/null
+++ b/site/src/components/docs/demos/controls/html/css/BasicUsage.astro
@@ -0,0 +1,10 @@
+---
+import HtmlDemo from '@/components/docs/demos/HtmlDemo.astro';
+import html from './BasicUsage.html?raw';
+import './BasicUsage.css';
+---
+
+
+
diff --git a/site/src/components/docs/demos/controls/html/css/BasicUsage.css b/site/src/components/docs/demos/controls/html/css/BasicUsage.css
new file mode 100644
index 00000000..05944d72
--- /dev/null
+++ b/site/src/components/docs/demos/controls/html/css/BasicUsage.css
@@ -0,0 +1,73 @@
+.controls-basic {
+ display: block;
+ position: relative;
+}
+
+.controls-basic video {
+ width: 100%;
+}
+
+.controls-basic__root {
+ position: absolute;
+ inset: 0;
+ display: flex;
+ align-items: flex-end;
+ padding: 12px;
+ pointer-events: none;
+ background: linear-gradient(to top, rgba(0, 0, 0, 0.45), transparent 45%);
+ transition: opacity 0.25s;
+}
+
+.controls-basic__root:not([data-visible]) {
+ opacity: 0;
+}
+
+.controls-basic__bottom {
+ pointer-events: auto;
+}
+
+.controls-basic__time {
+ display: inline-flex;
+ align-items: center;
+ gap: 4px;
+ padding-block: 8px;
+ padding-inline: 16px;
+ border-radius: 9999px;
+ background: rgba(255, 255, 255, 0.75);
+ backdrop-filter: blur(10px);
+ color: black;
+ border: 1px solid rgba(255, 255, 255, 0.25);
+ font-size: 14px;
+}
+
+.controls-basic__bottom {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+}
+
+.controls-basic__button {
+ padding-block: 8px;
+ background: rgba(255, 255, 255, 0.75);
+ backdrop-filter: blur(10px);
+ color: black;
+ border: 1px solid rgba(255, 255, 255, 0.25);
+ border-radius: 9999px;
+ padding-inline: 16px;
+ font-size: 14px;
+ cursor: pointer;
+}
+
+.controls-basic__button .show-when-paused,
+.controls-basic__button .show-when-playing {
+ display: none;
+}
+
+.controls-basic__play[data-paused] .show-when-paused {
+ display: inline;
+}
+
+.controls-basic__play:not([data-paused]) .show-when-playing {
+ display: inline;
+}
diff --git a/site/src/components/docs/demos/controls/html/css/BasicUsage.html b/site/src/components/docs/demos/controls/html/css/BasicUsage.html
new file mode 100644
index 00000000..f434bed5
--- /dev/null
+++ b/site/src/components/docs/demos/controls/html/css/BasicUsage.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ Play
+ Pause
+
+
+
+
+
diff --git a/site/src/components/docs/demos/controls/html/css/BasicUsage.ts b/site/src/components/docs/demos/controls/html/css/BasicUsage.ts
new file mode 100644
index 00000000..f1eee5ec
--- /dev/null
+++ b/site/src/components/docs/demos/controls/html/css/BasicUsage.ts
@@ -0,0 +1,4 @@
+import '@videojs/html/video/player';
+import '@videojs/html/ui/controls';
+import '@videojs/html/ui/play-button';
+import '@videojs/html/ui/time';
diff --git a/site/src/components/docs/demos/controls/react/css/BasicUsage.css b/site/src/components/docs/demos/controls/react/css/BasicUsage.css
new file mode 100644
index 00000000..9f99bc9e
--- /dev/null
+++ b/site/src/components/docs/demos/controls/react/css/BasicUsage.css
@@ -0,0 +1,59 @@
+.controls-basic {
+ position: relative;
+}
+
+.controls-basic video {
+ width: 100%;
+}
+
+.controls-basic__root {
+ position: absolute;
+ inset: 0;
+ display: flex;
+ align-items: flex-end;
+ padding: 12px;
+ pointer-events: none;
+ background: linear-gradient(to top, rgba(0, 0, 0, 0.45), transparent 45%);
+ transition: opacity 0.25s;
+}
+
+.controls-basic__root:not([data-visible]) {
+ opacity: 0;
+}
+
+.controls-basic__bottom {
+ pointer-events: auto;
+}
+
+.controls-basic__time {
+ display: inline-flex;
+ align-items: center;
+ gap: 4px;
+ padding-block: 8px;
+ padding-inline: 16px;
+ border-radius: 9999px;
+ background: rgba(255, 255, 255, 0.75);
+ backdrop-filter: blur(10px);
+ color: black;
+ border: 1px solid rgba(255, 255, 255, 0.25);
+ font-size: 14px;
+}
+
+.controls-basic__bottom {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+}
+
+.controls-basic__button {
+ padding-block: 8px;
+ background: rgba(255, 255, 255, 0.75);
+ backdrop-filter: blur(10px);
+ color: black;
+ border: 1px solid rgba(255, 255, 255, 0.25);
+ border-radius: 9999px;
+ padding-inline: 16px;
+ font-size: 14px;
+ cursor: pointer;
+}
diff --git a/site/src/components/docs/demos/controls/react/css/BasicUsage.tsx b/site/src/components/docs/demos/controls/react/css/BasicUsage.tsx
new file mode 100644
index 00000000..b98e65e3
--- /dev/null
+++ b/site/src/components/docs/demos/controls/react/css/BasicUsage.tsx
@@ -0,0 +1,32 @@
+import { Controls, createPlayer, features, PlayButton, Time, Video } from '@videojs/react';
+
+import './BasicUsage.css';
+
+const Player = createPlayer({ features: [...features.video] });
+
+export default function BasicUsage() {
+ return (
+
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+
+ );
+}
diff --git a/site/src/content/docs/reference/controls.mdx b/site/src/content/docs/reference/controls.mdx
new file mode 100644
index 00000000..32d7c827
--- /dev/null
+++ b/site/src/content/docs/reference/controls.mdx
@@ -0,0 +1,75 @@
+---
+title: Controls
+frameworkTitle:
+ html: media-controls
+description: Containers for composing and auto-hiding player controls
+---
+
+import ApiReference from "@/components/docs/api-reference/ApiReference.astro";
+import FrameworkCase from "@/components/docs/FrameworkCase.astro";
+import StyleCase from "@/components/docs/StyleCase.astro";
+import Demo from "@/components/docs/demos/Demo.astro";
+
+{/* React demos */}
+import BasicUsageDemoReact from "@/components/docs/demos/controls/react/css/BasicUsage";
+import basicUsageReactTsx from "@/components/docs/demos/controls/react/css/BasicUsage.tsx?raw";
+import basicUsageReactCss from "@/components/docs/demos/controls/react/css/BasicUsage.css?raw";
+
+{/* HTML demos */}
+import BasicUsageDemoHtml from "@/components/docs/demos/controls/html/css/BasicUsage.astro";
+import basicUsageHtml from "@/components/docs/demos/controls/html/css/BasicUsage.html?raw";
+import basicUsageHtmlCss from "@/components/docs/demos/controls/html/css/BasicUsage.css?raw";
+import basicUsageHtmlTs from "@/components/docs/demos/controls/html/css/BasicUsage.ts?raw";
+
+## Anatomy
+
+Import the component and assemble its parts:
+
+
+ ```tsx
+
+
+
+ ```
+
+
+
+ ```html
+
+
+
+ ```
+
+
+## Examples
+
+### Basic Usage
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/src/docs.config.ts b/site/src/docs.config.ts
index cce9f891..d5982882 100644
--- a/site/src/docs.config.ts
+++ b/site/src/docs.config.ts
@@ -24,9 +24,11 @@ export const sidebar: Sidebar = [
{
sidebarLabel: 'Components',
contents: [
- { slug: 'reference/play-button' },
- { slug: 'reference/mute-button' },
+ // sorted alphabetically
+ { slug: 'reference/controls' },
{ slug: 'reference/fullscreen-button' },
+ { slug: 'reference/mute-button' },
+ { slug: 'reference/play-button' },
{ slug: 'reference/time' },
],
},