Update CONTRIBUTING.md for clarity and consistency in theme development guidelines
This commit is contained in:
@@ -45,7 +45,7 @@ The orchestrator file `seasonals.js` manages theme loading at runtime. It reads
|
||||
|
||||
## 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/
|
||||
@@ -65,7 +65,7 @@ Jellyfin.Plugin.Seasonals/
|
||||
Every theme JS file follows a **consistent skeleton**. Use this as your starting template:
|
||||
|
||||
```javascript
|
||||
// ── 1. Read Configuration ──────────────────────────────────────────
|
||||
// 1. Read Configuration
|
||||
const config = window.SeasonalsPluginConfig?.MyTheme || {};
|
||||
|
||||
const enabled = config.EnableMyTheme !== undefined ? config.EnableMyTheme : true;
|
||||
@@ -74,8 +74,8 @@ const elementCount = config.ElementCount || 25;
|
||||
|
||||
let msgPrinted = false;
|
||||
|
||||
// ── 2. Toggle Function ────────────────────────────────────────────
|
||||
// Hides the effect when a video player, trailer, dashboard, or user menu is active.
|
||||
// 2. Toggle Function
|
||||
// Hides the effect when a video player, trailer (in full width mode), dashboard, or user menu is active.
|
||||
function toggleMyTheme() {
|
||||
const container = document.querySelector('.mytheme-container');
|
||||
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.
|
||||
const observer = new MutationObserver(toggleMyTheme);
|
||||
observer.observe(document.body, {
|
||||
@@ -109,7 +109,7 @@ observer.observe(document.body, {
|
||||
attributes: true
|
||||
});
|
||||
|
||||
// ── 4. Element Creation ────────────────────────────────────────────
|
||||
// 4. Element Creation
|
||||
// Create and append your animated elements to the container.
|
||||
function createElements() {
|
||||
const container = document.querySelector('.mytheme-container') || document.createElement('div');
|
||||
@@ -140,7 +140,7 @@ function createElements() {
|
||||
}
|
||||
}
|
||||
|
||||
// ── 5. Initialization ─────────────────────────────────────────────
|
||||
// 5. Initialization
|
||||
function initializeMyTheme() {
|
||||
if (!enabled) return;
|
||||
createElements();
|
||||
@@ -153,9 +153,9 @@ initializeMyTheme();
|
||||
### Key Rules
|
||||
|
||||
- **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** 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.
|
||||
|
||||
---
|
||||
@@ -165,7 +165,7 @@ initializeMyTheme();
|
||||
Every theme CSS file follows this structure:
|
||||
|
||||
```css
|
||||
/* ── Container ──────────────────────────────────────────────────── */
|
||||
/* Container */
|
||||
/* Full-screen overlay, transparent, non-interactive */
|
||||
.mytheme-container {
|
||||
display: block;
|
||||
@@ -179,7 +179,7 @@ Every theme CSS file follows this structure:
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* ── Animated Element ───────────────────────────────────────────── */
|
||||
/* Animated Element */
|
||||
.mytheme-element {
|
||||
position: fixed;
|
||||
z-index: 15;
|
||||
@@ -193,7 +193,7 @@ Every theme CSS file follows this structure:
|
||||
animation-iteration-count: infinite, infinite;
|
||||
}
|
||||
|
||||
/* ── Keyframes ──────────────────────────────────────────────────── */
|
||||
/* Keyframes */
|
||||
@keyframes mytheme-fall {
|
||||
0% { top: -10%; }
|
||||
100% { top: 100%; }
|
||||
@@ -204,7 +204,7 @@ Every theme CSS file follows this structure:
|
||||
50% { transform: translateX(80px); }
|
||||
}
|
||||
|
||||
/* ── Staggered Delays for Base Elements ─────────────────────────── */
|
||||
/* Staggered Delays for Base Elements */
|
||||
/* 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(2) { left: 20%; animation-delay: 6s, 0.5s; }
|
||||
@@ -214,9 +214,9 @@ Every theme CSS file follows this structure:
|
||||
|
||||
### Key Rules
|
||||
|
||||
- **Container** must be `position: fixed`, full-screen, with `pointer-events: none` and `z-index: 10`.
|
||||
- **Elements** should use `position: fixed` with `z-index: 15`.
|
||||
- Use **two animations** (primary movement + secondary effect) for natural-looking motion.
|
||||
- **Container** must be `position: fixed`, full-screen, with `pointer-events: none` and at least `z-index: 10`.
|
||||
- **Elements** should use `position: fixed` with at least `z-index: 15`.
|
||||
- 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 **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)
|
||||
|
||||
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/`
|
||||
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
|
||||
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
|
||||
@@ -264,7 +259,7 @@ const ThemeConfigs = {
|
||||
> [!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.
|
||||
>
|
||||
> 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
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
### What to Verify
|
||||
|
||||
- ✅ The effect is visible on the dark background
|
||||
- ✅ The animation runs smoothly without jank
|
||||
- ✅ The effect is visible on the background
|
||||
- ✅ The animation runs smoothly
|
||||
- ✅ Elements are spread across the full viewport
|
||||
- ✅ The mock header is **not blocked** by the effect (thanks to `pointer-events: none`)
|
||||
- ✅ Performance is acceptable (check DevTools → Performance tab)
|
||||
- ✅ No console errors appear (check DevTools → Console)
|
||||
- ✅ No theme related 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)
|
||||
- [ ] (If applicable) Created `{themeName}_images/` with optimized assets
|
||||
- [ ] Added theme to `ThemeConfigs` in `seasonals.js`
|
||||
- [ ] Tested locally with `test-site-new.html`
|
||||
- [ ] No console errors
|
||||
- [ ] Tested locally with `test-site.html`
|
||||
- [ ] No theme related console errors
|
||||
- [ ] Effect has `pointer-events: none` (doesn't block the UI)
|
||||
- [ ] Effect hides during video/trailer playback (toggle function implemented)
|
||||
- [ ] (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.
|
||||
|
||||
**Files Added:**
|
||||
- `{themeName}.js`
|
||||
- `{themeName}.css`
|
||||
- `{themeName}_images/` (if applicable)
|
||||
|
||||
**Screenshot / Recording:**
|
||||
[Attach a screenshot or GIF here]
|
||||
[Attach a screenshot or GIF showcasing the theme in action]
|
||||
|
||||
**Testing:**
|
||||
- Tested locally with test-site-new.html ✅
|
||||
|
||||
Reference in New Issue
Block a user