Skip to content

Rotate coin-face images from folder, enforce ≥2 headshots, add pre-deploy validation#16

Open
brianruggieri with Copilot wants to merge 5 commits into
mainfrom
copilot/enable-image-rotation-on-coin-flip
Open

Rotate coin-face images from folder, enforce ≥2 headshots, add pre-deploy validation#16
brianruggieri with Copilot wants to merge 5 commits into
mainfrom
copilot/enable-image-rotation-on-coin-flip

Conversation

Copilot AI commented Mar 11, 2026

Copy link
Copy Markdown
Contributor

The coin flip was hardcoded to toggle between exactly 2 static images. This converts it to dynamically discover images from a static/img/coin-faces/ folder, rotate randomly on each flip (never repeating back-to-back), and enforce a minimum of 2 headshots via a CI gate.

Image discovery at build time

  • Hugo readDir scans static/img/coin-faces/ for .png/.jpg/.jpeg files, passes the list as data-coin-images JSON attribute on both desktop and mobile coin elements
  • Moved me-photo.{png,webp} into coin-faces/, added me-alt.{png,webp} as a second headshot

No-repeat rotation in JS

  • pickRandomImage(excludePath) guarantees a different image on every back-face reveal via do-while exclusion
  • Back face image swaps while hidden (after flip-to-front completes), so the next reveal is always fresh
  • console.warn if < 2 images detected (degraded experience)

Pre-deploy validation

  • scripts/validate-coin-faces.sh — counts source images (png/jpg/jpeg, not webp companions), exits non-zero if < 2
  • Added as a CI step in deploy.yml before hugo --minify, blocking deploy on violation
  • Script is also usable as a local git hook
# .github/workflows/deploy.yml
- name: Validate coin-faces (≥2 headshots required)
  run: ./scripts/validate-coin-faces.sh

Template MIME type fix

  • Back-face image-set() now uses conditional blocks for jpeg vs png MIME type instead of a template variable (avoids Hugo minifier CSS-escaping image/pngimage\2fpng)

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 2 commits March 11, 2026 14:00
- Create static/img/coin-faces/ folder for coin face images
- Move me-photo.png/webp to coin-faces/ folder
- Update nav.html to auto-discover images via Hugo readDir
- Pass image list as data-coin-images JSON attribute
- Update coin-flip.js to randomly rotate back face images
- Each flip to the illustration triggers a new random photo swap

Co-authored-by: brianruggieri <2104689+brianruggieri@users.noreply.github.com>
… check

Co-authored-by: brianruggieri <2104689+brianruggieri@users.noreply.github.com>
Copilot AI changed the title [WIP] Enable rotating images for coin flip feature Rotate coin face images from a folder instead of static 2-image flip Mar 11, 2026
@brianruggieri
brianruggieri marked this pull request as ready for review March 11, 2026 14:52
@brianruggieri
brianruggieri requested a review from Copilot March 11, 2026 14:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the site’s “profile coin” to rotate through a build-time discovered set of face images (from static/img/coin-faces/) instead of relying on a single hardcoded back image, with JS selecting/swapping the back face on flips.

Changes:

  • Add Hugo build-time discovery of coin face images and expose them via a data-coin-images attribute on both coin elements.
  • Update coin-flip.js to parse the image list, randomize the initial back face, and rotate to a new back face after flips.
  • Add/move coin face image assets into static/img/coin-faces/.

Reviewed changes

Copilot reviewed 2 out of 4 changed files in this pull request and generated 5 comments.

File Description
static/js/coin-flip.js Parse data-coin-images, randomize initial back image, and rotate back-face images across flips.
layouts/partials/nav.html Build-time readDir of static/img/coin-faces and emit discovered image list + initial back-face style.
static/img/coin-faces/me-photo.webp Adds a WebP coin face asset in the new folder.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread static/js/coin-flip.js
Comment on lines +13 to +16
// Coin face image rotation — images discovered at build time by Hugo
const coinImages = JSON.parse(coin?.dataset?.coinImages || '[]');
let currentBackImage = coinImages.length > 0 ? coinImages[0] : null;

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR adds new behavior (parsing data-coin-images, randomizing the back face on load, and swapping it after flips), but the existing Puppeteer tests don’t appear to assert any of it. Consider adding an e2e/visual test that validates the data-coin-images JSON and that the .coin-back background-image changes after flipping back to the front (and remains stable for the single-image case).

