# FolderView3 Custom CSS & JS Developer Guide
## Overview
FolderView3 supports custom CSS and JavaScript extensions that load alongside the plugin on each tab. You can restyle folders, tooltips, container rows, and the dashboard — or add custom behavior via the JavaScript event system.
**Custom file locations on Unraid:**
- **CSS:** `/boot/config/plugins/folder.view3/styles/`
- **JS:** `/boot/config/plugins/folder.view3/scripts/`
These directories persist across reboots (they live on the USB flash drive).
---
## File Naming
Files must follow the pattern: **`name.tab.ext`**
| Part | Description |
|------|-------------|
| `name` | Any string (your theme name, a load-order prefix, etc.) |
| `tab` | One or more of: `docker`, `vm`, `dashboard` (chain with `-`) |
| `ext` | `.css` or `.js` |
**Examples:**
```
mytheme.docker.css → Docker tab only
mytheme.vm.css → VM tab only
mytheme.dashboard.css → Dashboard only
mytheme.docker-vm.css → Docker + VM tabs
mytheme.dashboard-docker-vm.css → All three tabs
01-colors.docker-vm.css → Prefixed for load ordering
```
**Will NOT work:**
```
mytheme-docker.css → Hyphen instead of dot separator
mytheme_docker.css → Underscore separator
mytheme.css → Missing tab name
```
**Disabling:** Append `.disabled` to any file to skip loading without deleting it:
```
mytheme.docker.css.disabled
```
---
## Load Order
Custom files load **after** the plugin's built-in CSS, so your rules naturally override defaults. No `!important` should be needed for most overrides — use CSS variables where possible.
```
1. customEvents.js (shared utilities: escapeHtml, CSRF, event bus)
2. shared.js (shared functions: debug, API, row separators, preview)
3. Custom JS files (your scripts)
4. Plugin JS (docker.js / vm.js / dashboard.js)
5. folder-common.css (shared CSS variables and rules)
6. Plugin CSS (docker.css / vm.css / dashboard.css)
7. Custom CSS files (your styles — loaded last, highest priority)
```
---
## CSS Variables Reference
Override these in your custom CSS by redefining them in `:root`. All are defined in `folder-common.css` (loaded on Docker and VM tabs).
### Layout
| Variable | Default | Description |
|----------|---------|-------------|
| `--fv3-folder-name-width` | `220px` | Width of the folder name cell |
| `--fv3-folder-preview-height` | `3.5em` | Height of the collapsed folder preview bar |
| `--fv3-folder-preview-radius` | `4px` | Border radius of the preview bar |
| `--fv3-folder-icon-spacing` | `4px` | Gap between container icons in preview |
| `--fv3-folder-preview-wrapper-margin` | `10px` | Left margin of each preview icon wrapper |
| `--fv3-folder-name-min-width` | `220px` | Minimum width of folder name cell |
| `--fv3-folder-name-gap` | `4px` | Gap between folder name elements |
| `--fv3-folder-row-padding` | `0` | Padding around folder rows |
| `--fv3-folder-row-radius` | `0` | Border radius on folder rows |
| `--fv3-folder-row-border-width` | `0` | Border width on folder rows |
| `--fv3-row-padding` | `8px` | General row content padding |
| `--tooltip-spacing` | `5px` | Spacing around tooltip content |
### Advanced Preview Tooltip
| Variable | Default | Description |
|----------|---------|-------------|
| `--fv3-tooltip-min-width` | `700px` | Minimum width of the advanced preview popup |
| `--fv3-tooltip-max-height` | `80vh` | Maximum height of the advanced preview popup |
| `--fv3-tooltip-action-pane-width` | `220px` | Width of the action button sidebar |
### Colors — Global (all pages)
| Variable | Default | Description |
|----------|---------|-------------|
| `--fv3-accent-color` | `var(--color-orange, #f0a30a)` | Accent color for panels and borders |
| `--fv3-surface-tint` | `rgba(128, 128, 128, 0.1)` | Subtle background tint |
| `--fv3-hover-bg` | `rgba(128, 128, 128, 0.2)` | Hover state background |
| `--fv3-border` | `1px solid rgba(128, 128, 128, 0.3)` | Standard border style |
| `--fv3-folder-name-bg` | `transparent` | Folder name cell background |
| `--fv3-folder-name-color` | `inherit` | Folder name text color |
| `--fv3-appname-color` | `inherit` | Container/VM name text color |
| `--fv3-folder-icon-size` | `48px` | Folder icon size |
| `--fv3-folder-name-size` | `inherit` | Folder name font size |
| `--fv3-folder-name-weight` | `inherit` | Folder name font weight |
### Colors — Docker & VM tabs only
| Variable | Default | Description |
|----------|---------|-------------|
| `--folder-view3-graph-cpu` | `#2b8da3` | CPU graph line color (Docker only) |
| `--folder-view3-graph-mem` | `#5d6db6` | Memory graph line color (Docker only) |
| `--fv3-toggle-color` | `#ff8c2f` | Folder chevron dropdown button color |
| `--fv3-toggle-hover-color` | `#ffad5c` | Chevron button hover color |
| `--fv3-chevron-color` | `inherit` | Expand/collapse chevron color |
| `--fv3-preview-border-color` | `currentColor` | Preview area border color |
| `--fv3-vertical-bars-color` | `currentColor` | Vertical divider bar color |
| `--fv3-separator-color` | `rgba(128, 128, 128, 0.3)` | Row separator color (expand mode) |
| `--fv3-separator-bg` | `rgba(128, 128, 128, 0.15)` | Row separator background |
| `--fv3-folder-row-border-color` | `transparent` | Folder row border color |
| `--fv3-preview-row-border-width` | `0` | Preview area row border width |
| `--fv3-preview-row-border-color` | `transparent` | Preview area row border color |
| `--fv3-scrollbar-color` | `rgba(255, 140, 47, 0.5)` | Scroll overflow scrollbar thumb |
| `--fv3-folder-preview-bg` | `transparent` | Preview bar background color |
| `--fv3-preview-icon-size` | `32px` | Container icon size in preview |
| `--fv3-appname-max-width` | `120px` | Max width before container names truncate |
| `--fv3-appname-size` | `inherit` | Container name font size in previews |
| `--fv3-chevron-size` | `14px` | Chevron button font size |
| `--fv3-row-bg` | `transparent` | Folder row background |
| `--fv3-row-alt-bg` | Unraid's `--dynamix-tablesorter-tbody-row-alt-bg-color` | Alternating row background |
### Colors — Dashboard only
| Variable | Default | Description |
|----------|---------|-------------|
| `--fv3-panel-border` | `rgba(128, 128, 128, 0.2)` | Fullwidth/accordion panel border |
| `--fv3-panel-bg` | `rgba(128, 128, 128, 0.08)` | Panel background |
| `--fv3-tab-active-bg` | `transparent` | Expanded tab background |
| `--fv3-tab-active-border` | `transparent` | Expanded tab border |
| `--fv3-showcase-bg` | `transparent` | Showcase (child area) background |
| `--fv3-inset-bg` | `transparent` | Inset expanded outer wrapper bg |
| `--fv3-inset-fill` | `none` | Inset SVG L-shape fill |
| `--fv3-inset-border-color` | `rgba(128,128,128,0.3)` | Inset SVG outer border |
| `--fv3-inset-showcase-fill` | `none` | Inset SVG inner box fill |
| `--fv3-inset-showcase-border` | `rgba(128,128,128,0.2)` | Inset SVG inner box border |
| `--fv3-embossed-border` | `rgba(128,128,128,0.3)` | Embossed outer border |
| `--fv3-embossed-accent` | `var(--color-orange, #f0a30a)` | Embossed left accent border |
| `--fv3-embossed-inner-border` | `rgba(128,128,128,0.2)` | Embossed inner showcase border |
**Example override:**
```css
:root {
--fv3-accent-color: #e74c3c;
--fv3-toggle-color: #e74c3c;
--fv3-toggle-hover-color: #ff6b6b;
--fv3-folder-preview-bg: rgba(0, 0, 0, 0.05);
--fv3-row-bg: #191818;
--fv3-row-alt-bg: #212121;
}
```
---
## Dashboard Layout Classes
FolderView3 adds layout classes to the container `
` when a non-classic dashboard layout is selected. These are useful for custom CSS targeting.
| Class | Applied to | Description |
|-------|-----------|-------------|
| `.fv3-layout-classic` | Container `
```
### Container Status Indicator (Only icon preview, added 2026.05.21)
When a folder's Preview is set to **Only icon** and Container/VM status is **Symbol**, each container icon in the preview row gets a small play/square indicator next to it on a second line (similar to how the icon-and-label preview shows them).
```html
```
**Selectors:**
| Selector | Purpose |
|----------|---------|
| `tr.folder[data-fv3-preview-status="symbol"]` | Folder row in icon-only mode with status icons enabled |
| `tr.folder[data-fv3-preview-status="grayscale"]` | Folder row in icon-only mode with stopped-greyscale enabled (desaturates `span.hand` of stopped containers) |
| `.folder-preview-wrapper[data-fv3-state="running"]` | Per-container preview wrapper, running state |
| `.folder-preview-wrapper[data-fv3-state="stopped"]` | Per-container preview wrapper, stopped state |
| `.fv3-status-icon` | The `` element. **Distinct from the folder-level `.fv3-folder-status-icon`** — this one lives per-container inside the preview row, not on the folder pill on Dashboard. Running uses `fa-play.started.green-text`, stopped uses `fa-square.stopped.red-text`. |
| `.fv3-status-line` | The ` ` element that pushes the status icon to a second line |
**Example: Change the running indicator from green play to a checkmark**
```css
.folder-preview-wrapper[data-fv3-state="running"] .fv3-status-icon::before {
content: "\f00c"; /* fa-check */
color: limegreen;
}
.folder-preview-wrapper[data-fv3-state="running"] .fv3-status-icon { color: limegreen; }
```
**Example: Replace symbol with a colored dot**
```css
.fv3-status-icon { font-size: 0; width: 8px; height: 8px; border-radius: 50%; }
.folder-preview-wrapper[data-fv3-state="running"] .fv3-status-icon { background: #4caf50; }
.folder-preview-wrapper[data-fv3-state="stopped"] .fv3-status-icon { background: #e53935; }
```
### Folder Editor Form (added 2026.05.21)
The folder editor page (`/Docker/Folder` or `/VMs/Folder`) gained a Delete button in the form footer.
```html
```
**Selectors:**
| Selector | Purpose |
|----------|---------|
| `.fv3-form-actions` | Flex row holding the form's footer buttons (Submit/Cancel on left, Delete on right) |
| `.fv3-form-actions-right` | Container for the right-aligned Delete button. Only rendered when editing an existing folder (not on create). |
| `.fv3-delete-folder-btn` | The Delete button itself. Default red; override `background`, `color`, `border-color` to restyle. |
### Settings Page Constraint Engine (added 2026.05.21)
The Settings → FolderView3 → Folder Defaults panel uses a declarative visibility system. Each setting row's `
` carries a `data-fv3-show-preview="..."` attribute listing the Preview values (0/1/2/3/4) for which it's relevant. JS toggles `display` accordingly. Customizers can target these conditionally:
```css
/* Style settings rows that are currently hidden by the constraint engine */
.fv3-page-panel .basic[style*="display: none"] { /* won't normally apply since display:none hides */ }
/* Style row when active for a specific preview mode */
.basic[data-fv3-show-preview~="2"] { /* matches rows shown in Only icon mode */ }
```
The Dashboard panel also adds:
- `dashboard-context` select (Default / Advanced) — global toggle for the Dashboard advanced preview popup
- `.fv3-dashboard-context-advanced` — sub-option rows visible only when context = Advanced
### Advanced Preview Tooltip Background (changed 2026.05.21)
The popup background used to inherit (often resolving to transparent). It now resolves to `var(--background-color, rgb(29, 27, 27))` — Unraid's theme-aware page background. Customizers who were previously setting `.preview-outbox { background-color: ... }` continue to work; the change just gives a sensible default when no override is present.
The outer `.tooltipster-base` is now `background: transparent;` so the tooltipster arrow appears clean without a surrounding box. The actual popup surface comes from `.preview-outbox`.
---
## Common Styling Recipes
| Goal | CSS |
|------|-----|
| Change folder name width | `:root { --fv3-folder-name-width: 180px; }` |
| Change preview bar height | `:root { --fv3-folder-preview-height: 2.5em; }` |
| Style folder name text | `.folder-name-sub .folder-appname { font-size: 1.5rem; font-weight: bold; }` |
| Style folder icon | `.folder-img { width: 36px; height: 36px; }` |
| Style icon hover | `.folder-img:hover { transform: scale(1.2); }` |
| Change preview icon spacing | `:root { --fv3-folder-icon-spacing: 8px; }` |
| Style expanded container rows | `.folder-element { background-color: rgba(0,0,0,0.05); }` |
| Style CPU/Memory text | `.folder-cpu { color: #00BCD4; } .folder-mem { color: #673AB7; }` |
| Change graph colors | `:root { --folder-view3-graph-cpu: #00BCD4; --folder-view3-graph-mem: #673AB7; }` |
| Style tooltip background | `.preview-outbox { background-color: #1c1b1b; }` |
| Style preview dividers | `.folder-preview-divider { border-color: #444 !important; }` |
| Style expanded folder name | `.folder-showcase-outer[expanded="true"] .fv3-folder-appname { font-weight: bold; color: #f0a30a; }` |
| Style child container names | `.fv3-child-appname-text { font-weight: bold; color: #00698c; }` |
| Hide folder start/stop text | `.folder-state { display: none; }` |
| Style dropdown chevron | `.folder-dropdown { color: #607D8B; }` |
---
## JavaScript Extension Events
The plugin dispatches custom events via `folderEvents` (a shared `EventTarget`). Listen in your custom JS:
```javascript
folderEvents.addEventListener('docker-post-folder-creation', (e) => {
const { id, folder } = e.detail;
// DOM is ready — modify the folder row
});
```
### Available Events
**Docker tab:**
| Event | Fired | Detail |
|-------|-------|--------|
| `docker-pre-folders-creation` | Before any folders render | `{ folders, containerInfo, order }` |
| `docker-post-folders-creation` | After all folders render | `{ folders, containerInfo, order }` |
| `docker-pre-folder-creation` | Before a single folder renders | `{ id, folder }` |
| `docker-post-folder-creation` | After a single folder renders | `{ id, folder }` |
| `docker-pre-folder-preview` | Before preview icons are built | `{ id, folder }` |
| `docker-post-folder-preview` | After preview icons are built | `{ id, folder }` |
| `docker-pre-folder-expansion` | Before folder expands/collapses | `{ id }` |
| `docker-post-folder-expansion` | After folder expands/collapses | `{ id }` |
| `docker-tooltip-before` | Before advanced preview opens | `{ id }` |
| `docker-tooltip-ready-start` | Tooltip DOM created, before content | `{ id }` |
| `docker-tooltip-ready-end` | Tooltip fully populated | `{ id }` |
| `docker-tooltip-after` | After tooltip closes | `{ id }` |
| `docker-folder-context` | Right-click context menu opens | `{ id, opts }` |
**VM tab:** Same pattern with `vm-` prefix (no tooltip events).
**Dashboard:** Dispatches both `docker-` and `vm-` events for their respective folder types.
---
## Theme Compatibility Tips
1. **Never use hardcoded colors.** Use `inherit`, `currentColor`, or `rgba()` so your theme works on both Unraid dark and light themes.
2. **Prefer CSS variables over `!important`.** Override `--fv3-*` variables in `:root` — this is the intended customization mechanism.
3. **Docker and VM share selectors.** `.folder-name-sub`, `.folder-outer`, `.folder-preview`, `.folder-dropdown` — if you style one, consider whether you need to style both.
4. **Dashboard has a different DOM structure** than Docker/VM tabs. Dashboard uses tile-based layout, not table rows. Test your theme on all three tabs.
5. **Scope your selectors.** Use `#docker_containers` or `#kvm_table` to target specific tabs and avoid accidentally styling other plugins' elements.
6. **Unraid theme variables** are available. For example, `--dynamix-tablesorter-tbody-row-alt-bg-color` provides the current theme's alternating row color.
7. **Cache busting.** Unraid's `autov()` appends a version hash to file URLs. If your changes aren't appearing, rename the file or clear your browser cache.
---
## Community Themes
These community themes serve as working examples:
- [hernandito/folder.view.custom](https://github.com/hernandito/folder.view.custom) — Compact and Midsize designs
- [Mattaton/folder.view.custom.css](https://github.com/Tyree/folder.view.custom.css) — urblack, urgray, urwhite themes
- [masterwishx/folder.view.custom.css](https://github.com/masterwishx/folder.view.custom.css) — urblack theme with extensive CSS variable system
---
## Default Styles
The plugin's built-in stylesheets for reference:
- [folder-common.css](../src/folder.view3/usr/local/emhttp/plugins/folder.view3/styles/folder-common.css) — shared variables and rules (Docker + VM)
- [docker.css](../src/folder.view3/usr/local/emhttp/plugins/folder.view3/styles/docker.css) — Docker-specific (tooltips, context menu)
- [vm.css](../src/folder.view3/usr/local/emhttp/plugins/folder.view3/styles/vm.css) — VM-specific
- [dashboard.css](../src/folder.view3/usr/local/emhttp/plugins/folder.view3/styles/dashboard.css) — Dashboard layouts
## CSS Template
A starter template for custom themes is available at [examples/custom-template.css](examples/custom-template.css). Copy it to `/boot/config/plugins/folder.view3/styles/` on your Unraid server and rename it following the naming convention above.
## CSS Tool & Theme Manager
The FolderView3 settings page includes a built-in CSS Tool with:
- **Themes** — Import community CSS themes from GitHub (`owner/repo`). One theme active at a time. Automatic update checking via GitHub API SHA comparison.
- **Variables** — Edit all 27 CSS variables with color pickers and sliders. Per-page scope (Global/Dashboard/Docker/VM).
- **Presets** — One-click preset themes (Default, Compact, Blue Accent, Muted).
- **Advanced CSS** — Free-form CSS textarea for custom rules.
### Generated CSS File
The CSS Tool generates `_fv3-generated.docker-vm-dashboard.css` in the styles directory. It loads before user CSS (underscore prefix sorts first) and contains:
- `:root {}` block with overridden variable values
- Custom CSS from the Advanced tab
### Theme File Convention
Imported themes are stored as subdirectories in `/boot/config/plugins/folder.view3/styles/`:
- `masterwishx/` — active theme folder
- `masterwishx.disabled/` — disabled theme (`.disabled` suffix)
- `.fv3-source` — JSON file inside theme folder with repo URL and file SHAs for update checking
### PHP Endpoints (CSS Tool)
| Endpoint | Method | Purpose |
|----------|--------|---------|
| `read_css_config.php` | GET | Read CSS config |
| `read_css_defaults.php` | GET | Get all variable defaults |
| `update_css_config.php` | POST | Save CSS config + generate CSS |
| `list_themes.php` | GET | List installed themes |
| `toggle_theme.php` | POST | Enable/disable theme |
| `import_theme.php` | POST | Import from GitHub |
| `delete_theme.php` | POST | Delete theme |
| `export_all.php` | GET | Full backup (all configs + CSS) |
| `import_all.php` | POST | Restore full backup |
## Folder Defaults System
Global defaults are stored in `settings.json` with keys like `default_preview`, `default_overflow`, `default_preview_hover`, etc. When rendering, `fv3ApplyDefaults()` in `shared.js` merges defaults into any folder whose settings are unset. New folders created via Folder.page are pre-filled from these defaults.
The "Apply Defaults to All Folders" button on the settings page reads current defaults and writes them to every existing folder config.
## Theme Manager
### Import Flow
1. User enters `owner/repo` (or `owner/repo:branch`) → JS fetches GitHub Contents API (1 call)
2. If repo has subdirectories with CSS (e.g. masterwishx, hernandito) → directory picker shown with checkboxes for multi-select
3. Selected directories are sent to PHP `import_theme.php` which fetches file listing (1 API call per dir) and downloads via `raw.githubusercontent.com` (no API limit)
4. PHP scans downloaded CSS for security warnings, returns results to JS
5. If external URLs found → progress dialog shows warnings, user chooses Keep/Delete
6. `.fv3-source` JSON file stored in theme folder: `{ "repo": "owner/repo", "path": "subdir", "branch": "branch", "files": { "file.css": "sha" }, "updated": "ISO date" }`
7. Non-default branch names (not `main`/`master`) are appended to the theme folder name
### Update Checking
On settings page load, each theme's SHAs are compared against GitHub API. Status shown per-theme:
- Spinner while checking
- Green checkmark + "up-to-date" if all SHAs match
- Blue cloud + "update ready" if any file changed
### Security Scanning
On every import/update, downloaded CSS files are scanned server-side by PHP (`fv3_scan_css_warnings()` in `lib.php`) for:
- `url()` — external resource loading (could track users or load malicious content)
- `@import` — external stylesheet loading
- `expression()` — IE script execution
- `javascript:` — script execution
The scan happens **after download** on the Unraid server, not via the GitHub API. This is safe because CSS files on disk are inert — external URLs are only fetched when a browser renders the stylesheet. If external `url()` references are found, the progress dialog shows each flagged URL and prompts the user to **Keep** or **Delete** the theme. The CSS Tool's Advanced CSS textarea strips `@import`, `expression()`, and `javascript:` on save.
### PHP Endpoints (Theme Manager)
| Endpoint | Method | Purpose |
|----------|--------|---------|
| `list_themes.php` | GET | Scan styles dir, return themes with source data |
| `toggle_theme.php` | POST | Enable/disable (rename with `.disabled`), exclusive mode |
| `import_theme.php` | POST | Fetch from GitHub API, supports `path` param for subdirs |
| `delete_theme.php` | POST | Recursive delete, `_fv3-generated` protected |
### Full Backup
| Endpoint | Method | Purpose |
|----------|--------|---------|
| `export_all.php` | GET | Returns JSON bundle: docker/vm folders, settings, CSS config, custom styles |
| `import_all.php` | POST | Restores bundle with path traversal prevention |
Custom styles over 2MB are excluded from export with a warning. The bundle format:
```json
{
"fv3_export_version": 1,
"docker": {}, "vm": {}, "settings": {}, "css_config": {},
"custom_styles": { "theme-dir/file.css": "/* css content */" }
}
```
## Debug Mode
Type **fv3debug** on any FolderView3 page to toggle debug logging. When enabled, the browser console shows folder creation, API calls, organizer sync, and stats updates with `[FV3]` prefix. State persists in localStorage across page loads.