Graphic Testimonials
Makes the images. Takes quote + name + role (+ optional face) per client and produces branded square cards wired into a site. Fully standalone — all brand inputs come from a config, not from this skill.
Inputs (collect before starting)
Check the project for a brand.json (schema: references/brand-config.md). If none, ask the user for:
- Template: a Canva design ID of a single-card template they own, OR use
assets/card-template.html(no Canva needed). - Brand color for the card background (hex), plus text/accent/ring colors.
- Site target: HTML file, grid CSS class, image naming, git repo if any.
- Copy rules: banned words, tone, section heading.
Card source A — Canva (preferred when the user has a template)
copy-design(usepage_numbersfor one card). NEVER edit the user's original.start-editing-transaction→perform-editing-operations→commit-editing-transaction:- Read element IDs from the transaction response; never guess. Store stable suffixes in
brand.json. - Fixed pages:
replace_text. Responsive pages:find_and_replace_textONLY. - Before deleting/replacing any photo, call
get-assetson every fill asset ID and view thumbnails — stacked shapes can hide stock photos behind client photos. update_titletoTestimonial — <Name>; rename unusable cardsDO NOT USE — <reason>instead of deleting.
- Read element IDs from the transaction response; never guess. Store stable suffixes in
export-designpngpro; download the signed URL with curl.
Card source B — standalone HTML template (no Canva required)
assets/card-template.html renders a 1080×1080 card (stars, heading, quote, script name, role, optional circular photo) driven by CSS variables and a window.CARD object (fields documented in the file header). Render:
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --headless --disable-gpu \
--screenshot=card.png --window-size=1080,1080 --hide-scrollbars file:///abs/path/card.html
Substitute the local Chrome/Chromium binary on other platforms.
Post-process (Canva-source cards)
- Recolor background:
python3 scripts/recolor_bg.py <png>... --target "#1B3A5C"(corner-samples bg, blendsw=clip((90-dist)/60)to preserve anti-aliasing). - Circular photo:
python3 scripts/circle_face.py <card.png> <face.png> [--left 420 --top 55 --size 240 --bg "#1B3A5C" --ring "#F1EFE9"]. Canva's API cannot insert local files (upload-asset-from-urlrequires an ALREADY-public URL — never public-host local files), so faces are baked into exported PNGs only. Warn the user: re-exporting from Canva loses the face; re-run this step after any Canva text edit.
Wire in
Add <div class="asset-wrap"><img src="./card-N.png" alt="Name, role: short quote"></div> to the page grid. Rank cards by relevance to the offer — put the ones whose stories match what is being sold first; overflow goes under a "More Client Wins"-style subheading. Verify every src exists on disk, then git commit + push if a repo exists.
Hard rules
- Real, verifiable clients only — every name/quote traceable to a source.
- Version files; never overwrite user originals.