fix(badges): shrink punch-hole markers horizontally, add inset_x_mm config

Default horizontal inset increased from 1mm to 2mm per side (width shrinks
by 4mm total) so markers stay inside the physical hole slot on all printers
and badge stock with minor registration variance. Vertical stays at 1mm.

The inset is now driven by punch_holes_inset_x (template_cfg.punch_holes
.inset_x_mm, default 2) so it can be tuned per template without a code
change. Added inset_x_mm field to AeBadgeTemplateCfg type with doc comment.

All 9 slot marker divs (6 rainbow + 3 solid) updated via replace_all.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Scott Idem
2026-06-08 20:07:59 -04:00
parent 7c5cf53106
commit 955d28d9c5
2 changed files with 22 additions and 10 deletions

View File

@@ -76,6 +76,10 @@ export interface BadgeTemplateCfg {
center_bg?: string;
center_rainbow?: boolean;
slow_pulse?: boolean; // when true: slow breathing pulse instead of fast linear cycle
// Extra horizontal inset per side (mm) beyond the 1mm base safety margin.
// Shrinks the visible marker width to keep it inside the physical hole on
// printers or badge stock with variance. Default 2 when unset (see view component).
inset_x_mm?: number;
};
// Allow arbitrary extra keys to preserve forward-compatibility.

View File

