OverviewFilesCommitsIssuesDocsReleasesPeers

folio commits / fff9999

Initial setup: folio portfolio template
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fff99993b2e554ce59260cd8fffec0a31eaaf111
by plankatron <fleeky@gmail.com> · 2026-03-17T16:24:42.000Z

Changed files (2) +141 -0

.gitignore +2 -0
……@@ -0,0 +1,2 @@
1__pycache__/
2*.pyc
docs/plans/2026-03-17-portfolio-template-design.md +139 -0
……@@ -0,0 +1,139 @@
1# Portfolio Template — Design
2
3## Overview
4
5Generalize 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
91. **Config-driven** — `site.json` controls branding, colors, SEO, contact. No touching `index.php`.
102. **Video support** — custom dark-themed player with seeking, skip, buffering indicator, theater mode.
113. **Manifest-based discovery** — `process.py` generates `manifest.json` instead of runtime directory scanning.
124. **PHP server-side rendering** — markdown content rendered to HTML at request time. Perfect SEO, no loading flash.
135. **Reusable** — clone, edit config, swap content/media = new site.
14
15## File Structure
16
17```
18site-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
96Generated 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
127Custom 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