Theming
py-player supports three customization layers that stack on top of each other: theme presets, CSS variable overrides, and CSS Parts.
Theme Presets
Set the theme attribute to apply a color preset:
html
<py-audio src="audio.mp3" theme="dark"></py-audio>
<py-video src="video.mp4" theme="midnight"></py-video>
<py-player src="media.mp3" theme="nord"></py-player>| Preset | Description |
|---|---|
auto | Follows system prefers-color-scheme (default) |
light | White background, light tones |
dark | Dark charcoal tones |
midnight | Deep blue tones |
nord | Nord color palette |
sunset | Warm oranges and reds |
forest | Earthy greens |
Accent Color
Override the accent color independently of the theme:
html
<py-audio src="audio.mp3" theme="dark" accent="#e07b39"></py-audio>
<py-audio src="audio.mp3" theme="nord" accent="#ff6b6b"></py-audio>Any valid CSS color works: hex, rgb(), hsl(), named colors.
CSS Variables
Override individual design tokens from outside the Shadow DOM:
css
py-audio {
--py-accent: #e07b39;
--py-bg: #1a1a2e;
--py-bg-secondary: #16213e;
--py-text: #eaeaea;
--py-radius: 16px;
}All CSS Variables
| Variable | Default (light) | Description |
|---|---|---|
--py-accent | #378ADD | Accent/highlight color |
--py-bg | #ffffff | Primary background |
--py-bg-secondary | #f5f5f4 | Secondary background |
--py-bg-hover | #ede9e0 | Hover state background |
--py-border | rgba(0,0,0,0.08) | Border color |
--py-text | #1a1916 | Primary text |
--py-text-secondary | #6b6860 | Secondary text |
--py-text-muted | #a8a59e | Muted/disabled text |
--py-radius | 10px | Border radius |
--py-font | 'DM Sans', system-ui | Font family |
CSS Parts
Style specific internal elements from outside the Shadow DOM using ::part():
css
py-audio::part(root) {
border: 2px solid hotpink;
border-radius: 20px;
}
py-audio::part(btn-play) {
background: linear-gradient(135deg, #ff6b6b, #feca57);
border-radius: 50%;
}
py-audio::part(progress-fill) {
background: #e07b39;
}Available Parts
Shared (audio + video):
| Part | Description |
|---|---|
root | Outer container |
top-bar | Title bar |
controls | Button group container |
btn-play | Play/pause button |
btn-prev | Previous button |
btn-next | Next button |
progress | Progress bar track |
progress-fill | Progress bar filled portion |
progress-thumb | Progress bar handle |
playlist | Playlist container |
playlist-item | Individual track/chapter item |
upload-area | File upload zone |
now-playing | Current track display |
Audio-only:
| Part | Description |
|---|---|
visualizer | Visualizer container |
viz-bar | Individual frequency bar |
Video-only:
| Part | Description |
|---|---|
video-container | Video element wrapper |
video-controls | Bottom controls bar |
volume-slider | Volume control |
fullscreen-btn | Fullscreen toggle |
Combining Layers
Layers stack: preset → CSS variables → CSS Parts.
html
<style>
#my-player {
--py-radius: 20px;
--py-font: 'Georgia', serif;
}
#my-player::part(btn-play) {
width: 48px;
height: 48px;
border-radius: 50%;
}
</style>
<py-audio
id="my-player"
src="audio.mp3"
theme="forest"
accent="#2d6a4f"
></py-audio>Layout + Theme Combinations
Layout and theme are independent — any layout works with any theme:
html
<py-audio layout="visualizer" theme="midnight" accent="#ff6b6b"></py-audio>
<py-audio layout="podcast" theme="nord"></py-audio>
<py-audio layout="compact" theme="sunset" accent="#c0392b"></py-audio>