From c7fa75afc7eb83d724ebf62c8e9a808a1131e3c6 Mon Sep 17 00:00:00 2001
From: Scott Idem
Date: Fri, 10 Apr 2026 14:25:08 -0400
Subject: [PATCH] ui(badges): add background image bleed support (cfg_json, PVC
layout)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Add `bleed` field to BadgeTemplateCfg (CSS length, e.g. "0.125in")
- Badge view: derive bleed_offset, move bg-image to absolute positioned div
that extends past card edges; add isolation:isolate to badge_front stacking context
- Template form: add bleed input in Advanced > Appearance; wire to cfg_json save/load
- PVC layout CSS: change overflow:hidden → overflow:visible in print rule to allow
bleed div to render at physical card boundary (Zebra driver clips at card edge)
- Prevents white borders on PVC cards when printer has slight alignment variance.
Screen preview shows bleed visually extending past the card outline.
---
.../css/badge_layout_zebra_zc10l_pvc.css | 4 ++-
.../ae_events/types/ae_badge_template_cfg.ts | 6 ++++
.../[badge_id]/ae_comp__badge_obj_view.svelte | 29 ++++++++++++++++---
.../ae_comp__badge_template_form.svelte | 21 ++++++++++++++
4 files changed, 55 insertions(+), 5 deletions(-)
diff --git a/src/lib/ae_events/badges/css/badge_layout_zebra_zc10l_pvc.css b/src/lib/ae_events/badges/css/badge_layout_zebra_zc10l_pvc.css
index 58fd9454..829cedeb 100644
--- a/src/lib/ae_events/badges/css/badge_layout_zebra_zc10l_pvc.css
+++ b/src/lib/ae_events/badges/css/badge_layout_zebra_zc10l_pvc.css
@@ -44,6 +44,8 @@
height: 5.5in !important;
max-width: 3.5in !important;
max-height: 5.5in !important;
- overflow: hidden !important;
+ /* Allow visible: the bleed div inside badge_front extends past the card
+ boundary intentionally. The Zebra driver clips at the physical card edge. */
+ overflow: visible !important;
}
}
diff --git a/src/lib/ae_events/types/ae_badge_template_cfg.ts b/src/lib/ae_events/types/ae_badge_template_cfg.ts
index e8dbb287..7e060ffa 100644
--- a/src/lib/ae_events/types/ae_badge_template_cfg.ts
+++ b/src/lib/ae_events/types/ae_badge_template_cfg.ts
@@ -30,6 +30,12 @@ export interface BadgeTemplateCfg {
// Body text color: either a Tailwind `text-*` class or a hex color like `#112233`.
body_text_color?: string;
+ // Background image bleed: how far the background image extends past the physical
+ // card edge on all four sides. Prevents white borders when the printer clips at the
+ // card boundary. Any CSS length is valid: "0.125in", "3mm", "9px".
+ // Leave unset (or "0") for no bleed.
+ bleed?: string;
+
// Allow arbitrary extra keys to preserve forward-compatibility.
[key: string]: any;
}
diff --git a/src/routes/events/[event_id]/(badges)/badges/[badge_id]/ae_comp__badge_obj_view.svelte b/src/routes/events/[event_id]/(badges)/badges/[badge_id]/ae_comp__badge_obj_view.svelte
index be98c8c0..e47a6e42 100644
--- a/src/routes/events/[event_id]/(badges)/badges/[badge_id]/ae_comp__badge_obj_view.svelte
+++ b/src/routes/events/[event_id]/(badges)/badges/[badge_id]/ae_comp__badge_obj_view.svelte
@@ -309,6 +309,15 @@ let body_text_color_style = $derived.by(() => {
return 'color: #000000;';
});
+// Background image bleed: template cfg_json.bleed is a CSS length like "0.125in" or "3mm".
+// We negate it here so it can be used as a CSS inset value on the bleed container div.
+// "0px" means no bleed — the bg image is flush with the badge edges (same as before).
+let bleed_offset = $derived.by(() => {
+ const b = template_cfg?.bleed;
+ if (!b || b === '0' || b === '0px') return '0px';
+ return `-${b}`;
+});
+
/**
* Layout-aware section heights for Element_fit_text.
*
@@ -615,9 +624,7 @@ const code_to_icon: {
text-center hover:outline-2 hover:outline-red-500/75
hover:outline-dashed
"
- style="{bg_image_path
- ? `background-image: url('${bg_image_path}'); background-size: cover; background-position: top center; background-repeat: no-repeat;`
- : ''}{demo_bg_style ? ` ${demo_bg_style}` : ''}">
+ style="isolation: isolate;{demo_bg_style ? ` ${demo_bg_style}` : ''}">
+ {#if bg_image_path}
+
+
+ {/if}
+
@@ -1310,7 +1331,7 @@ const code_to_icon: {
}
.name_pad_long {
/* padding-left: 2%; */
- /* padding-right: 0%; */
+ padding-right: 0%;
}
/*
diff --git a/src/routes/events/[event_id]/(badges)/templates/ae_comp__badge_template_form.svelte b/src/routes/events/[event_id]/(badges)/templates/ae_comp__badge_template_form.svelte
index fc063689..599d48f3 100644
--- a/src/routes/events/[event_id]/(badges)/templates/ae_comp__badge_template_form.svelte
+++ b/src/routes/events/[event_id]/(badges)/templates/ae_comp__badge_template_form.svelte
@@ -63,6 +63,8 @@ let cfg_controls_shown: string[] = $state(['name', 'title', 'affiliations', 'loc
let cfg_controls_auth_editable: string[] = $state([]);
// Body text color (hex)
let cfg_body_text_color = $state('#000000');
+// Background image bleed (CSS length, e.g. "0.125in", "3mm"). Empty = no bleed.
+let cfg_bleed = $state('');
// Alignment overrides: 'left' | 'center' | 'right' | 'justify'
let cfg_align_name = $state('center');
let cfg_align_title = $state('center');
@@ -156,6 +158,9 @@ async function load_template(id: string) {
cfg_body_text_color = '#000000';
}
+ // Background bleed (CSS length, e.g. "0.125in"). Empty = no bleed.
+ cfg_bleed = parsed_cfg.bleed ?? '';
+
// Alignment overrides (nested under cfg_json.align and cfg_json.qr_alignment)
cfg_align_name = parsed_cfg?.align?.name ?? parsed_cfg.align_name ?? 'center';
cfg_align_title = parsed_cfg?.align?.title ?? parsed_cfg.align_title ?? 'center';
@@ -226,6 +231,13 @@ async function handle_submit() {
if ((cfg_obj as any)?.body_text_color_hex) delete (cfg_obj as any).body_text_color_hex;
}
+ // Background bleed: save if set, remove key entirely when cleared
+ if (cfg_bleed.trim()) {
+ cfg_obj.bleed = cfg_bleed.trim();
+ } else {
+ delete cfg_obj.bleed;
+ }
+
const data_to_save: key_val = {
name,
background_image_path,
@@ -525,6 +537,15 @@ function toggle_cfg_controls_auth_editable(key: string) {
Pick a hex color (e.g. #112233). The renderer will use this inline color.
+
Controls Menu Toggles