| … | … | @@ -0,0 +1,139 @@ |
| 1 | # Portfolio Template — Design |
| 2 | |
| 3 | ## Overview |
| 4 | |
| 5 | Generalize the Ashes Monroe portfolio site into a reusable, config-driven template. No code changes to rebrand — edit `site.json` + content files + media. Add video support with a custom player, proper seeking, and theater mode. Deploy as static files + PHP on Bluehost (or any Apache/nginx host with PHP). |
| 6 | |
| 7 | ## Goals |
| 8 | |
| 9 | 1. **Config-driven** — `site.json` controls branding, colors, SEO, contact. No touching `index.php`. |
| 10 | 2. **Video support** — custom dark-themed player with seeking, skip, buffering indicator, theater mode. |
| 11 | 3. **Manifest-based discovery** — `process.py` generates `manifest.json` instead of runtime directory scanning. |
| 12 | 4. **PHP server-side rendering** — markdown content rendered to HTML at request time. Perfect SEO, no loading flash. |
| 13 | 5. **Reusable** — clone, edit config, swap content/media = new site. |
| 14 | |
| 15 | ## File Structure |
| 16 | |
| 17 | ``` |
| 18 | site-name/ |
| 19 | ├── index.php # the entire site (PHP template + CSS + JS) |
| 20 | ├── site.json # all config (name, colors, contact, SEO) |
| 21 | ├── manifest.json # generated by process.py (all media + metadata) |
| 22 | ├── content/ |
| 23 | │ ├── about.md # artist statement / bio |
| 24 | │ └── collections/ |
| 25 | │ └── aerials.md # optional per-collection description |
| 26 | ├── images/ |
| 27 | │ ├── collection-name/ |
| 28 | │ │ ├── photo.jpg # full-res (1500px longest edge) |
| 29 | │ │ └── thumbs/ |
| 30 | │ │ └── photo.jpg # thumbnail (400px longest edge) |
| 31 | ├── videos/ |
| 32 | │ ├── collection-name/ |
| 33 | │ │ ├── clip.mp4 # faststart MP4 (h264 + aac) |
| 34 | │ │ └── thumbs/ |
| 35 | │ │ └── clip.jpg # poster frame thumbnail |
| 36 | ├── process.py # preprocesses media + generates manifest.json |
| 37 | ├── rename.py # rename utility (images, collections) |
| 38 | ├── logo.png # site logo |
| 39 | └── content/ |
| 40 | └── about.md # long-form text content |
| 41 | ``` |
| 42 | |
| 43 | ## site.json — Config |
| 44 | |
| 45 | ```json |
| 46 | { |
| 47 | "name": "Site Name", |
| 48 | "tagline": "Tagline", |
| 49 | "url": "https://example.com", |
| 50 | "logo": "logo.png", |
| 51 | "email": "email@example.com", |
| 52 | "location": "City, State", |
| 53 | "social": { |
| 54 | "instagram": "https://instagram.com/handle" |
| 55 | }, |
| 56 | "seo": { |
| 57 | "description": "...", |
| 58 | "keywords": "...", |
| 59 | "og_description": "..." |
| 60 | }, |
| 61 | "structured_data": { |
| 62 | "type": "Person", |
| 63 | "jobTitle": "Artist", |
| 64 | "description": "...", |
| 65 | "knowsAbout": [] |
| 66 | }, |
| 67 | "theme": { |
| 68 | "background": "#000", |
| 69 | "text": "#ccc", |
| 70 | "accent": "#fff", |
| 71 | "subtle": "#666", |
| 72 | "border": "#222" |
| 73 | }, |
| 74 | "about": { |
| 75 | "image": "images/collection/photo.jpg", |
| 76 | "signature": "Name, Date" |
| 77 | }, |
| 78 | "contact": { |
| 79 | "note": "Contact note text." |
| 80 | }, |
| 81 | "processing": { |
| 82 | "thumbnail": 400, |
| 83 | "fullsize": 1500, |
| 84 | "quality": 85, |
| 85 | "video_thumbnail_time": 2 |
| 86 | }, |
| 87 | "collections": { |
| 88 | "order": [], |
| 89 | "ignore": ["old", "_archive"] |
| 90 | } |
| 91 | } |
| 92 | ``` |
| 93 | |
| 94 | ## manifest.json — Generated Media Index |
| 95 | |
| 96 | Generated by `process.py`, never hand-edited: |
| 97 | |
| 98 | ```json |
| 99 | { |
| 100 | "collections": ["collection-a", "collection-b"], |
| 101 | "items": [ |
| 102 | { |
| 103 | "collection": "collection-a", |
| 104 | "name": "photo.jpg", |
| 105 | "type": "image", |
| 106 | "thumb": "images/collection-a/thumbs/photo.jpg", |
| 107 | "full": "images/collection-a/photo.jpg" |
| 108 | }, |
| 109 | { |
| 110 | "collection": "collection-a", |
| 111 | "name": "clip.mp4", |
| 112 | "type": "video", |
| 113 | "thumb": "videos/collection-a/thumbs/clip.jpg", |
| 114 | "full": "videos/collection-a/clip.mp4", |
| 115 | "duration": 184 |
| 116 | } |
| 117 | ] |
| 118 | } |
| 119 | ``` |
| 120 | |
| 121 | ## PHP Template — Server-Side Rendering |
| 122 | |
| 123 | `index.php` reads config + content at request time. PHP handles SEO, markdown rendering, theme injection. JS handles gallery interaction only. |
| 124 | |
| 125 | ## Video Player |
| 126 | |
| 127 | Custom dark-themed player: progress bar with buffer display, skip ±10s, time display, volume, fullscreen, theater mode, buffering spinner. MP4 faststart + Apache range requests for seeking. |
| 128 | |
| 129 | ## Preprocessing |
| 130 | |
| 131 | `process.py` handles images (resize, thumbnails) and videos (faststart, transcode, poster frames). Generates `manifest.json` after every run. |
| 132 | |
| 133 | ## Constraints |
| 134 | |
| 135 | - Zero external JS/CSS dependencies |
| 136 | - Single `index.php` file (CSS and JS inline) |
| 137 | - PHP only for reading config + rendering markdown |
| 138 | - Works on any Apache/nginx host with PHP |
| 139 | - ffmpeg required locally for video preprocessing |