@@ -332,6 +332,11 @@ let punch_holes_left = $derived(!!template_cfg?.punch_holes?.left);
let punch_holes_right = $derived(!!template_cfg?.punch_holes?.right);
let punch_holes_center = $derived(!!template_cfg?.punch_holes?.center);
// Horizontal inset per side applied to each marker, in mm.
// Default 2mm — enough to stay inside the hole on real badge stock with typical printer variance.
// Increase via punch_holes.inset_x_mm in the template config if a printer or stock runs long.
let punch_holes_inset_x = $derived(template_cfg?.punch_holes?.inset_x_mm ?? 2);
// Per-slot colors: slot-specific override → shared fallback → component default.
// When rainbow is on and no explicit fg/bg is set, use a saturated base color so
// hue-rotate produces a full visible spectrum (gray has no hue to rotate).
@@ -697,7 +702,10 @@ const code_to_icon: {
<!-- Punch-out hole markers: X overlays at physical badge clip slot positions.
Slots: 5/8in wide × 1/8in tall, 1/4in from top, 3/8in from left/right edges.
z-index: 10 keeps markers above the header image regardless of DOM order.
Inset ~1mm on all sides — safety margin for printer registration variance.
Horizontal inset = punch_holes_inset_x mm per side (default 2mm) — keeps the
marker inside the physical hole across printers and badge stock with variance.
Vertical inset is fixed at 1mm (top and bottom). Configurable per template via
punch_holes.inset_x_mm in the template cfg_json.
Rainbow sync: rainbow markers share ONE wrapper div with the hue-rotate animation.
A single CSS animation on the wrapper drives all children simultaneously — no
@@ -712,7 +720,7 @@ const code_to_icon: {
class:punch_hole_rainbow_left={!punch_holes_slow_pulse}
class:punch_hole_pulse_left={punch_holes_slow_pulse}
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;">
style="top: calc(0.25in + 1mm); left: calc(0.375in + {punch_holes_inset_x}mm); width: calc(0.625in - {punch_holes_inset_x * 2}mm); 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">
<rect x="0.5" y="0.5" width="49" height="7" fill={punch_holes_colors.left.bg} stroke={punch_holes_colors.left.fg} stroke-width="1" stroke-dasharray="4,2"/>
<line x1="0" y1="0" x2="50" y2="8" stroke={punch_holes_colors.left.fg} stroke-width="1"/>
@@ -721,7 +729,7 @@ const code_to_icon: {
</div>
<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;">
style="top: calc(0.25in + 1mm); left: calc(0.375in + {punch_holes_inset_x}mm); width: calc(0.625in - {punch_holes_inset_x * 2}mm); 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"/>
@@ -735,7 +743,7 @@ const code_to_icon: {
class:punch_hole_rainbow_right={!punch_holes_slow_pulse}
class:punch_hole_pulse_right={punch_holes_slow_pulse}
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;">
style="top: calc(0.25in + 1mm); right: calc(0.375in + {punch_holes_inset_x}mm); width: calc(0.625in - {punch_holes_inset_x * 2}mm); 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">
<rect x="0.5" y="0.5" width="49" height="7" fill={punch_holes_colors.right.bg} stroke={punch_holes_colors.right.fg} stroke-width="1" stroke-dasharray="4,2"/>
<line x1="0" y1="0" x2="50" y2="8" stroke={punch_holes_colors.right.fg} stroke-width="1"/>
@@ -744,7 +752,7 @@ const code_to_icon: {
</div>
<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;">
style="top: calc(0.25in + 1mm); right: calc(0.375in + {punch_holes_inset_x}mm); width: calc(0.625in - {punch_holes_inset_x * 2}mm); 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"/>
@@ -758,7 +766,7 @@ const code_to_icon: {
class:punch_hole_rainbow_center={!punch_holes_slow_pulse}
class:punch_hole_pulse_center={punch_holes_slow_pulse}
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;">
style="top: calc(0.25in + 1mm); left: 50%; transform: translateX(-50%); width: calc(0.625in - {punch_holes_inset_x * 2}mm); 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">
<rect x="0.5" y="0.5" width="49" height="7" fill={punch_holes_colors.center.bg} stroke={punch_holes_colors.center.fg} stroke-width="1" stroke-dasharray="4,2"/>
<line x1="0" y1="0" x2="50" y2="8" stroke={punch_holes_colors.center.fg} stroke-width="1"/>
@@ -767,7 +775,7 @@ const code_to_icon: {
</div>
<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;">
style="top: calc(0.25in + 1mm); left: 50%; transform: translateX(-50%); width: calc(0.625in - {punch_holes_inset_x * 2}mm); 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"/>
@@ -781,7 +789,7 @@ const code_to_icon: {
{#if punch_holes_left && !punch_holes_left_rainbow}
<div class="punch_hole_marker 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;">
style="top: calc(0.25in + 1mm); left: calc(0.375in + {punch_holes_inset_x}mm); width: calc(0.625in - {punch_holes_inset_x * 2}mm); 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">
<rect x="0.5" y="0.5" width="49" height="7" fill={punch_holes_colors.left.bg} stroke={punch_holes_colors.left.fg} stroke-width="1" stroke-dasharray="4,2"/>
<line x1="0" y1="0" x2="50" y2="8" stroke={punch_holes_colors.left.fg} stroke-width="1"/>
@@ -792,7 +800,7 @@ const code_to_icon: {
{#if punch_holes_right && !punch_holes_right_rainbow}
<div class="punch_hole_marker 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;">
style="top: calc(0.25in + 1mm); right: calc(0.375in + {punch_holes_inset_x}mm); width: calc(0.625in - {punch_holes_inset_x * 2}mm); 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">
<rect x="0.5" y="0.5" width="49" height="7" fill={punch_holes_colors.right.bg} stroke={punch_holes_colors.right.fg} stroke-width="1" stroke-dasharray="4,2"/>
<line x1="0" y1="0" x2="50" y2="8" stroke={punch_holes_colors.right.fg} stroke-width="1"/>
@@ -803,7 +811,7 @@ const code_to_icon: {
{#if punch_holes_center && !punch_holes_center_rainbow}
<div class="punch_hole_marker 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;">
style="top: calc(0.25in + 1mm); left: 50%; transform: translateX(-50%); width: calc(0.625in - {punch_holes_inset_x * 2}mm); 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">
<rect x="0.5" y="0.5" width="49" height="7" fill={punch_holes_colors.center.bg} stroke={punch_holes_colors.center.fg} stroke-width="1" stroke-dasharray="4,2"/>
<line x1="0" y1="0" x2="50" y2="8" stroke={punch_holes_colors.center.fg} stroke-width="1"/>