Badges: use position:fixed for print centering — bypasses all ancestor layout issues

This commit is contained in:
Scott Idem
2026-03-12 18:25:31 -04:00
parent 5410bb4a8c
commit c867795f4d

View File

@@ -162,41 +162,29 @@
<style> <style>
@media print { @media print {
/* Full-page reset. /* Full-page reset.
app.css sets `overflow: hidden` on html + body globally — override it app.css sets overflow:hidden on html + body — override so they don't
so body/html fill the full page (not just the badge width). */ create BFCs that shrink to content. */
html, body { html, body {
display: block !important; display: block !important;
width: 100% !important; width: 100% !important;
height: 100% !important; overflow: visible !important;
margin: 0 !important; margin: 0 !important;
padding: 0 !important; padding: 0 !important;
overflow: visible !important;
} }
/* Hide app chrome */ /* Hide all app chrome */
.submenu { display: none !important; } .submenu { display: none !important; }
/* #ae_main_content becomes the positioning context for the badge. /* #ae_main_content: make it a transparent, non-interfering passthrough block.
It fills the full page (100%×100%), and the badge is absolutely We cannot use display:contents here — it has overflow:auto, and per spec
centered within it. display:contents cannot override overflow-clipping elements (Firefox
enforces this strictly). We just strip its visual/layout effects instead. */
Why not flex: Firefox respects the printer driver's minimum hardware
margins even with @page { margin: 0 }. This makes the available
content area slightly smaller than 100%, throwing off flex centering.
CSS transform-based centering (translate -50%,-50%) is immune to
this: it centers relative to the containing block dimension, which
Firefox computes correctly even when printer margins are applied.
Why not display:contents on this element: #ae_main_content has
overflow:auto. Per spec, display:contents cannot override an element
with overflow clipping; Firefox keeps it as a block box. We reset it
to a positioned block instead. */
#ae_main_content { #ae_main_content {
position: relative !important;
display: block !important; display: block !important;
position: static !important;
width: 100% !important; width: 100% !important;
max-width: none !important; max-width: none !important;
height: 100% !important; height: auto !important;
min-height: 0 !important; min-height: 0 !important;
overflow: visible !important; overflow: visible !important;
background: transparent !important; background: transparent !important;
@@ -205,26 +193,37 @@
} }
/* .main_content and #badge_render_area have no overflow constraints, /* .main_content and #badge_render_area have no overflow constraints,
so display:contents dissolves their boxes in all browsers. so display:contents safely dissolves their boxes in all browsers. */
.event_badge_wrapper emerges as a direct child of #ae_main_content. */
.main_content, .main_content,
#badge_render_area { #badge_render_area {
display: contents !important; display: contents !important;
} }
/* Center the badge using CSS transforms — the most reliable cross-browser /* Center badge using position:fixed.
print centering method; unaffected by printer margin variations. */ Why fixed instead of absolute/flex:
- position:fixed in print positions relative to the @page content area,
completely bypassing the ancestor hierarchy (no containing-block height
dependency, no overflow-clip interference, no flex-parent issues).
- top/left:50% + translate(-50%,-50%) centers within the page content area.
- Firefox applies physical printer hardware margins even with @page{margin:0};
this shrinks the content area slightly but top:50%/left:50% still centers
within whatever area the browser gives us it's immune to asymmetric margins.
- Chrome: identical behavior to absolute+flex centering but more robust.
- min-height:0 overrides the Tailwind min-h-[6.0in] class on this wrapper;
badge_front/back provide their own dimensions. */
.event_badge_wrapper { .event_badge_wrapper {
position: absolute !important; position: fixed !important;
top: 50% !important; top: 50% !important;
left: 50% !important; left: 50% !important;
transform: translate(-50%, -50%) !important; transform: translate(-50%, -50%) !important;
min-height: 0 !important;
max-width: none !important;
margin: 0 !important; margin: 0 !important;
padding: 0 !important; padding: 0 !important;
gap: 0 !important; gap: 0 !important;
} }
/* Never split the front or back across pages */ /* Never split front/back across pages */
.badge_front, .badge_front,
.badge_back { .badge_back {
break-inside: avoid; break-inside: avoid;
@@ -242,7 +241,7 @@
outline: 4px solid blue !important; outline: 4px solid blue !important;
outline-offset: -4px !important; outline-offset: -4px !important;
} }
/* Red = #ae_main_content — should be VISIBLE and fill the page (same as blue). */ /* Red = #ae_main_content — block passthrough, should fill page width/height. */
#ae_main_content { #ae_main_content {
outline: 3px dashed red !important; outline: 3px dashed red !important;
outline-offset: -6px !important; outline-offset: -6px !important;
@@ -256,7 +255,7 @@
outline: 3px dashed purple !important; outline: 3px dashed purple !important;
outline-offset: -12px !important; outline-offset: -12px !important;
} }
/* Cyan = the actual badge — should be dead-center */ /* Cyan = the actual badge — should be dead-center on page */
.event_badge_wrapper { .event_badge_wrapper {
outline: 3px solid cyan !important; outline: 3px solid cyan !important;
outline-offset: 2px !important; outline-offset: 2px !important;