{MASCOT_NAME} Mascot Image Test
This page tests every production mascot PNG in two ways:
- Each image is drawn over checkerboard and dark backgrounds so opaque boxes, halos, and edge contamination are easy to spot.
- A browser-side pixel test checks for an RGBA transparency region, transparent
corners, and the 4 px content border produced by
trim-padding-from-image.py(using the script's alpha threshold of 10).
Mascot admonitions
!!! mascot-neutral "General Note" {MASCOT_NAME} neutral pose{ class="mascot-admonition-img" } This is the neutral style, used for general sidebars or introductions.
!!! mascot-welcome "Welcome!" {MASCOT_NAME} waving welcome{ class="mascot-admonition-img" } This is the welcome style, used at chapter openings.
!!! mascot-thinking "Key Insight" {MASCOT_NAME} thinking{ class="mascot-admonition-img" } This is the thinking style, used for key concepts.
!!! mascot-tip "Helpful Tip" {MASCOT_NAME} giving a tip{ class="mascot-admonition-img" } This is the tip style, used for hints and advice.
!!! mascot-warning "Watch Out!" {MASCOT_NAME} warning{ class="mascot-admonition-img" } This is the warning style, used for common mistakes.
!!! mascot-encourage "Keep Going!" {MASCOT_NAME} encouraging{ class="mascot-admonition-img" } This is the encouraging style, used for difficult content.
!!! mascot-celebration "Well Done!" {MASCOT_NAME} celebrating{ class="mascot-admonition-img" } This is the celebration style, used for achievements.
<style> .mascot-test-summary { margin: 1rem 0; padding: .75rem 1rem; border: 2px solid #546e7a; border-radius: .4rem; font-weight: 700; } .mascot-test-summary.pass { border-color: #2e7d32; background: #e8f5e9; color: #1b5e20; } .mascot-test-summary.fail { border-color: #c62828; background: #ffebee; color: #b71c1c; } .mascot-test-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); gap: 1rem; } .mascot-test-card { margin: 0; padding: .8rem; border: 2px solid #90a4ae; border-radius: .5rem; } .mascot-test-card.pass { border-color: #43a047; } .mascot-test-card.fail { border-color: #e53935; } .mascot-test-card h2 { margin: 0 0 .6rem; } .mascot-test-swatches { display: grid; grid-template-columns: 1fr 1fr; gap: .5rem; } .mascot-test-swatch { display: grid; place-items: center; min-height: 13rem; padding: .5rem; overflow: hidden; border: 1px solid rgba(127, 127, 127, .5); } .mascot-test-swatch.checker { background-color: #fff; background-image: linear-gradient(45deg, #cfd8dc 25%, transparent 25%), linear-gradient(-45deg, #cfd8dc 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #cfd8dc 75%), linear-gradient(-45deg, transparent 75%, #cfd8dc 75%); background-position: 0 0, 0 8px, 8px -8px, -8px 0; background-size: 16px 16px; } .mascot-test-swatch.dark { background: #17131f; } .mascot-test-swatch img { display: block; width: 100%; height: 12rem; object-fit: contain; } .mascot-test-result { margin: .7rem 0 0; font-size: .82rem; line-height: 1.45; } .mascot-test-result strong { display: inline-block; margin-right: .25rem; } </style><script> (() => { const threshold = 10; const expectedBorder = 4; const cards = [...document.querySelectorAll('.mascot-test-card[data-src]')]; const summary = document.getElementById('mascot-test-summary'); function inspect(card) { return new Promise((resolve) => { const image = new Image(); image.onload = () => { const canvas = document.createElement('canvas'); canvas.width = image.naturalWidth; canvas.height = image.naturalHeight; const context = canvas.getContext('2d', { willReadFrequently: true }); context.drawImage(image, 0, 0); const pixels = context.getImageData(0, 0, canvas.width, canvas.height).data; let minX = canvas.width; let minY = canvas.height; let maxX = -1; let maxY = -1; let transparent = 0; for (let y = 0; y < canvas.height; y += 1) { for (let x = 0; x < canvas.width; x += 1) { const alpha = pixels[((y * canvas.width + x) * 4) + 3]; if (alpha === 0) transparent += 1; if (alpha > threshold) { minX = Math.min(minX, x); minY = Math.min(minY, y); maxX = Math.max(maxX, x); maxY = Math.max(maxY, y); } } } const cornerOffsets = [ 3, ((canvas.width - 1) * 4) + 3, (((canvas.height - 1) * canvas.width) * 4) + 3, ((((canvas.height - 1) * canvas.width) + canvas.width - 1) * 4) + 3 ]; const cornersTransparent = cornerOffsets.every((offset) => pixels[offset] === 0); const margins = [minX, minY, canvas.width - 1 - maxX, canvas.height - 1 - maxY]; const trimPass = maxX >= 0 && margins.every((margin) => margin === expectedBorder); const alphaPass = transparent > 0 && cornersTransparent; const pass = alphaPass && trimPass; const result = card.querySelector('.mascot-test-result'); card.classList.add(pass ? 'pass' : 'fail'); result.innerHTML = `<strong>${pass ? 'PASS' : 'FAIL'}</strong> ` + `${canvas.width}×${canvas.height}px · RGBA transparency: ${alphaPass ? 'pass' : 'fail'} · ` + `content margins L/T/R/B: ${margins.join('/')}px (${trimPass ? 'pass' : 'expected 4/4/4/4'})`; resolve(pass); }; image.onerror = () => { card.classList.add('fail'); card.querySelector('.mascot-test-result').innerHTML = '<strong>FAIL</strong> Image could not be loaded.'; resolve(false); }; image.src = card.dataset.src; }); } Promise.all(cards.map(inspect)).then((results) => { const passed = results.filter(Boolean).length; const allPass = passed === results.length; summary.classList.add(allPass ? 'pass' : 'fail'); summary.textContent = allPass ? `PASS — all ${passed} mascot PNGs have transparency and the expected 4 px trim border.` : `FAIL — ${passed} of ${results.length} mascot PNGs passed. Review the failed cards below.`; }); })(); </script>