Web app themes and custom CSS
Change the web app's colours, restyle it with your own CSS, and the selectors to style its parts by.
The web app's Theme settings, under Settings → Theme, change its look for you on every device you sign in on. Each user of a server has their own.
- Presets set an accent and a background colour together.
- Accent colours buttons, progress bars and highlights.
- Background colours pages and panels, which take their shades from it. Dark colours read best.
- Custom CSS goes on top of the theme, for anything the colours don't cover.
Adding custom CSS
Write your CSS in the Custom CSS box and press Apply CSS. A message then asks whether to keep it: press Keep, or it is undone after 15 seconds. That way CSS that hides the page, or the button, can't lock you out. Your text stays in the box either way, so you can fix it and try again.
Once kept, it is saved to your account and applies on every device you sign in on. It doesn't apply on the sign-in screens, since nobody is signed in there yet.
If your CSS ever breaks the app on some device, add ?safe to the address
(for example https://example.com/web/?safe). Custom CSS stays off for that
visit, so you can fix or clear it in the settings.
A server's own CSS
A server's owner can style the app for everyone signed in to it, with App custom CSS in the dashboard's Branding settings, or APP_CUSTOM_CSS. It applies on the sign-in screens too, and before everyone's own custom CSS, so yours still wins. To leave it out for yourself, turn off Use this server's CSS under Settings → Theme. ?safe turns it off for that visit as well.
Finding what to style
Parts of the app carry a data-ui attribute naming what they are, and other data-* attributes saying which one or what state it's in. Aim your CSS at these rather than at class names, which change between versions.
To find a part's name, right-click it in a browser and choose Inspect, then look at the data-ui on it or on the elements around it. The reference below lists them all.
/* Every progress bar */
[data-ui='progress-bar-fill'] {
background: hotpink;
}
/* Only those in the continue watching row */
[data-row='resume'] [data-ui='progress-bar-fill'] {
background: hotpink;
}Examples
True black
The Background setting does this best, since panels take their shades from it. With CSS:
:root {
--background: #000;
--paper: #000;
--color-gray-950: 0 0 0;
--color-gray-900: 10 10 10;
}Squarer corners
[data-ui='poster-card-image'],
[data-ui='wide-card-image'],
[data-ui='episode-image'] {
border-radius: 0.25rem;
}Dim what you've watched
:is([data-ui='poster-card'], [data-ui='wide-card'])[data-watched]
[data-ui='artwork'] {
opacity: 0.4;
}No zoom on hover
[data-ui='poster-card']:is(:hover, :focus-visible) [data-ui='artwork'],
[data-ui='wide-card']:hover [data-ui='artwork'],
[data-ui='wide-card'] :focus-visible [data-ui='artwork'] {
transform: none;
}Bigger posters in rows
[data-ui='media-row'][data-shape='poster'] [data-ui='media-row-item'] {
flex-basis: 14rem;
}Hide a row on the home page
[data-row='upcoming'] {
display: none;
}A catalog's row is named view: and then its id. To find the id, open the catalog by clicking the row's title: the id is the part of the address after /discover/.
[data-row='view:abc123'] {
display: none;
}To hide the featured banner, set Featured on home to Nothing under Settings → Interface instead.
Hide what a catalog holds
A catalog's row on the home page says what it holds, such as Shows or Movies, beside its title.
[data-ui='media-row'][data-row^='view:'] [data-ui='media-row-title'] a > span {
display: none;
}Mark your favourites
[data-ui='poster-card'][data-favourite] [data-ui='poster-card-image']::after {
content: '♥';
position: absolute;
top: 0.5rem;
left: 0.5rem;
z-index: 2;
color: #f43f5e;
font-size: 1.25rem;
text-shadow: 0 1px 4px rgb(0 0 0 / 0.6);
}Hide spoilers
This needs no CSS: turn on Blur episodes you haven't started under Settings → Interface → Spoilers. It covers title pages, the player's episode list and the continue watching, next up and upcoming rows.
Hide buttons on title pages
[data-ui='item-action']:is([data-name='trailer'], [data-name='drop']) {
display: none;
}More buttons beside Play on a phone
On a phone, a title's page shows three actions beside More, which lists every action. They are the
first three the title offers of trailer, watched, favourite, download, rating,
add-to-playlist, random, party, play-on and drop, and carry data-shown. To bring another
one out, name it:
[data-ui='item-quick-action'][data-name='party'] {
display: flex;
}
[data-ui='item-more-action'][data-name='party'] {
display: none;
}Pages in the bottom bar
The bar shows Home, Discover, Search and Favourites and lists the other pages under You. Every page is
in both, with data-shown on the ones the bar shows. To swap Favourites for Live TV:
[data-ui='mobile-nav-item'][data-name='live'] {
display: flex;
}
[data-ui='mobile-nav-item'][data-name='favourites'],
[data-ui='account-menu-page'][data-name='live'] {
display: none;
}
[data-ui='account-menu-page'][data-name='favourites'] {
display: flex;
}The pages are home, discover, search, favourites, live, calendar, activity, playlists,
party and downloads. Live TV shows only on a server with live channels.
Smaller icons in the bottom bar
Without names, the bar's icons are a little bigger. For the smaller ones, spaced further apart:
[data-ui='mobile-nav'][data-names='hidden'] [data-ui='mobile-nav-item'] {
width: 3.5rem;
padding-block: 0.875rem;
}
[data-ui='mobile-nav'][data-names='hidden'] [data-ui='mobile-nav-item'] svg {
font-size: 1.25rem;
}No parallax
A title's backdrop and the rotating hero scroll away at half the page's speed. To keep them with the page:
:is([data-ui='item-backdrop'], [data-ui='hero-backdrops']) {
animation: none;
}Hide the score, and the dot before it
[data-ui='item-meta'] [data-name='score'],
[data-ui='meta-separator']:has(+ [data-name='score']) {
display: none;
}Episodes without their air dates
[data-ui='episode-meta'] [data-name='aired'],
[data-ui='episode-meta'] [data-name='aired'] + [data-ui='meta-separator'] {
display: none;
}Seasons without the bar
Mark the open season by its brighter poster and bolder name alone:
[data-ui='season-indicator'] {
display: none;
}
[data-ui='season-poster'][data-selected] [data-ui='season-name'] {
font-weight: 600;
}Only some ratings
Keep IMDb and the Rotten Tomatoes critics' score, wherever a title's ratings show:
[data-ui='score']:not([data-source='imdb'], [data-source='tomatoes']) {
display: none;
}Square cast photos
[data-ui='person-card-image'] {
border-radius: 0.75rem;
}Versions
/* Mark cached versions */
[data-ui='version'][data-cached] {
border-color: rgb(var(--color-brand-500) / 0.6);
background: rgb(var(--color-brand-500) / 0.08);
}
/* Ring the top version, since the list is sorted best first */
[data-ui='version']:nth-child(1 of [data-ui='version']) {
box-shadow: 0 0 0 1px rgb(var(--color-brand-400));
}
/* Names in a fixed-width font, for formats laid out in columns */
[data-ui='version-name'],
[data-ui='version-description'] {
font-family: ui-monospace, monospace;
}
/* A wider list on big screens */
[data-ui='dialog'][data-name='versions'] {
max-width: 72rem;
}
/* Hide quick filters that find nothing for this title */
[data-ui='versions-quick-filter'][data-empty] {
display: none;
}
/* Bigger badges */
[data-ui='version-badge'] {
height: 1.75rem;
font-size: 0.8125rem;
}Leave choosing which versions appear to your filters, rather than hiding some with CSS. Hidden versions still count, and playing straight away or carrying on to the next episode can still pick them.
The player
/* A red timeline, thicker on hover */
[data-ui='seek-bar-progress'] {
background: #e50914;
}
[data-ui='seek-bar-track'] {
height: 6px;
}
[data-ui='seek-bar']:hover [data-ui='seek-bar-track'] {
height: 10px;
}
/* Drop buttons you never use */
[data-ui='player-button']:is([data-name='speed'], [data-name='fit']) {
display: none;
}
/* Bigger track lists, with the chosen one in the accent colour */
[data-ui='player-menu-item'] {
font-size: 1.1rem;
}
[data-ui='player-menu-item'][data-selected] {
color: rgb(var(--color-brand-300));
}To change how subtitles look in a browser, style video::cue. The desktop and Android apps draw subtitles themselves, so use Settings → Subtitles there.
video::cue {
font-family: 'Trebuchet MS', sans-serif;
}Your own font
@import has to come first in the box.
@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@400;600;700&display=swap');
body {
font-family: 'Outfit', sans-serif;
}Your own logo
[data-ui='brand-logo'] {
content: url('https://example.com/logo.png');
}Hide the scrollbar
html {
scrollbar-width: none;
}
::-webkit-scrollbar {
display: none;
}Messages in the bottom right
On a phone they stay at the top, where they span the screen.
@media (min-width: 640px) {
[data-sonner-toaster] {
top: auto !important;
bottom: 24px !important;
left: auto !important;
right: 24px !important;
transform: none !important;
}
/* Each message sits at the bottom, with older ones stacked above */
[data-sonner-toast] {
top: auto !important;
bottom: 0 !important;
--lift: -1 !important;
--lift-amount: calc(-1 * var(--gap)) !important;
}
}No fade-ins
[data-page],
[data-ui='media-row-item'] > div,
[data-ui='card-grid'] > div,
[data-ui='episode-list'] > div > div {
opacity: 1 !important;
transform: none !important;
top: 0 !important;
}Window buttons in a pill of their own
With the navigation along the top, the desktop app puts its window buttons at the end of the last pill. To give them a pill of their own:
[data-ui='top-tabs'][data-style='pill'] [data-ui='top-tabs-end'] {
gap: 1.5rem;
padding: 0;
border: none;
background: none;
box-shadow: none;
backdrop-filter: none !important;
}
[data-ui='top-tabs'][data-style='pill'] [data-ui='top-tabs-divider'] {
display: none;
}
[data-ui='top-tabs'][data-style='pill']
:is([data-ui='top-tabs-actions'], [data-ui='top-window-buttons']) {
padding: 0.375rem;
border: 1px solid rgb(255 255 255 / 0.1);
border-radius: 9999px;
background-color: rgb(14 14 18 / 0.55);
backdrop-filter: blur(24px) saturate(1.8);
box-shadow: 0 12px 32px -12px rgb(0 0 0 / 0.7);
}Only in one app
/* A bigger timeline in the desktop app only */
html[data-host='desktop'] [data-ui='seek-bar-track'] {
height: 8px;
}
/* No suggestions under the search box in the Android app on a TV */
html[data-host='android-app'][data-tv] [data-ui='search-suggestions'] {
display: none;
}When a rule doesn't take
Your CSS comes after the app's own, so a rule as specific as the app's wins. When one doesn't:
- Hover and focus styles need the same state in your selector. The zoom on a hovered poster, for example, is only undone by
[data-ui='poster-card']:hover [data-ui='artwork']. - Animations set their styles on the element itself, which only
!importantbeats. Fade-ins and the message popups are like this. - Class names change between versions, so a rule aimed at one can stop working after an update. The
data-uinames and the shared component classes are kept stable.
What CSS can't change
- Subtitles in the desktop and Android apps, which the player draws. Use Settings → Subtitles.
- The page while a video plays in the desktop app. It has to stay see-through for the video beneath it, so custom CSS can't give it a background then.
- The mini player's video in the desktop and Android apps, which the app draws itself where the page places the card. Moving or resizing
mini-playerwith CSS leaves the video where it was. - The sign-in screens, before anyone is signed in.
Selectors
States are attributes set only while true, such as data-watched. Match them with [data-watched], or :not([data-watched]) for the opposite.
The whole page
| Selector | What it is |
|---|---|
html[data-host] | Which app you're in: browser, desktop (AIOStreams' desktop app), android-app (AIOStreams' Android app), webos or tizen (the TV apps), jellyfin-desktop, android or jellyfin-app (another Jellyfin app that loads the web client) |
html[data-platform] | In the desktop app, the system: windows, linux or macos |
html[data-tv] | Set on a TV: in the LG and Samsung apps, and in the Android app on Android TV |
html[data-nav] | The navigation on a wide screen: sidebar, on a touch screen rail or bar, and on a TV tv-rail or tv-top. A narrow screen always has the bar at the bottom |
html[data-in-rows] | Set while focus is in the rows under a banner that follows the selected card |
html[data-pointer-focus] | Set while focus came from a mouse or a touch, not from keys, a remote or a gamepad |
html.playing | Set while the player fills the window, not in the mini player |
html[data-blur-spoilers] | Set while Blur episodes you haven't started is on |
[data-page] | Each page's container: home, discover, library (a catalog), search, favourites, live, calendar, history, downloads, settings, item, person, player or sign-in. A trailer's player is player with data-name="trailer" |
[data-page='sign-in'] | The screens before you're signed in; data-name is sign-in, quick-connect, user-picker, servers or unreachable |
page-background | The colour behind every page |
shell | The pages and the navigation, kept under the player while a video plays |
page-body, page-header, page-title | A page's padded column, its heading row and its title |
page-message | A message that fills the page, such as when there are no catalogs. The message itself is [data-luffy-error] (see shared components) |
section, section-title | A titled part of a page; data-name is episodes, collection, details or filmography |
sidebar | The sidebar. Its items are .UI-VerticalMenu__item, with data-name (home, discover, search, favourites, live, calendar, activity, downloads, party, settings, account) and data-current="true" on the page you're on |
sidebar-logo, brand-logo | The logo in the sidebar, and the logo itself (also on the sign-in screens) |
sidebar-history | The back and forward buttons in the sidebar, where the browser has none; data-name is back or forward |
mobile-nav, mobile-nav-item | The bar at the bottom on a phone, and on a touch screen set to it; data-names is shown or hidden. Every page is an item, with data-name as in the sidebar, data-shown on those the bar shows and aria-current on the page you're on |
touch-rail, rail-item | The rail down the side on a touch screen, and its items; data-shape="floating" when it floats over the page |
tv-rail | A TV's navigation down the side; data-shape="floating" when it floats over the page |
top-tabs, top-history | The navigation along the top, on a TV or a wide window set to it; data-style is pill, bar or fade. top-history holds its back and forward buttons, where no browser has them |
top-tabs-end, top-tabs-actions, top-tabs-divider, top-window-buttons | Its group at the end (the end pill in the pill style); the buttons in it; and, in the desktop app, the line and the window buttons after them |
corner-menu | The navigation shrunk to a button in the top corner; data-open while it's open |
nav-item | An item in tv-rail, top-tabs and corner-menu; data-name as in the sidebar, and aria-current on the page you're on |
download-ring | The ring around your picture while downloads run |
account-menu | The menu under your picture; its items have data-name: settings, switch-user, configure, change-server, sign-out. From the bottom bar it also lists every page as account-menu-page, data-shown on those the bar shows |
connection-note, connection-note-downloads, connection-note-dismiss | The note while the server can't be reached, its Downloads button and its close button; data-state="small" once it's shrunk to an icon |
update-toast | The message that an app update is ready. On a TV it's a dialog instead |
window-controls | The window buttons in the desktop app; each has data-name: minimise, maximise, fullscreen or close, and data-fullscreen is set while the window is full screen |
pill-tabs, pill-tab | A row of choices such as Movies / Shows; data-name says which row, data-selected marks the choice |
dialog | A dialog box; data-name is versions, overview, all-episodes, episodes and chapters (the player's), calendar-day, unaired (asking before playing or downloading an episode that hasn't aired), sync-to-line, external-player, trailers, update, party, send-report, crash-report, or quick-filters and badges (their settings editors) |
avatar | A user's picture or initial |
Rows, grids and cards
| Selector | What it is |
|---|---|
home-rows | Everything under the featured banner on the home page |
media-row | A scrolling row. data-row names it: resume, next-up, upcoming, now-playing, similar, cast, crew, seasons, episodes:<season id>, sub-collections, search:movies, search:shows, search:people, or view:<id> for a catalog. data-shape is poster, wide or square |
media-row-title, media-row-action, media-row-nav, media-row-item | A row's title, the button beside it (such as Activity), its arrows, and each card's slot |
card-grid | A grid of cards; data-shape is poster or wide, data-size the poster size setting (small, medium, large) |
poster-card, poster-card-image, poster-card-shade, poster-card-animated, poster-card-text, poster-card-title, poster-card-subtitle | A poster, its picture, the shade over it on hover, the moving poster played while it's selected, and the lines under it. data-shape is poster, landscape or square, and data-type says what it is, such as Movie, Series or BoxSet. States: data-watched, data-favourite, data-in-progress |
wide-card, wide-card-image, wide-card-play, wide-card-badge, wide-card-text, wide-card-title, wide-card-subtitle, wide-card-meta | A landscape card, as in continue watching, with its play icon and corner badge. States: data-watched, data-in-progress, data-highlighted, data-dimmed, data-unavailable |
artwork, artwork-fallback | A card's picture, and the title shown when it has none |
progress-bar, progress-bar-fill | How far through something you are |
watched-badge, unwatched-count | The tick on something watched, and the count of episodes left on a show |
focus-ring, opened-pulse | The ring on a card the keyboard, a remote or a gamepad is on, and the brief one on the episode a page opened on |
item-menu | The menu a card opens on right-click or a long press; its items have data-name: play, play-now, choose-version, details, open, watched, watched-up-to, remove-resume, favourite, download, downloads |
card-preview | The bigger preview a card on the home page grows into while the pointer or focus rests on it; data-type says what it is. Hide it with display: none, or turn it off under Settings → Interface → Cards |
card-preview-logo, card-preview-title, card-preview-episode, card-preview-genres, card-preview-overview | Its logo or title, the episode, the genres and the description |
score | One rating in a line of facts, on title pages, the featured banner and card previews. data-source is imdb, tomatoes, tomatoesAudience, metacritic, letterboxd, rogerebert, trakt, tmdb, mal, or community for the server's own rating |
card-preview-meta | Its line of facts; each has data-name (year, or aired for an episode, score, length, rating), with a meta-separator between |
open-card, open-card-poster | A card opened in its row in place of a preview, and the poster fading off its start as it opens. Its slide (media-row-item) has data-open, data-opening while it opens and data-trailer while a trailer plays in it |
trailer-preview-video, trailer-preview-embed | A trailer playing by itself in a card, the banner or a title page, where the device has no player of its own beneath the page |
The featured banner
| Selector | What it is |
|---|---|
hero | The banner; data-follow when it follows the selected card, data-loading while it loads |
hero-backdrop, backdrop-frame, hero-backdrops | The picture, the box holding it, and the layer of them all in the rotating hero, which scrolls at half speed |
hero-shade | The shading over the picture; data-name is side or bottom |
hero-content, hero-logo, hero-title, hero-overview | The text part, the logo or title, and the description |
hero-meta | The line of facts; each has data-name (year or episode, score, genres), with a meta-separator between |
hero-actions, hero-action | The buttons; data-name is play or info |
hero-dots, hero-dot | The dots for each title; data-current marks the one showing |
hero-rows | The rows scrolling under the banner when it follows the selected card |
hero-corner | The picture in the top corner, when the banner keeps to the corner |
hero-trailer | The box a title's trailer plays in, at the banner's end |
Title pages
| Selector | What it is |
|---|---|
item-page | The page; data-type is Movie, Series or BoxSet. States: data-watched, data-favourite |
item-backdrop, item-backdrop-image, item-backdrop-shade | The backdrop, its picture (data-wash when a blurred poster stands in) and its shading (data-name is side or bottom) |
item-trailer, item-trailer-hint | The title's trailer playing behind the whole page, with its shading, and the line over it naming it (data-name playing) and how to open the player (data-name controls) |
item-body, item-header | Everything over the backdrop, and the top part |
item-poster, item-logo, item-title, item-tagline, item-next-airing, item-genres, item-genre, item-details, item-detail | The parts of the header, and the details list; each detail has data-name (studios, released, airs, country) |
item-meta | The line of facts; each has data-name (year, age-rating, runtime, score, status), with a meta-separator between |
item-actions, item-action | The buttons; data-name is play, restart, trailer, random, party, play-on, add-to-playlist, watched, favourite, drop, rating, download (a show's) or more, and data-active marks watched, favourited, dropped or rated |
item-quick-actions, item-quick-action, item-more-action | On a phone, the row under Play and each labelled action in it, by the same data-name (data-shown on the three it shows), and the actions in More's list |
download-button, download-menu | A movie's or an episode's download button, and a show's download menu. data-state on the button is finding, needs-version, queued, downloading, paused, failed, done or sent once there is a download |
rating-picker, inline-rating | The box to rate a title, and a season's rating beside its summary; data-active once rated |
item-actions-divider | The line before the links to other sites |
overview, overview-more, overview-popover, overview-banner, overview-text | A description, its More link and the full text it opens |
external-links, external-link | The links to other sites, and each link; data-name is the site, such as imdb, tmdb, thetvdb, trakt, anidb or myanimelist |
season-poster, season-name, season-indicator, season-pills, season-pill | A season to pick, its name and the bar under the open one's, and the row of season buttons; data-selected marks the open one |
season-summary, season-overview | The season's year and episode count, and its description |
episode-card, episode-list-item, episode-list | An episode in a row or a list. States: data-watched, data-in-progress, data-highlighted (the one you came to), data-unaired (not aired yet, whether or not it can play), and data-unavailable (unaired or missing, for one that can't) |
episode-image, episode-play, episode-number, episode-status, episode-head, episode-title, episode-synopsis | The parts of an episode |
episode-meta | The line of facts under its title; each has data-name (aired, length, rating), with a meta-separator between |
episode-actions, episode-action | The button beside an episode's title that opens its menu (data-name is menu), with its download-button once it has a download. On a TV, an episode that can't play yet has details instead |
person-card, person-card-image, person-card-name, person-card-role | Someone in the cast or crew |
person-header, person-photo, person-dates | A person's page |
Versions
| Selector | What it is |
|---|---|
dialog[data-name='versions'] | The version list's dialog box |
versions-banner, versions-header, versions-count, versions-list | The parts of the list |
versions-action | The buttons at the top; data-name is details or search-again |
versions-start, versions-filter, versions-download-hint | The choice between resuming and starting over, the box to filter versions, and the note when you pick one to download |
version | One version; data-playing marks the one playing, data-cached a cached one |
version-play, version-play-icon | The part you press to play it, and its play icon |
version-name, version-description, version-playing | Its title, the lines under it, and the Playing label |
version-action, version-menu | Its buttons, and the menu that holds them on a phone; data-name is copy-link or download |
version-notice | A message instead of a version; data-type says what kind |
versions-quick, versions-quick-filter | The row of quick filters, and one of them; aria-selected marks the one picked, data-count holds how many versions it finds, and data-empty marks one that finds none |
version-badges, version-badge | A version's badges, and one of them |
Player
| Selector | What it is |
|---|---|
video | The video, in a browser |
player-controls | Everything over the video. States: data-visible while the controls show, data-paused, data-waiting while it loads |
player-top-bar, player-title, player-bottom-bar, player-middle, player-time, player-clock | The bars and the middle buttons, and the time of day in the top bar. The title's lines have data-name: name, and detail under it for an episode or a trailer |
player-top-end, player-window-buttons | The group at the end of the top bar, and in it, in the desktop app, the window buttons, which stand in for window-controls unless the player is full screen or a dialog is open; each has data-name as there |
player-button | A button in the bars; data-name is exit, mini-player, lock, play, back, forward, previous, next, mute, episodes, subtitles, chapters, versions, audio, speed, fit, statistics, party or fullscreen |
player-middle-button | A button in the middle, on phones and touch screens; data-name as above |
player-menu, player-menu-item | The list a button opens; data-name says which (subtitles, audio, speed, statistics), and data-selected marks the chosen item. An audio or subtitle track shows its language and kind, and its own title, often a release name, is the line with data-name="detail" |
player-menu-action, player-menu-pages | An item beside a list's choices (data-name is add-file, more for the languages not in your settings, or the page it opens), and the rows of pages above the list |
player-menu-page | A page a list opens into; data-name is timing or style for the subtitles, sound for the audio. Its back button is player-menu-action with data-name="back" |
player-sync-status | How the subtitles were synced, on their timing page; data-status is hand, synced, retimed, unmatched or speed |
volume | The volume button and slider |
volume-bar, volume-track, volume-level, volume-thumb, volume-value | The slider, its filled part, its handle and the level as a number |
volume-boost, volume-boost-level | Past 100%, where the desktop app can make it louder, and the red over the filled part that deepens the louder it gets |
seek-bar, seek-bar-track, seek-bar-buffered, seek-bar-progress, seek-bar-thumb, seek-bar-tooltip | The timeline |
seek-bar-thumbnail | In the apps, the picture over the timeline of the point you're on |
seek-bar-segment, skip-segment | An intro, recap or credits mark, and the button to skip it; data-type is Intro, Recap, Outro, Preview or Commercial |
seek-bar-chapter | A tick where a chapter starts, in the desktop app |
play-flash, seek-flash, player-notice | What pops up on play, pause, skipping and subtitle changes |
player-scrub, player-held-speed | On a touch screen, the time while you swipe to seek, and the speed while you hold for faster playback |
player-lock, player-unlock | The locked screen on a touch screen, and its button to unlock |
next-episode-card, next-episode-image, next-episode-action, next-episode-countdown | The next episode's card near the end; its buttons have data-name play or hide |
still-watching, still-watching-image, still-watching-action | The check that you're still watching; its buttons have data-name continue or back |
player-episodes-header, player-episodes-seasons, player-episodes-list, player-episode | The player's episode list, dialog[data-name='episodes']; data-current marks the one playing, and each has the states of episode-card and holds an episode-image and an episode-title |
player-chapters-header, player-chapters-list, player-chapter, player-chapter-image | The player's chapter list, dialog[data-name='chapters'], with a picture of each where the app makes them; data-current marks the one playing |
sync-by-ear, sync-lines, sync-line | Subtitle syncing; data-closest marks the line nearest now |
player-cover, player-cover-image, player-error | What shows while a video loads, and when it fails |
player-cover-logo, player-cover-title, player-cover-detail, player-cover-bar | On that cover, the logo, or the title when there's none, the episode's line under it, and the loading bar |
pause-screen | What shows over the dimmed picture once the video has stayed paused for a few seconds; data-visible while it shows |
pause-logo, pause-title, pause-episode, pause-episode-title, pause-overview | On it, the logo, or the title when there's none, an episode's season and number, its title, and the synopsis |
player-stats | The statistics in the Android app when it plays with ExoPlayer, which draws none of its own |
player-subtitle | Subtitles in the Samsung TV app, where the page draws them |
subtitle-drop | What shows while a file is dragged over the player |
player-external | In the desktop app, the note that the video plays in your own player's window |
mini-player | The mini player, over the pages; data-mode is card, in a corner (data-corner is top-start, top-end, bottom-start or bottom-end), or bar, above the navigation on a narrow screen |
mini-player-video, mini-player-image | Where the video shows, and the title's picture there before it starts or while something covers it |
mini-player-strip, mini-player-title | The part under the video, or beside it in the bar, and the title in it, which opens the player again; its lines have data-name name and detail |
mini-player-button | Its buttons; data-name is play, pause or close |
mini-player-progress, mini-player-progress-fill | How far through the video it is, and the filled part |
Watch parties
| Selector | What it is |
|---|---|
dialog[data-name='party'] | The watch party panel |
party-section | A part of the panel; data-name is start, join, preview, invite, now-playing, members, host, overlay or leave |
party-relay, party-listing, party-preview | Before joining: a relay to start on, a party on your server, and the party a code finds |
party-tabs, party-unread | The Chat and People and invite tabs (data-name is chat or party), and the count of unread messages |
party-connection, party-watch-along | The note while the party connects or reconnects, and the one to go back to what the party is watching |
party-code, party-qr | The invite code and its QR code |
party-member | Someone in the party. States: data-you, data-host, data-away (their connection dropped), data-buffering |
party-chat, party-message, party-reaction | The chat, a message in it, and a reaction button. A message has data-you when it's yours, and data-status (sending or failed) until it's sent |
party-layer, party-status, party-action | What shows over the video: who the party is waiting for, and who paused, jumped or started something |
party-bubbles, party-bubble, party-reactions, party-reaction-sent | Messages over the video and each one, and the reactions rising over it and each one |
Other pages
| Selector | What it is |
|---|---|
search-input, search-voice, search-suggestions, search-suggestion, search-history, search-history-item, search-history-menu, search-results | The search page: the box to search and its voice search button (data-listening while it listens), the titles suggested as you type, your past searches and the menu each opens, and the results |
trailers-list, trailer, trailer-bar, trailer-embed | A title's trailers in their dialog, and in a browser, the bar above YouTube's player and the player itself. The bar's buttons have data-name exit, previous or next |
discover-label, discover-count | Beside Discover's title, the kind or genre shown, and the number of titles |
discover-filters, catalog-picker, genre-pills, genre-pill, genre-picker | Discover's filters: the catalog picker, and the genres as buttons or, when there are many, a picker |
calendar-grid, calendar-agenda, calendar-day, calendar-entry | The calendar as a month or a list; data-today marks today, data-watched a watched episode |
calendar-nav, calendar-month, calendar-today | The month buttons (data-name is previous or next), the month shown, and Today |
live-tv, channel-list, channel-row, live-side, live-preview, channel-details, live-watch, live-favourite, channel-later, docked-player | Live TV: a group's channels (data-active on the one the keys are on, data-playing on the one playing), the preview the playing video fills, and the channel's programmes |
guide, guide-ruler, guide-row, guide-channel, guide-cell, guide-now, live-top, programme-details | Live TV's guide: a day of programmes by channel (data-airing and data-active on cells), the now line, and the programme above it |
live-group-picker | The searchable list of an addon's groups, when there are too many for pills. The pills are pill-tabs named live-group and live-view |
live-from-start, live-day-picker | From the start, on a programme its channel keeps (its guide cell has data-catchup), and the list of the guide's days |
player-live, live-pill, channel-number, previous-channel | A channel in the player: the programme in place of the seek bar, the Live pill (data-behind after a pause), a typed number, and the channels sheet (a dialog named channels) |
live-remind | Remind me on a later programme (data-active once set); its guide cell has data-reminder, and the reminder as it starts is a dialog named reminder |
channel-number | The channel number being typed with a remote's digits, over the player or Live TV |
history-day, history-tiles, history-tile, history-entry, history-entry-menu, history-table, history-view, user-chips | The activity page; data-open marks an expanded tile |
downloads-summary, downloads-action | The downloads page's size and folder, and its buttons; data-name is open-folder, change-folder, open-app, pause-all, resume-all or delete-all |
downloads-section, downloads-show | A group of downloads (data-name is running, attention, sent or done), and a show's episodes in it |
download, download-image, download-title, download-version, download-status | A download and its parts; data-state as on download-button |
download-action | A download's buttons; data-name is pause, resume, retry, pick, folder or more |
settings-tabs, settings-tab-group, settings-tab, settings-panel, settings-donate | The settings tabs, their groups (data-name is watching or app), each tab and its page (data-name such as playback, subtitles, theme or about), and Donate |
settings-header, settings-header-description, settings-card, settings-card-title, settings-card-description, settings-card-body, settings-card-item, theme-preset, custom-css-editor, language-list | A tab's page. Each card has a data-name from its title, such as next-episode, and each line in it is a settings-card-item |
auth-card, user-picker, user-picker-user, servers, server, server-status, sign-in-links, quick-connect-code | Signing in, choosing a user and choosing a server; the buttons to the other screens, and the Quick Connect code |
no-catalogs-tip | The tip on where to find catalogs, when you have none |
no-details, no-details-tip | What a title's page shows instead when no addon has details for it, and its tip on where to find one |
report-sent, report-code | After sending a report, the message and the code to pass on |
quick-filter, quick-filter-import, badge-packs, badge-pack, own-badges, own-badge, badge-import, badge-try, badge-form | In the quick filters and badges editors: a filter, adding filters from text, the badge packs and each one, your own badges and each one, adding badges from text, trying badges on a name, and the form to edit one |
shortcuts-help, shortcut-keys, key-caps | The shortcuts list ? opens, a shortcut's keys in settings, and the keys shown for one |
Shared components
Menus, dialogs, buttons and form controls come from a shared set of components. Their parts carry a class named UI-<Component>__<part>, kept stable like the data-ui names:
| Class | What it is |
|---|---|
.UI-Modal__content, __overlay, __header, __title, __description, __close | A dialog box and its parts |
.UI-DropdownMenu__root, __item, __label, __separator | A menu |
.UI-ContextMenu__root, __item, __label, __separator | A right-click menu |
.UI-Tooltip__root, .UI-Popover__root, .UI-HoverCard__root | Tooltips and popups |
.UI-Button__root, .UI-IconButton__root | Buttons |
.UI-Badge__root | A small label, such as a genre |
.UI-Input__root, .UI-Textarea__root, .UI-Select__root, .UI-Switch__root | Form controls |
.UI-Card__root | A panel |
.UI-VerticalMenu__item | A sidebar item |
.UI-Carousel__item | A card's slot in a row |
.UI-Skeleton__root | A placeholder while something loads |
A message that fills a page or a row, such as an error or an empty catalog, is [data-luffy-error], with [data-luffy-error-image], [data-luffy-error-title] and [data-luffy-error-content] for its parts.
The popup messages use [data-sonner-toaster] for their container and [data-sonner-toast] for each one, with data-type set to success, error, warning or info.
Colours
The theme's colours are CSS variables, which you can set in your CSS as well. Those holding an R G B triple are used as rgb(var(--color-brand-500)), or rgb(var(--color-brand-500) / 0.5) to make them see-through.
| Variable | What it is |
|---|---|
--color-brand-50 to --color-brand-950 | The accent's shades, as R G B |
--color-gray-50 to --color-gray-950 | The background's shades, used by panels and text, as R G B |
--background | The page's colour |
--paper | The colour of menus and dialogs |
--foreground, --muted, --muted-highlight | Text, dimmed text, and dimmed text on hover |
--border, --ring | Borders, and the ring around what has keyboard focus |
--subtle, --subtle-highlight | Faint fills, such as behind a hovered item |
--brand | Accent-coloured text |
--radius, --radius-md | Corner rounding of menus and inputs |
--red, --orange, --green, --blue and the like | Named colours for text |

