Update CONTRIBUTING.md for clarity and consistency in theme development guidelines

This commit is contained in:
CodeDevMLH
2026-02-17 14:10:19 +01:00
parent 81facbdb00
commit 1ddaab325e

View File

@@ -45,7 +45,7 @@ The orchestrator file `seasonals.js` manages theme loading at runtime. It reads
## Standard Theme File Structure ## Standard Theme File Structure
Here is the complete file layout for a theme called `mytheme`: Here is a complete file layout for a theme called `mytheme`:
``` ```
Jellyfin.Plugin.Seasonals/ Jellyfin.Plugin.Seasonals/
@@ -65,7 +65,7 @@ Jellyfin.Plugin.Seasonals/
Every theme JS file follows a **consistent skeleton**. Use this as your starting template: Every theme JS file follows a **consistent skeleton**. Use this as your starting template:
```javascript ```javascript
// ── 1. Read Configuration ────────────────────────────────────────── // 1. Read Configuration
const config = window.SeasonalsPluginConfig?.MyTheme || {}; const config = window.SeasonalsPluginConfig?.MyTheme || {};
const enabled = config.EnableMyTheme !== undefined ? config.EnableMyTheme : true; const enabled = config.EnableMyTheme !== undefined ? config.EnableMyTheme : true;
@@ -74,8 +74,8 @@ const elementCount = config.ElementCount || 25;
let msgPrinted = false; let msgPrinted = false;
// ── 2. Toggle Function ──────────────────────────────────────────── // 2. Toggle Function
// Hides the effect when a video player, trailer, dashboard, or user menu is active. // Hides the effect when a video player, trailer (in full width mode), dashboard, or user menu is active.
function toggleMyTheme() { function toggleMyTheme() {
const container = document.querySelector('.mytheme-container'); const container = document.querySelector('.mytheme-container');
if (!container) return; if (!container) return;
@@ -100,7 +100,7 @@ function toggleMyTheme() {
} }
} }
// ── 3. MutationObserver ──────────────────────────────────────────── // 3. MutationObserver
// Watches the DOM for changes so the effect can auto-hide/show. // Watches the DOM for changes so the effect can auto-hide/show.
const observer = new MutationObserver(toggleMyTheme); const observer = new MutationObserver(toggleMyTheme);
observer.observe(document.body, { observer.observe(document.body, {
@@ -109,7 +109,7 @@ observer.observe(document.body, {
attributes: true attributes: true
}); });
// ── 4. Element Creation ──────────────────────────────────────────── // 4. Element Creation
// Create and append your animated elements to the container. // Create and append your animated elements to the container.
function createElements() { function createElements() {
const container = document.querySelector('.mytheme-container') || document.createElement('div'); const container = document.querySelector('.mytheme-container') || document.createElement('div');
@@ -140,7 +140,7 @@ function createElements() {
} }
} }
// ── 5. Initialization ───────────────────────────────────────────── // 5. Initialization
function initializeMyTheme() { function initializeMyTheme() {
if (!enabled) return; if (!enabled) return;
createElements(); createElements();
@@ -153,9 +153,9 @@ initializeMyTheme();
### Key Rules ### Key Rules
- **Always** read config from `window.SeasonalsPluginConfig?.{ThemeName}`. - **Always** read config from `window.SeasonalsPluginConfig?.{ThemeName}`.
- **Always** implement the toggle function with the same selectors (`.videoPlayerContainer`, `.youtubePlayerContainer`, `.dashboardDocument`, `#app-user-menu`). - **Always** implement the toggle function with the same selectors (`.videoPlayerContainer`, `.youtubePlayerContainer`, `.dashboardDocument`, `#app-user-menu`, just use the above template).
- **Always** use `aria-hidden="true"` on the container for accessibility. - **Always** use `aria-hidden="true"` on the container for accessibility.
- **Always** call your `initialize` function at the end of the file. - Call your `initialize` function at the end of the file.
- For **canvas-based** themes (like `snowfall.js`), use a `<canvas>` element with `requestAnimationFrame` instead of CSS animations. Make sure to clean up with `cancelAnimationFrame` when hidden. - For **canvas-based** themes (like `snowfall.js`), use a `<canvas>` element with `requestAnimationFrame` instead of CSS animations. Make sure to clean up with `cancelAnimationFrame` when hidden.
--- ---
@@ -165,7 +165,7 @@ initializeMyTheme();
Every theme CSS file follows this structure: Every theme CSS file follows this structure:
```css ```css
/* ── Container ──────────────────────────────────────────────────── */ /* Container */
/* Full-screen overlay, transparent, non-interactive */ /* Full-screen overlay, transparent, non-interactive */
.mytheme-container { .mytheme-container {
display: block; display: block;
@@ -179,7 +179,7 @@ Every theme CSS file follows this structure:
z-index: 10; z-index: 10;
} }
/* ── Animated Element ───────────────────────────────────────────── */ /* Animated Element */
.mytheme-element { .mytheme-element {
position: fixed; position: fixed;
z-index: 15; z-index: 15;
@@ -193,7 +193,7 @@ Every theme CSS file follows this structure:
animation-iteration-count: infinite, infinite; animation-iteration-count: infinite, infinite;
} }
/* ── Keyframes ──────────────────────────────────────────────────── */ /* Keyframes */
@keyframes mytheme-fall { @keyframes mytheme-fall {
0% { top: -10%; } 0% { top: -10%; }
100% { top: 100%; } 100% { top: 100%; }
@@ -204,7 +204,7 @@ Every theme CSS file follows this structure:
50% { transform: translateX(80px); } 50% { transform: translateX(80px); }
} }
/* ── Staggered Delays for Base Elements ─────────────────────────── */ /* Staggered Delays for Base Elements */
/* Spread the initial 12 elements across the screen */ /* Spread the initial 12 elements across the screen */
.mytheme-element:nth-of-type(1) { left: 10%; animation-delay: 1s, 1s; } .mytheme-element:nth-of-type(1) { left: 10%; animation-delay: 1s, 1s; }
.mytheme-element:nth-of-type(2) { left: 20%; animation-delay: 6s, 0.5s; } .mytheme-element:nth-of-type(2) { left: 20%; animation-delay: 6s, 0.5s; }
@@ -214,9 +214,9 @@ Every theme CSS file follows this structure:
### Key Rules ### Key Rules
- **Container** must be `position: fixed`, full-screen, with `pointer-events: none` and `z-index: 10`. - **Container** must be `position: fixed`, full-screen, with `pointer-events: none` and at least `z-index: 10`.
- **Elements** should use `position: fixed` with `z-index: 15`. - **Elements** should use `position: fixed` with at least `z-index: 15`.
- Use **two animations** (primary movement + secondary effect) for natural-looking motion. - Use **animations** (eg. primary movement + secondary effect for natural-looking motion).
- Include **`nth-of-type` rules** for the initial set of base elements to stagger them. - Include **`nth-of-type` rules** for the initial set of base elements to stagger them.
- Include **webkit prefixes** (`-webkit-animation-*`, `@-webkit-keyframes`) for broader compatibility (see existing themes for examples). - Include **webkit prefixes** (`-webkit-animation-*`, `@-webkit-keyframes`) for broader compatibility (see existing themes for examples).
@@ -224,7 +224,7 @@ Every theme CSS file follows this structure:
## Image Assets (Optional) ## Image Assets (Optional)
If your theme uses image sprites (e.g., leaves, ghosts, eggs): If your theme uses images (e.g., leaves, ghosts, eggs):
1. Create a folder: `Jellyfin.Plugin.Seasonals/Web/{themeName}_images/` 1. Create a folder: `Jellyfin.Plugin.Seasonals/Web/{themeName}_images/`
2. Place your assets inside (PNG recommended, keep files small) 2. Place your assets inside (PNG recommended, keep files small)
@@ -232,11 +232,6 @@ If your theme uses image sprites (e.g., leaves, ghosts, eggs):
```javascript ```javascript
img.src = '../Seasonals/Resources/mytheme_images/sprite1.png'; img.src = '../Seasonals/Resources/mytheme_images/sprite1.png';
``` ```
4. For local testing, you can reference them directly:
```javascript
img.src = './mytheme_images/sprite1.png';
```
--- ---
## Registering Your Theme ## Registering Your Theme
@@ -264,7 +259,7 @@ const ThemeConfigs = {
> [!NOTE] > [!NOTE]
> The backend registration is handled by the plugin maintainers. You do **not** need to modify C# files for your theme submission. Just focus on the JS/CSS/images. > The backend registration is handled by the plugin maintainers. You do **not** need to modify C# files for your theme submission. Just focus on the JS/CSS/images.
> >
> However, if you'd like to include full backend integration, add your theme to the enum/configuration in `Configuration/PluginConfiguration.cs`. > However, if you'd like to include full backend integration, add your theme to the enum/configuration in `Configuration/PluginConfiguration.cs` and the selectors in `configPage.html`.
--- ---
@@ -275,19 +270,18 @@ You can test your theme without a Jellyfin server by using the included test sit
### Steps ### Steps
1. Navigate to the `Jellyfin.Plugin.Seasonals/Web/` directory 1. Navigate to the `Jellyfin.Plugin.Seasonals/Web/` directory
2. Open `test-site-new.html` in your browser (just double-click the file) 2. Open `test-site.html` in your browser (just double-click the file) or vscode or what ever you use...
3. Use the **theme selector dropdown** to pick an existing theme or select **"Custom (Local Files)"** to test your own 3. Use the **theme selector dropdown** to pick an existing theme or select **"Custom (Local Files)"** to test your own
4. When "Custom" is selected, enter your theme's JS and CSS filenames (e.g., `mytheme.js` and `mytheme.css`) 4. When "Custom" is selected, enter your theme's JS and CSS filenames (e.g., `mytheme.js` and `mytheme.css` (must be in the same folder as `test-site.html` for this to work))
5. Click **"Load Theme"** to apply. Click **"Clear & Reload"** to reset and try again 5. Click **"Load Theme"** to apply. Click **"Clear & Reload"** to reset and try again
### What to Verify ### What to Verify
- ✅ The effect is visible on the dark background - ✅ The effect is visible on the background
- ✅ The animation runs smoothly without jank - ✅ The animation runs smoothly
- ✅ Elements are spread across the full viewport - ✅ Elements are spread across the full viewport
- ✅ The mock header is **not blocked** by the effect (thanks to `pointer-events: none`) - ✅ The mock header is **not blocked** by the effect (thanks to `pointer-events: none`)
- ✅ Performance is acceptable (check DevTools → Performance tab) - ✅ No theme related console errors appear (check DevTools → Console)
- ✅ No console errors appear (check DevTools → Console)
--- ---
@@ -299,8 +293,8 @@ You can test your theme without a Jellyfin server by using the included test sit
- [ ] Created `{themeName}.css` following the [CSS pattern](#css-file-pattern) - [ ] Created `{themeName}.css` following the [CSS pattern](#css-file-pattern)
- [ ] (If applicable) Created `{themeName}_images/` with optimized assets - [ ] (If applicable) Created `{themeName}_images/` with optimized assets
- [ ] Added theme to `ThemeConfigs` in `seasonals.js` - [ ] Added theme to `ThemeConfigs` in `seasonals.js`
- [ ] Tested locally with `test-site-new.html` - [ ] Tested locally with `test-site.html`
- [ ] No console errors - [ ] No theme related console errors
- [ ] Effect has `pointer-events: none` (doesn't block the UI) - [ ] Effect has `pointer-events: none` (doesn't block the UI)
- [ ] Effect hides during video/trailer playback (toggle function implemented) - [ ] Effect hides during video/trailer playback (toggle function implemented)
- [ ] (Optional) Included a screenshot or short recording of the effect to the readme - [ ] (Optional) Included a screenshot or short recording of the effect to the readme
@@ -312,13 +306,8 @@ You can test your theme without a Jellyfin server by using the included test sit
**Description:** Brief description of the theme and what occasion/season it's for. **Description:** Brief description of the theme and what occasion/season it's for.
**Files Added:**
- `{themeName}.js`
- `{themeName}.css`
- `{themeName}_images/` (if applicable)
**Screenshot / Recording:** **Screenshot / Recording:**
[Attach a screenshot or GIF here] [Attach a screenshot or GIF showcasing the theme in action]
**Testing:** **Testing:**
- Tested locally with test-site-new.html ✅ - Tested locally with test-site-new.html ✅