Copilot uses AI. Check for mistakes.
Comment thread layouts/partials/nav.html
<div id="mobileCoin" class="mobile-brand__coin" data-coin-images='{{ $coinFaces | jsonify }}'>
<div class="coin-side coin-front" style="background-image: image-set(url('{{ $frontWebp }}') type('image/webp'), url('{{ $frontImg }}') type('image/png'))"></div>
<div class="coin-side coin-back" style="background-image: image-set(url('/img/me-photo.webp') type('image/webp'), url('/img/me-photo.png') type('image/png'))"></div>
<div class="coin-side coin-back" style="background-image: image-set(url('{{ $backWebp }}') type('image/webp'), url('{{ $backImg }}') type('image/png'))"></div>

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The back-face inline style always uses type('image/png'), but $backImg can be .jpg/.jpeg (since those are included in $coinFaces). This makes the declared MIME type incorrect and can cause browsers to ignore that source in image-set(). Consider deriving the correct type from the extension (or omit the type(...) for the fallback URL).

Copilot uses AI. Check for mistakes.
Comment thread layouts/partials/nav.html
<div id="profileCoin" class="mx-auto mb-2" data-coin-images='{{ $coinFaces | jsonify }}'>
<div class="coin-side coin-front" style="background-image: image-set(url('{{ $frontWebp }}') type('image/webp'), url('{{ $frontImg }}') type('image/png'))"></div>
<div class="coin-side coin-back" style="background-image: image-set(url('/img/me-photo.webp') type('image/webp'), url('/img/me-photo.png') type('image/png'))"></div>
<div class="coin-side coin-back" style="background-image: image-set(url('{{ $backWebp }}') type('image/webp'), url('{{ $backImg }}') type('image/png'))"></div>

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The back-face inline style always uses type('image/png'), but $backImg can be .jpg/.jpeg (since those are included in $coinFaces). This makes the declared MIME type incorrect and can cause browsers to ignore that source in image-set(). Consider deriving the correct type from the extension (or omit the type(...) for the fallback URL).

Copilot uses AI. Check for mistakes.
Comment thread layouts/partials/nav.html
Comment on lines +14 to +18
{{ $backImg := "" }}
{{ if gt (len $coinFaces) 0 }}
{{ $backImg = index $coinFaces 0 }}
{{ end }}
{{ $backWebp := $backImg | replaceRE `\.(png|jpg|jpeg)$` ".webp" }}

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When static/img/coin-faces/ is empty, $backImg stays "" and $backWebp is also empty. The generated inline image-set(url('')...) can resolve to the current document URL and trigger an unintended request / broken background. Consider conditionally emitting the background-image only when a back image exists (or providing a safe placeholder).

Copilot uses AI. Check for mistakes.
Comment thread static/js/coin-flip.js
*/
function setFaceImage(face, imgPath) {
if (!face || !imgPath) return;
const webpPath = imgPath.replace(/\.(png|jpg|jpeg)$/, '.webp');

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setFaceImage() derives webpPath via a case-sensitive \.(png|jpg|jpeg)$ replace. Because the Hugo template includes files by checking a lowercased name, filenames like ME.PNG would be accepted but the .webp path would not be generated correctly here. Consider making the replace regex case-insensitive (and/or normalizing the emitted paths) so mixed-case extensions work reliably.

Suggested change
const webpPath = imgPath.replace(/\.(png|jpg|jpeg)$/, '.webp');
const webpPath = imgPath.replace(/\.(png|jpg|jpeg)$/i, '.webp');

Copilot uses AI. Check for mistakes.
Copilot AI and others added 2 commits March 11, 2026 15:07
- Add second headshot (me-alt.png/webp) to coin-faces folder
- Add scripts/validate-coin-faces.sh to verify ≥2 images exist
- Add validation step to deploy workflow before Hugo build
- Add console.warn in coin-flip.js when fewer than 2 images

Co-authored-by: brianruggieri <2104689+brianruggieri@users.noreply.github.com>
…itespace in validation script

Co-authored-by: brianruggieri <2104689+brianruggieri@users.noreply.github.com>
Copilot AI changed the title Rotate coin face images from a folder instead of static 2-image flip Rotate coin-face images from folder, enforce ≥2 headshots, add pre-deploy validation Mar 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants