| 1 |
/* |
| 2 |
* PDF rendering of the on-screen designs (templates/invoice-templates, |
| 3 |
* templates/quote-templates) through dompdf. |
| 4 |
* |
| 5 |
* dompdf implements CSS 2.1: no flexbox, no grid, no custom properties, no |
| 6 |
* positioned pseudo-elements. The designs share one DOM skeleton, so this |
| 7 |
* sheet maps the handful of flex/grid containers they all use onto table |
| 8 |
* layout, neutralises the decorations dompdf mishandles, and keeps blocks |
| 9 |
* from splitting across pages. Custom properties are resolved before the |
| 10 |
* markup reaches dompdf (PdfRenderer::resolveCssVariables()). |
| 11 |
*/ |
| 12 |
/* Six of the nine designs lay themselves out on one rem spacing scale, sized |
| 13 |
for a screen: three steps of --spacing-12 alone is 117px of air. The scale |
| 14 |
is redeclared here for print — the resolver takes the last :root wins, and |
| 15 |
this sheet is appended after the design — so every gap shrinks in |
| 16 |
proportion instead of each design being hand-tuned. A six-line invoice then |
| 17 |
ends on the page it started on. */ |
| 18 |
:root { |
| 19 |
--spacing-1: 0.2rem; |
| 20 |
--spacing-2: 0.35rem; |
| 21 |
--spacing-3: 0.5rem; |
| 22 |
--spacing-4: 0.62rem; |
| 23 |
--spacing-5: 0.72rem; |
| 24 |
--spacing-6: 0.85rem; |
| 25 |
--spacing-8: 1.05rem; |
| 26 |
--spacing-10: 1.2rem; |
| 27 |
--spacing-12: 1.35rem; |
| 28 |
--spacing-16: 1.6rem; |
| 29 |
--spacing-20: 1.8rem; |
| 30 |
|
| 31 |
/* Display sizes are set for a screen too: the largest heading is 2.25rem |
| 32 |
before its line box. Compressed from the top down, so the hierarchy |
| 33 |
survives but the masthead stops eating an inch of the page. */ |
| 34 |
--font-size-xs: 0.7rem; |
| 35 |
--font-size-sm: 0.8rem; |
| 36 |
--font-size-base: 0.92rem; |
| 37 |
--font-size-lg: 1rem; |
| 38 |
--font-size-xl: 1.08rem; |
| 39 |
--font-size-2xl: 1.2rem; |
| 40 |
--font-size-3xl: 1.4rem; |
| 41 |
--font-size-4xl: 1.6rem; |
| 42 |
} |
| 43 |
|
| 44 |
/* Screen line spacing is generous; paper does not need it. A Template |
| 45 |
Builder canvas positions every element itself, so it keeps its own. */ |
| 46 |
.template, [class*="template-"] { line-height: 1.38 !important; } |
| 47 |
body.easy-invoice-pdf--canvas .template, body.easy-invoice-pdf--canvas .canvas-element, |
| 48 |
body.easy-invoice-pdf--canvas .canvas-element * { line-height: normal !important; } |
| 49 |
table.invoice-items, table.quote-items, .invoice-totals, .quote-totals { line-height: 1.3 !important; } |
| 50 |
|
| 51 |
/* Headings carry screen-sized margins of their own on top of the block gaps. */ |
| 52 |
.template h1, .template h2, .template h3, .template h4, |
| 53 |
[class*="template-"] h1, [class*="template-"] h2, [class*="template-"] h3, [class*="template-"] h4 { margin-top: 0 !important; margin-bottom: 5px !important; } |
| 54 |
[class*="template-"] p { margin-top: 0 !important; margin-bottom: 5px !important; } |
| 55 |
|
| 56 |
/* legacy sizes itself in pixels rather than the shared scale. */ |
| 57 |
.template-legacy .invoice-header, .template-legacy .quote-header { padding: 10px 12px !important; } |
| 58 |
.template-legacy .invoice-notes, .template-legacy .invoice-terms, |
| 59 |
.template-legacy .quote-notes, .template-legacy .quote-terms { padding: 8px 10px !important; } |
| 60 |
.template-legacy .address-block { padding: 8px 10px !important; margin-bottom: 6px !important; } |
| 61 |
.template-legacy .address-section { margin-bottom: 8px !important; } |
| 62 |
/* The inner wrappers the designs use between those blocks carry their own |
| 63 |
screen margins; on paper the block gap above is enough. */ |
| 64 |
.company-address, .invoice-title-section, .quote-title-section, .address-content, |
| 65 |
.client-address, .terms-content, .notes-content, .description-content { margin-top: 0 !important; margin-bottom: 0 !important; } |
| 66 |
|
| 67 |
/* legacy sets its type in pixels, so the rem scale above does not reach it. */ |
| 68 |
.template-legacy { font-size: 12px !important; } |
| 69 |
.template-legacy table.invoice-items th, .template-legacy table.invoice-items td, |
| 70 |
.template-legacy table.quote-items th, .template-legacy table.quote-items td { padding: 4px 7px !important; } |
| 71 |
.template-legacy .invoice-items, .template-legacy .quote-items, |
| 72 |
.template-legacy .invoice-totals, .template-legacy .quote-totals, |
| 73 |
.template-legacy .invoice-notes, .template-legacy .invoice-terms { margin-top: 8px !important; } |
| 74 |
|
| 75 |
/* dompdf compatibility: the shared flex/grid containers become table layout */ |
| 76 |
@page { { margin: 9mm 9mm; } |
| 77 |
body { margin: 0; } |
| 78 |
/* The designs size everything in rem; a smaller root scales the whole sheet |
| 79 |
down evenly, the way print is set smaller than a screen. */ |
| 80 |
html { font-size: 13px; } |
| 81 |
* { box-shadow: none !important; text-shadow: none !important; transition: none !important; animation: none !important; box-sizing: border-box; } |
| 82 |
/* The designs are spaced for a screen; tighten the largest gaps so a short |
| 83 |
invoice stays on one page. Proportions are kept, only the scale changes. */ |
| 84 |
.template, [class*="template-"] { padding: 10px 12px !important; margin: 0 !important; max-width: none !important; width: auto !important; font-size: 13px; } |
| 85 |
.invoice-header, .quote-header { margin-bottom: 10px !important; padding: 10px 12px !important; } |
| 86 |
.address-section { margin: 8px 0 !important; } |
| 87 |
.address-block { padding: 8px 10px !important; } |
| 88 |
table.invoice-items, table.quote-items { margin: 8px 0 !important; } |
| 89 |
table.invoice-items th, table.invoice-items td, table.quote-items th, table.quote-items td { padding: 5px 8px !important; } |
| 90 |
.invoice-totals, .quote-totals { margin-top: 8px !important; padding: 8px 10px !important; } |
| 91 |
.invoice-notes, .invoice-terms, .invoice-footer, .quote-notes, .quote-terms, .quote-footer, |
| 92 |
.notes-section, .terms-section, .description-section { margin-top: 8px !important; margin-bottom: 8px !important; padding: 8px 10px !important; } |
| 93 |
.terms-section, .description-section { padding-left: 0 !important; padding-right: 0 !important; } |
| 94 |
.invoice-title, .quote-title { margin-bottom: 8px !important; } |
| 95 |
.invoice-header, .quote-header { display: table !important; width: 100%; table-layout: fixed; } |
| 96 |
.invoice-header > *, .quote-header > * { display: table-cell !important; vertical-align: top; } |
| 97 |
.invoice-header > .invoice-meta, .quote-header > .quote-meta { width: 42%; text-align: right; } |
| 98 |
.company-info { display: block !important; } |
| 99 |
.company-info .company-logo { display: block; margin-bottom: 8px; } |
| 100 |
.company-info .company-logo img { max-height: 60px; width: auto; } |
| 101 |
.invoice-meta, .quote-meta { display: block !important; } |
| 102 |
.meta-item { display: table !important; width: 100%; margin-bottom: 3px; padding: 4px 8px !important; } |
| 103 |
.meta-item > * { display: table-cell !important; vertical-align: middle; } |
| 104 |
.meta-item > *:first-child { white-space: nowrap; padding-right: 10px; text-align: left; } |
| 105 |
.meta-item > *:last-child { text-align: right; white-space: nowrap; } |
| 106 |
.address-section { display: table !important; width: 100%; table-layout: fixed; } |
| 107 |
.address-section > * { display: table-cell !important; vertical-align: top; width: 50%; padding-right: 12px; } |
| 108 |
.invoice-totals, .quote-totals { display: table !important; width: 55% !important; margin-left: 45% !important; margin-right: 0 !important; position: static !important; page-break-inside: avoid; } |
| 109 |
.invoice-total-row, .quote-total-row { display: table !important; width: 100%; } |
| 110 |
.invoice-total-row > *, .quote-total-row > * { display: table-cell !important; vertical-align: middle; } |
| 111 |
.invoice-total-row > *:last-child, .quote-total-row > *:last-child { text-align: right; white-space: nowrap; } |
| 112 |
table.invoice-items, table.quote-items { width: 100%; border-collapse: collapse; page-break-inside: auto; } |
| 113 |
table.invoice-items tr, table.quote-items tr { page-break-inside: avoid; } |
| 114 |
table.invoice-items th, table.quote-items th { white-space: nowrap; } |
| 115 |
/* Notes, terms and the footer are prose: keeping them atomic pushed the whole |
| 116 |
block to a second page whenever it did not fit in what was left, leaving a |
| 117 |
third of page one empty. They may break; a couple of lines are kept |
| 118 |
together at each edge so a heading never ends a page on its own. */ |
| 119 |
.invoice-footer, .quote-footer, .invoice-notes, .invoice-terms, .quote-notes, .quote-terms { page-break-inside: auto; orphans: 2; widows: 2; } |
| 120 |
.invoice-notes h1, .invoice-notes h2, .invoice-notes h3, .invoice-notes h4, |
| 121 |
.invoice-terms h1, .invoice-terms h2, .invoice-terms h3, .invoice-terms h4, |
| 122 |
.quote-notes h1, .quote-notes h2, .quote-notes h3, .quote-notes h4, |
| 123 |
.quote-terms h1, .quote-terms h2, .quote-terms h3, .quote-terms h4 { page-break-after: avoid; } |
| 124 |
.partial-payments-breakdown, .ei-signature-block { page-break-inside: avoid; } |
| 125 |
/* Decorative pseudo elements: dompdf cannot position them, so the top accent bar |
| 126 |
becomes a static strip and the small underline / stripe accents are dropped. */ |
| 127 |
.template::before, [class*="template-"]::before { position: static !important; display: block !important; width: auto !important; left: auto !important; right: auto !important; top: auto !important; margin: 0 0 10px 0; border-radius: 0; } |
| 128 |
.invoice-header::after, .quote-header::after, .invoice-header::before, .quote-header::before, |
| 129 |
.invoice-meta::before, .quote-meta::before, .invoice-meta::after, .quote-meta::after, |
| 130 |
.invoice-totals::before, .quote-totals::before, .invoice-totals::after, .quote-totals::after, |
| 131 |
.address-block::before, .address-block::after, .company-info::before, .company-info::after, |
| 132 |
.invoice-title::before, .invoice-title::after, .quote-title::before, .quote-title::after { display: none !important; content: none !important; } |
| 133 |
.invoice-header, .quote-header, .invoice-meta, .quote-meta, .address-block { position: static !important; } |
| 134 |
/* Totals: pushed right with a margin; floats behave unpredictably in dompdf */ |
| 135 |
.invoice-totals, .quote-totals { float: none !important; } |
| 136 |
.invoice-notes, .invoice-terms, .invoice-footer, .quote-notes, .quote-terms, .quote-footer { clear: both; } |
| 137 |
/* Cards around the whole document sometimes use overflow/transform tricks */ |
| 138 |
.template, [class*="template-"] { overflow: visible !important; transform: none !important; } |
| 139 |
/* Gradients: dompdf paints none, so header cells fall back to the solid colour. */ |
| 140 |
.template-creative table.invoice-items th { background: var(--primary-color) !important; } |
| 141 |
.template-modern table.quote-items th { background: var(--primary-color) !important; } |
| 142 |
.template-corporate::before { display: none !important; } |
| 143 |
/* The corporate masthead bleeds past the card on screen; on paper it sits inside the margin. */ |
| 144 |
.template-corporate .invoice-header { margin: 0 0 16px !important; padding: 14px 16px !important; } |
| 145 |
.template-corporate .invoice-items th, .template-corporate .invoice-items td { padding-left: 0 !important; } |
| 146 |
.template-elegant table.invoice-items th, .template-elegant table.invoice-items td { padding-left: 0 !important; } |
| 147 |
.template-elegant table.invoice-items th:last-child, .template-elegant table.invoice-items td:last-child { padding-right: 0 !important; } |
| 148 |
/* Payment position appended after the design when it does not print one */ |
| 149 |
.ei-pdf-paid { width: 55%; margin-left: 45%; margin-top: 6px; border-collapse: collapse; font-size: 13px; } |
| 150 |
.ei-pdf-paid td { padding: 4px 8px; } |
| 151 |
.ei-pdf-paid td.v { text-align: right; white-space: nowrap; } |
| 152 |
.ei-pdf-paid tr.grand td { font-weight: bold; border-top: 1px solid #333; } |
| 153 |
|