feat(badges): static RGB gradient for punch holes on print, cycling on screen

Each rainbow-enabled slot now has a companion print-only div with an inline
SVG linearGradient (R→Y→G→C→B→M spectrum). On screen: the animated
hue-rotate div cycles. On print: CSS hides the animated div and shows the
static gradient instead. X lines print in semi-transparent black over the
gradient fill for visibility.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Scott Idem
2026-06-04 20:28:56 -04:00
parent 35c1324824
commit ba4a0dc828

View File

@@ -710,6 +710,18 @@ const code_to_icon: {
<line x1="50" y1="0" x2="0" y2="8" stroke={punch_holes_colors.left.fg} stroke-width="1"/> <line x1="50" y1="0" x2="0" y2="8" stroke={punch_holes_colors.left.fg} stroke-width="1"/>
</svg> </svg>
</div> </div>
{#if punch_holes_left_rainbow}
<div class="punch_hole_marker punch_hole_print_rgb pointer-events-none absolute"
aria-hidden="true"
style="top: calc(0.25in + 1mm); left: calc(0.375in + 1mm); width: calc(0.625in - 2mm); height: calc(0.125in - 2mm); z-index: 10;">
<svg width="100%" height="100%" viewBox="0 0 50 8" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
<defs><linearGradient id="ph_rg_left" x1="0%" y1="0%" x2="100%" y2="0%"><stop offset="0%" stop-color="#ff0000"/><stop offset="20%" stop-color="#ffff00"/><stop offset="40%" stop-color="#00ff00"/><stop offset="60%" stop-color="#00ffff"/><stop offset="80%" stop-color="#0000ff"/><stop offset="100%" stop-color="#ff00ff"/></linearGradient></defs>
<rect x="0.5" y="0.5" width="49" height="7" fill="url(#ph_rg_left)" stroke="url(#ph_rg_left)" stroke-width="1" stroke-dasharray="4,2"/>
<line x1="0" y1="0" x2="50" y2="8" stroke="#000" stroke-width="1" stroke-opacity="0.45"/>
<line x1="50" y1="0" x2="0" y2="8" stroke="#000" stroke-width="1" stroke-opacity="0.45"/>
</svg>
</div>
{/if}
{/if} {/if}
{#if punch_holes_right} {#if punch_holes_right}
<div class="punch_hole_marker pointer-events-none absolute" <div class="punch_hole_marker pointer-events-none absolute"
@@ -722,6 +734,18 @@ const code_to_icon: {
<line x1="50" y1="0" x2="0" y2="8" stroke={punch_holes_colors.right.fg} stroke-width="1"/> <line x1="50" y1="0" x2="0" y2="8" stroke={punch_holes_colors.right.fg} stroke-width="1"/>
</svg> </svg>
</div> </div>
{#if punch_holes_right_rainbow}
<div class="punch_hole_marker punch_hole_print_rgb pointer-events-none absolute"
aria-hidden="true"
style="top: calc(0.25in + 1mm); right: calc(0.375in + 1mm); width: calc(0.625in - 2mm); height: calc(0.125in - 2mm); z-index: 10;">
<svg width="100%" height="100%" viewBox="0 0 50 8" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
<defs><linearGradient id="ph_rg_right" x1="0%" y1="0%" x2="100%" y2="0%"><stop offset="0%" stop-color="#ff0000"/><stop offset="20%" stop-color="#ffff00"/><stop offset="40%" stop-color="#00ff00"/><stop offset="60%" stop-color="#00ffff"/><stop offset="80%" stop-color="#0000ff"/><stop offset="100%" stop-color="#ff00ff"/></linearGradient></defs>
<rect x="0.5" y="0.5" width="49" height="7" fill="url(#ph_rg_right)" stroke="url(#ph_rg_right)" stroke-width="1" stroke-dasharray="4,2"/>
<line x1="0" y1="0" x2="50" y2="8" stroke="#000" stroke-width="1" stroke-opacity="0.45"/>
<line x1="50" y1="0" x2="0" y2="8" stroke="#000" stroke-width="1" stroke-opacity="0.45"/>
</svg>
</div>
{/if}
{/if} {/if}
{#if punch_holes_center} {#if punch_holes_center}
<div class="punch_hole_marker pointer-events-none absolute" <div class="punch_hole_marker pointer-events-none absolute"
@@ -734,6 +758,18 @@ const code_to_icon: {
<line x1="50" y1="0" x2="0" y2="8" stroke={punch_holes_colors.center.fg} stroke-width="1"/> <line x1="50" y1="0" x2="0" y2="8" stroke={punch_holes_colors.center.fg} stroke-width="1"/>
</svg> </svg>
</div> </div>
{#if punch_holes_center_rainbow}
<div class="punch_hole_marker punch_hole_print_rgb pointer-events-none absolute"
aria-hidden="true"
style="top: calc(0.25in + 1mm); left: 50%; transform: translateX(-50%); width: calc(0.625in - 2mm); height: calc(0.125in - 2mm); z-index: 10;">
<svg width="100%" height="100%" viewBox="0 0 50 8" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
<defs><linearGradient id="ph_rg_center" x1="0%" y1="0%" x2="100%" y2="0%"><stop offset="0%" stop-color="#ff0000"/><stop offset="20%" stop-color="#ffff00"/><stop offset="40%" stop-color="#00ff00"/><stop offset="60%" stop-color="#00ffff"/><stop offset="80%" stop-color="#0000ff"/><stop offset="100%" stop-color="#ff00ff"/></linearGradient></defs>
<rect x="0.5" y="0.5" width="49" height="7" fill="url(#ph_rg_center)" stroke="url(#ph_rg_center)" stroke-width="1" stroke-dasharray="4,2"/>
<line x1="0" y1="0" x2="50" y2="8" stroke="#000" stroke-width="1" stroke-opacity="0.45"/>
<line x1="50" y1="0" x2="0" y2="8" stroke="#000" stroke-width="1" stroke-opacity="0.45"/>
</svg>
</div>
{/if}
{/if} {/if}
<!-- Background image bleed container. <!-- Background image bleed container.
@@ -1336,10 +1372,12 @@ const code_to_icon: {
<style> <style>
/* /*
* Punch-hole rainbow animation: continuously rotates the hue of the marker div. * Punch-hole rainbow: animated hue-rotate on screen; static SVG gradient on print.
* A saturated base color (PH_RAINBOW_FG) is used so the full visible spectrum appears. * .punch_hole_rainbow — screen display, cycling animation
* On print the animation freezes at whatever frame the browser captures — this is fine, * .punch_hole_print_rgb — print-only companion div with built-in linearGradient SVG
* the marker still shows in some vivid color and remains within the hole boundary. *
* On print: the animated div is hidden; the gradient div appears in its place.
* The gradient is defined inside each SVG so it's self-contained per slot.
*/ */
@keyframes ae_punch_hole_rainbow { @keyframes ae_punch_hole_rainbow {
from { filter: hue-rotate(0deg); } from { filter: hue-rotate(0deg); }
@@ -1348,6 +1386,13 @@ const code_to_icon: {
.punch_hole_rainbow { .punch_hole_rainbow {
animation: ae_punch_hole_rainbow 2.5s linear infinite; animation: ae_punch_hole_rainbow 2.5s linear infinite;
} }
.punch_hole_print_rgb {
display: none;
}
@media print {
.punch_hole_rainbow { display: none; }
.punch_hole_print_rgb { display: block; }
}
/* /*
* Force light-mode rendering on the badge print area. * Force light-mode rendering on the badge print area.