Web Template Theming
Overview
Re-skin and extend an existing static-site template instead of rebuilding it. The template supplies
layout, grid, animations and JS behaviour; this skill supplies a colour override layer, a content
swap, new sections, and interaction polish — all appended, so the original style.css /
script.js stay untouched and the change is reversible.
Core rule: override, never rewrite. Every visual change goes into one appended <style> block that
loads after the template's own CSS; every content change is a targeted string replacement, never a
hand-retyped block.
When to use
Use when the user supplies a template (HTML file, or a zip whose root holds a preview HTML plus
<name>_files/) and wants any of:
- a personal / topic / character / product home page built on that template
- a different colour scheme from the template's default
- extra sections (stats, timeline, video gallery, quote strip, relation grid) inserted into it
- local images or videos embedded into it
- the template's demo copy replaced with real content
When not to use
- The user wants a page built from scratch with no template → build it directly instead.
- The user only wants a small text edit → just edit the file.
- The template is a JS-framework SPA (React/Vue build output) → out of scope; only use this skill for
plain HTML + CSS/JS templates.
Core principles
- Append, don't rewrite. Add one
<style>block after the last template<link>and one<script>
after the last template<script>. Never editstyle.css/script.jsin place — the user may want
the original behaviour back, and in-place edits make diffs unreviewable. - Back up before touching. Copy the target HTML to
<name>.backup.htmlfirst. - Read the CSS before overriding. Grep
style.cssfor the class names you intend to restyle; the
override must match the template's real specificity or it silently loses. Note colours that appear in
pseudo-elements, gradients and vendor CSS (Bootstrap/MDB), not just in the main sheet. - Content must be sourced, not invented. For a real person / work / product, fetch the facts from an
authoritative reference (encyclopaedia entry, official site) and keep originals where the user supplied them. - Batch replacements go through a script with assertions. Replacing 16 links by hand breaks silently.
Usescripts/safe_replace.py, which asserts eacholdoccurs exactly the expected number of times. - Content must never depend on JS to be visible. Scroll-reveal / counter effects are enhancements;
if the script fails, every section must still be readable.
Workflow
Step 0 — Reconnaissance and backup
- If given a zip, extract to a scratch directory and list files. A 模板王-style download looks like:
<page>.html <- outer preview shell, NOT the template
<page>_files/ <- real template assets
index.html <- the actual template markup
style.css responsive.css ... <- theme sheets
*.js.下载 <- scripts, saved with a bogus extension
*.jpg / *.png
Read <page>_files/index.html to see the template's real structure; the outer HTML is only a wrapper.
2. Record: the CSS/JS file list, the asset folder name, and which classes the sections use.
3. Copy the working HTML to <name>.backup.html.
Step 1 — Map the template anatomy
Read the template's style.css and write down the selectors you will need. See
references/template-anatomy.md for a worked mapping of a Bootstrap + MDB + Owl Carousel portfolio
template (menu, tabs, cards, form, footer), including the class names and the traps in each.
At minimum locate and note:
- the nav / hamburger menu structure and its
.jsexpectations - one card component per content type (service / project / news / contact)
- the section wrapper class that controls background colour
- which colours come from the template vs. from vendor CSS
Step 2 — Prepare content
- Decide the theme (subject, tone, colour direction) with the user if not stated.
- Fetch factual content from an authoritative source; keep a section list so navigation can mirror it.
- Inventory the user's local media (images, videos) and rename to ASCII filenames where possible —
non-ASCII filenames insrcneed URL-encoding and are a common silent-failure source.
Step 3 — Inject the theme skin
- Copy or adapt
assets/theme-skin.css. It is a complete, working skin (dark ink × jade palette) built
for the template mapped inreferences/template-anatomy.md; rewrite its palette variables for the
user's theme and drop the sections that do not apply. - Insert it as a
<style>block immediately after the last template<link rel="stylesheet">and
before</head>. Usescripts/inject_theme.pyto do this deterministically:
bash
python scripts/inject_theme.py <html> <css-file> [--backup]
- Walk the override checklist in
references/theme-override-map.md— it lists every colour source that
a naive override misses (pseudo-element arrows, gradient stops, focus rings in vendor CSS, and
class-name collisions with Bootstrap utilities).
Step 4 — Insert new sections
- Pick snippets from
assets/snippets/, which are self-contained and only depend on classes defined in
assets/theme-skin.css:
| file | section | notes |
|---|---|---|
00-progress-bar.html |
top scroll progress bar | place right after <body> |
10-stats.html |
animated number strip | needs the counter JS in 90-enhance.js |
20-realm-timeline.html |
zig-zag timeline (odd/even nodes) | copy the node block per entry |
40-clips.html |
2×2 video gallery | see references/media-embed.md |
50-quote.html |
full-bleed pull-quote strip | check the class is not a Bootstrap utility |
60-relations.html |
relation-card grid | |
70-scroll-top.html |
back-to-top button | needs 90-enhance.js |
90-enhance.js |
progress bar + counters + reveal + video autopause | wrap in one IIFE, place after template scripts |
- Insert new sections at a boundary where the background colour already alternates correctly —
match the neighbouring section's background class so the seam is invisible. - Every new section must also be added to the nav submenu and the footer index, otherwise the page
looks half-finished. Do this in the same pass. - Strip the
data-page-node-idattributes and the original subject's copy when reusing a snippet.
Step 5 — Interaction enhancement
Use assets/snippets/90-enhance.js as the starting point. It bundles: scroll progress bar, requestIdle
counters, IntersectionObserver reveal, and video autopause. Three rules baked into it:
- Reveal must be opt-in at runtime: the script adds
.hl-pendingonly to elements below the fold, so
content is visible by default if the script never runs. Never shipopacity:0in the stylesheet. - Always add a timed fallback (
setTimeout~3s) that clears pending states and pins counters to their
target values — guarantees correctness even if IntersectionObserver misbehaves. - Wire the back-to-top button from JS and toggle a
.showclass; do not leave an inlinedisplay:block.
Step 6 — Batch content and link replacement
Replace demo copy / buttons with real content and links using scripts/safe_replace.py:
python scripts/safe_replace.py <html> replacement.tsv
The TSV is old<TAB>new<TAB>expected_count per line. The script asserts each old appears exactly
expected_count times and aborts otherwise, so a partially-applied edit is impossible. For external
links add target="_blank" rel="noopener noreferrer" and give them a distinct arrow (↗) vs. the
internal →.
Step 7 — Verify
Never deliver without looking at it. Capture screenshots with a headless Chromium/Edge —
scripts/shot.sh wraps the working command line for Windows/macOS/Linux, and
references/verification-headless.md documents the flags, the required path style, and the known
gotchas (path conversion, profile lock, screenshot height limit, how to scroll to a given section).
Check at minimum: 1440-wide desktop full page, 767-wide mobile, and one shot per newly added section.
Compare against the backup to confirm every change landed.
Step 8 — Deliver
- Keep the new page beside the template's asset folder so relative paths resolve.
- Report the concrete file paths, which template constructs were overridden, which sections were added,
and anything left unverified.
Pitfalls
These each cost real debugging time; check them proactively.
- Font-icon fonts often do not exist in a scraped template — Font Awesome glyphs render as empty
boxes (social icons, card arrows, back-to-top). Replace every::before/<i class="fa ...">with an
inline SVG or a CSScontent:"\2192". Grep the asset folder forwoff/ttf/eotfirst. - Class-name collisions with the framework. A custom
.markcollides with Bootstrap's.mark
(which paints a cream background); likewise.box,.title,.post,.pointer. Prefix every custom
class (e.g.hl-) and grep the vendor CSS for the name before using it. - Override specificity. Bootstrap/MDB rules with
!importantor deeper selectors beat a plain
.foo{color:x}in your appended sheet. Match with the same selector and add!importantwhere the
template does. - Non-ASCII filenames in
srcsilently 404. Rename assets to ASCII, or URL-encode. - Hiding content for animation. See Step 5 — never default to
opacity:0in CSS. - Broken menu JS. Hamburger menus often dereference child
uls; deleting a.blog/.contact
class or its<ul>throws and kills the whole menu. Keep the class names, change only the labels. - Timeline/zig-zag layouts need alternating markup, not CSS alone — mirror the node block, and reset
the alternation in the mobile breakpoint. - Fixed-size screenshot ceilings. Headless screenshots fail above roughly 1440×2000 on some builds.
Resources
references/template-anatomy.md— worked mapping of the Bootstrap/MDB/Owl portfolio template that
this skill was built from: class inventory, JS expectations, and what each section is made of.references/theme-override-map.md— checklist of every colour source a re-skin must cover, plus the
palette variables used byassets/theme-skin.cssand the vendor-class collisions to avoid.references/verification-headless.md— headless screenshot recipes and gotchas, plus how to shoot a
specific section by offsetting the body.references/media-embed.md— embedding local images and videos: aspect-ratio normalisation, native
controls, decorative play overlays, autopause wiring, and reading MP4 duration/resolution with no
ffmpeg installed.assets/theme-skin.css— a complete, working dark theme skin. Template it: rewrite the:root
variables, delete unused sections.assets/snippets/— drop-in HTML/JS for the added sections listed in Step 4.scripts/inject_theme.py— insert a CSS file as a<style>block after the last stylesheet link.scripts/safe_replace.py— count-asserted batch find/replace, driven by a TSV.scripts/probe_mp4.py— read MP4 duration and resolution by parsing boxes directly (no ffmpeg).scripts/shot.sh— headless-browser screenshot helper.