Implement comprehensive print optimization for receipt page
All checks were successful
Build and Push Docker Images / docker (push) Successful in 57s

- Replace pie chart with print-optimized table to eliminate label overlap
- Add extensive @media print CSS for high-contrast, professional output
- Convert all gradients to white backgrounds with black borders
- Optimize spacing and font sizes for print media
- Update CarbonImpactComparison with high-contrast print styling
- Ensure full page width utilization and proper page breaks
- Zero new dependencies (Pure CSS approach per consensus)

Resolves print quality issues: label collision, poor contrast, wasted whitespace

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Matt 2025-10-31 18:37:27 +01:00
parent f0337101cf
commit 109f350ee8
3 changed files with 159 additions and 28 deletions

View File

@ -81,34 +81,34 @@ function ComparisonCard({ comparison, index }: ComparisonCardProps) {
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.1, duration: 0.5 }}
className="bg-white/10 backdrop-blur-md rounded-xl p-6 border border-white/20 hover:border-emerald-400/50 transition-all duration-300 hover:shadow-lg hover:shadow-emerald-500/20"
className="bg-white/10 backdrop-blur-md rounded-xl p-6 border border-white/20 hover:border-emerald-400/50 transition-all duration-300 hover:shadow-lg hover:shadow-emerald-500/20 print:bg-white print:border-2 print:border-black print:backdrop-blur-none"
>
{/* Icon */}
<div className="mb-4 flex items-center justify-center">
<div className="bg-emerald-500/20 p-3 rounded-full">
<Icon className="w-8 h-8 text-emerald-400" />
<div className="bg-emerald-500/20 p-3 rounded-full print:bg-gray-100 print:border print:border-black">
<Icon className="w-8 h-8 text-emerald-400 print:text-black" />
</div>
</div>
{/* Value */}
<div className="text-center mb-2">
<div className="text-3xl font-bold text-white">
<div className="text-3xl font-bold text-white print:text-black">
<AnimatedCounter value={comparison.value} />
</div>
<div className="text-sm text-emerald-300 mt-1">{comparison.unit}</div>
<div className="text-sm text-emerald-300 mt-1 print:text-black print:font-semibold">{comparison.unit}</div>
</div>
{/* Label */}
<div className="text-center">
<p className="text-sm text-white/90 font-medium">{comparison.label}</p>
<p className="text-sm text-white/90 font-medium print:text-black">{comparison.label}</p>
{comparison.description && (
<p className="text-xs text-white/60 mt-2">{comparison.description}</p>
<p className="text-xs text-white/60 mt-2 print:text-gray-700">{comparison.description}</p>
)}
</div>
{/* Source */}
<div className="text-center mt-4">
<span className="text-xs text-white/50 italic">Source: {comparison.source}</span>
<span className="text-xs text-white/50 italic print:text-gray-600">Source: {comparison.source}</span>
</div>
</motion.div>
);
@ -140,14 +140,14 @@ export function CarbonImpactComparison({
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
className="text-center mb-8"
className="text-center mb-8 print:mb-4"
>
<h3 className="text-2xl md:text-3xl font-bold text-white mb-2">{titleText}</h3>
<p className="text-white/80 text-sm md:text-base">{subtitleText}</p>
<h3 className="text-2xl md:text-3xl font-bold text-white mb-2 print:text-black print:text-2xl">{titleText}</h3>
<p className="text-white/80 text-sm md:text-base print:text-black print:text-sm">{subtitleText}</p>
</motion.div>
{/* Comparison Cards Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 print:gap-4">
{comparisons.map((comparison, index) => (
<ComparisonCard
key={`${comparison.category}-${comparison.label}`}
@ -162,9 +162,9 @@ export function CarbonImpactComparison({
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.5, duration: 0.5 }}
className="text-center mt-6"
className="text-center mt-6 print:mt-3"
>
<p className="text-xs text-white/50">
<p className="text-xs text-white/50 print:text-gray-600">
Equivalencies calculated using EPA 2024, DEFRA 2024, and IMO 2024 verified conversion
factors.
</p>

View File

@ -98,9 +98,9 @@ export function RechartsPortfolioPieChart({
return (
<div className="flex flex-col items-center w-full max-w-4xl mx-auto">
{/* SVG Chart - Responsive container */}
<div className="w-full print:max-w-[600px]" style={{ maxWidth: '700px' }}>
<ResponsiveContainer width="100%" height={400} className="print:h-[350px]">
{/* SVG Chart - Responsive container (hidden in print) */}
<div className="w-full print:hidden" style={{ maxWidth: '700px' }}>
<ResponsiveContainer width="100%" height={400}>
<PieChart>
<Pie
data={chartData}
@ -121,14 +121,14 @@ export function RechartsPortfolioPieChart({
<Label
value={totalTons.toString()}
position="center"
className="text-2xl font-bold fill-emerald-600 print:text-xl"
className="text-2xl font-bold fill-emerald-600"
style={{ fontSize: '20px', fontWeight: 700, fill: '#059669' }}
/>
<Label
value="tons CO₂"
position="center"
dy={18}
className="text-sm fill-slate-600 print:text-xs"
className="text-sm fill-slate-600"
style={{ fontSize: '12px', fill: '#475569' }}
/>
</Pie>
@ -136,19 +136,45 @@ export function RechartsPortfolioPieChart({
</ResponsiveContainer>
</div>
{/* Legend below chart (for print clarity) */}
<div className="mt-6 grid grid-cols-1 md:grid-cols-2 gap-3 w-full max-w-2xl print:mt-4 print:gap-2">
{/* Print-optimized table (visible only in print) */}
<div className="hidden print:block w-full mb-6">
<h3 className="text-lg font-bold mb-4 text-center">Portfolio Distribution</h3>
<p className="text-center mb-4 text-lg font-semibold">
Total: {totalTons} tons CO
</p>
<table className="w-full border-collapse border-2 border-black">
<thead>
<tr className="bg-gray-100">
<th className="border-2 border-black px-4 py-2 text-left font-bold">Project Name</th>
<th className="border-2 border-black px-4 py-2 text-center font-bold">Percentage</th>
<th className="border-2 border-black px-4 py-2 text-right font-bold">Tons CO</th>
</tr>
</thead>
<tbody>
{chartData.map((item, index) => (
<tr key={index} className={index % 2 === 0 ? 'bg-white' : 'bg-gray-50'}>
<td className="border-2 border-black px-4 py-2">{item.name}</td>
<td className="border-2 border-black px-4 py-2 text-center">{item.percentage.toFixed(1)}%</td>
<td className="border-2 border-black px-4 py-2 text-right">{item.tons}</td>
</tr>
))}
</tbody>
</table>
</div>
{/* Legend below chart (for screen clarity, hidden in print) */}
<div className="mt-6 grid grid-cols-1 md:grid-cols-2 gap-3 w-full max-w-2xl print:hidden">
{chartData.map((item, index) => (
<div key={index} className="flex items-center gap-3 print:gap-2">
<div key={index} className="flex items-center gap-3">
<div
className="w-4 h-4 rounded-sm flex-shrink-0 print:w-3 print:h-3"
className="w-4 h-4 rounded-sm flex-shrink-0"
style={{ backgroundColor: item.fill }}
/>
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-slate-800 truncate print:text-xs">
<p className="text-sm font-medium text-slate-800 truncate">
{item.name}
</p>
<p className="text-xs text-slate-600 print:text-[10px]">
<p className="text-xs text-slate-600">
{item.tons} tons ({item.percentage.toFixed(1)}%)
</p>
</div>

View File

@ -126,29 +126,134 @@ export default function CheckoutSuccess({
{/* Print-specific styles */}
<style>{`
@media print {
/* Reset and Base Styles */
body {
background: white !important;
}
/* Hide non-print elements */
.no-print {
display: none !important;
}
/* Main container - full width utilization */
.print-receipt {
max-width: 100% !important;
margin: 0 !important;
padding: 20px !important;
padding: 10px !important;
box-shadow: none !important;
}
.print-logo {
max-width: 200px !important;
/* Remove all decorative elements */
* {
box-shadow: none !important;
border-radius: 0 !important;
animation: none !important;
transition: none !important;
}
/* Replace gradients with high-contrast borders */
[class*="gradient"] {
background: white !important;
background-image: none !important;
border: 2px solid #000 !important;
}
/* High contrast text */
h1, h2, h3, h4, h5, h6, p, span, div {
color: #000 !important;
}
/* Page breaks */
.print-page-break {
page-break-after: always !important;
break-after: page !important;
}
/* Page setup */
@page {
margin: 0.5in;
size: letter;
}
/* Header section with logo */
.print-logo {
max-width: 150px !important;
}
/* Order details section - two column layout */
.order-details-grid {
display: grid !important;
grid-template-columns: 1fr 1fr !important;
gap: 0.5rem !important;
}
/* Optimize spacing */
.px-8 {
padding-left: 1rem !important;
padding-right: 1rem !important;
}
.py-6, .py-8 {
padding-top: 0.75rem !important;
padding-bottom: 0.75rem !important;
}
.p-8 {
padding: 1rem !important;
}
.mb-6, .mb-8 {
margin-bottom: 0.5rem !important;
}
/* Font size optimization */
.text-3xl, .text-4xl {
font-size: 1.5rem !important;
}
.text-2xl {
font-size: 1.25rem !important;
}
.text-xl {
font-size: 1.125rem !important;
}
.text-lg {
font-size: 1rem !important;
}
/* Remove colored backgrounds, use borders */
[class*="bg-emerald"], [class*="bg-green"], [class*="bg-blue"],
[class*="bg-cyan"], [class*="bg-teal"], [class*="bg-indigo"],
[class*="bg-slate-50"], [class*="bg-blue-50"] {
background: white !important;
background-color: white !important;
border: 1px solid #000 !important;
}
/* Ensure icons remain visible */
svg {
color: #000 !important;
}
/* Status badges */
[class*="bg-green-100"], [class*="bg-yellow-100"] {
background: white !important;
border: 2px solid #000 !important;
color: #000 !important;
}
/* Optimize table spacing if present */
table {
width: 100% !important;
}
/* Portfolio chart container */
.portfolio-chart-container {
max-width: 100% !important;
}
}
`}</style>