Open demo

CSS code

HTML
<div><span class="lbl">Dark invoice that prints light</span>
<article class="nj" aria-labelledby="nj1-title">
  <div class="nj-toolbar"><button class="nj-btn" type="button" data-print hidden><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 9V3h12v6M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"/><path d="M6 14h12v7H6z"/></svg>Print or save as PDF</button></div>
  <div class="nj-sheet">
    <header class="nj-top">
      <p class="nj-brand"><svg width="30" height="30" viewBox="0 0 30 30" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round" aria-hidden="true"><path d="M3 18c5-1 8-5 12-11 4 6 7 10 12 11-5 1-8 3-12 6-4-3-7-5-12-6z"/><circle cx="15" cy="15" r="1.6" fill="currentColor"/></svg>Nightjar Labs</p>
      <div class="nj-head"><h2 class="nj-title" id="nj1-title">Invoice</h2><p class="nj-num">No. <b>NJ-0412</b></p></div>
    </header>
    <div class="nj-meta">
      <div><h3 class="nj-k">From</h3><address><strong>Nightjar Labs OÜ</strong>Kalamaja 14<br>10415 Tallinn, Estonia<br>VAT EE102938475</address></div>
      <div><h3 class="nj-k">Bill to</h3><address><strong>Polaris Freight GmbH</strong>Speicherstraße 7<br>20457 Hamburg, Germany<br>VAT DE298765432</address></div>
      <div><h3 class="nj-k">Details</h3><dl class="nj-dl"><div><dt>Issued</dt><dd>16 Oct 2026</dd></div><div><dt>Due</dt><dd>30 Oct 2026</dd></div><div><dt>Project</dt><dd>Fleet app</dd></div></dl></div>
    </div>
    <table class="nj-table">
      <caption>Sprint 14 · 1–14 Oct 2026</caption>
      <thead><tr><th scope="col">Description</th><th scope="col" class="num">Qty</th><th scope="col" class="num">Rate</th><th scope="col" class="num">Amount</th></tr></thead>
      <tbody>
        <tr><th scope="row">Mobile app sprint<span>Two weeks, fixed price</span></th><td class="num">1</td><td class="num">€9,600.00</td><td class="num">€9,600.00</td></tr>
        <tr><th scope="row">Carrier tracking API<span>Integration, hours</span></th><td class="num">32</td><td class="num">€145.00</td><td class="num">€4,640.00</td></tr>
        <tr><th scope="row">QA and device testing<span>Hours</span></th><td class="num">18</td><td class="num">€95.00</td><td class="num">€1,710.00</td></tr>
        <tr><th scope="row">Cloud hosting<span>October usage</span></th><td class="num">1</td><td class="num">€486.20</td><td class="num">€486.20</td></tr>
      </tbody>
      <tfoot>
        <tr><th scope="row" colspan="3">Subtotal</th><td class="num">€16,436.20</td></tr>
        <tr><th scope="row" colspan="3">VAT 0%, reverse charge</th><td class="num">€0.00</td></tr>
        <tr class="nj-grand"><th scope="row" colspan="3">Total due (EUR)</th><td class="num">€16,436.20</td></tr>
      </tfoot>
    </table>
    <footer class="nj-foot">
      <div><h3 class="nj-k">Bank transfer</h3><p>IBAN EE38 2200 0000 0000 4821<br>BIC NJLBEE2X · Reference NJ-0412</p></div>
      <div><h3 class="nj-k">Note</h3><p>VAT is due from the customer under the reverse charge rule. Questions: <a href="mailto:[email protected]">[email protected]</a></p></div>
    </footer>
  </div>
</article>
</div>
<div><span class="lbl">Dark invoice card with partial payment</span>
<article class="nj nj-card nj-break" aria-labelledby="nj2-title">
  <div class="nj-cardtop">
    <div><h2 class="nj-k" id="nj2-title">Invoice NJ-0398</h2><p>Polaris Freight GmbH</p></div>
    <span class="nj-pill">Partly paid</span>
  </div>
  <p class="nj-big">€4,000.00 <span class="nj-left">left to pay</span></p>
  <progress class="nj-progress" value="60" max="100" aria-labelledby="nj2-paid">60%</progress>
  <p class="nj-paid" id="nj2-paid"><span>€6,000.00 of €10,000.00 paid</span><span>60%</span></p>
  <dl class="nj-dl">
    <div><dt>Issued</dt><dd>1 Oct 2026</dd></div>
    <div><dt>Due</dt><dd>24 Oct 2026, in 5 days</dd></div>
    <div><dt>Last payment</dt><dd>12 Oct 2026</dd></div>
  </dl>
  <div class="nj-actions"><a class="nj-btn" href="#pay-nj-0398">Pay €4,000.00</a><a class="nj-btn nj-btn--ghost" href="#nj-0398-pdf">Download PDF</a></div>
</article>
</div>
CSS
/* Dark invoice. On screen it is a dark card; in print every colour token flips to a light palette, so Print or
   Save as PDF gives black text on white paper and no ink-heavy background. Grey text keeps 4.5:1 on the dark card. */
.nj{--nj-bg:#141a24;--nj-raise:#1a2130;--nj-ink:#e6eaf2;--nj-muted:#a3acbd;--nj-line:#2a3446;--nj-accent:#a78bfa;--nj-accent-ink:#17112b;--nj-glow:rgba(167,139,250,.35);--nj-track:#2a3446;
  box-sizing:border-box;max-width:820px;margin:0 auto;color:var(--nj-ink);font-size:14px;line-height:1.55;color-scheme:dark}
.nj *,.nj *::before,.nj *::after{box-sizing:border-box}
:where(.nj) :is(h2,h3,p,dl,dd,table,address){margin:0}
.nj .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
.nj-toolbar{display:flex;justify-content:flex-end;margin-bottom:14px}
.nj-btn{display:inline-flex;align-items:center;gap:8px;padding:10px 18px;border:0;border-radius:10px;background:var(--nj-accent);color:var(--nj-accent-ink);font:inherit;font-weight:700;text-decoration:none;cursor:pointer;
  box-shadow:0 0 0 0 var(--nj-glow);transition:box-shadow .2s,background-color .2s}
.nj-btn[hidden]{display:none}
.nj-btn:hover{background:#b9a3fc;box-shadow:0 0 24px 2px var(--nj-glow)}
.nj-btn--ghost{background:transparent;color:var(--nj-ink);box-shadow:inset 0 0 0 1px #5d6880}
.nj-btn--ghost:hover{background:var(--nj-raise);box-shadow:inset 0 0 0 1px var(--nj-accent)}
.nj :is(a,button):focus-visible{outline:2px solid var(--nj-accent);outline-offset:3px}
.nj-sheet{position:relative;padding:44px;border:1px solid var(--nj-line);border-radius:20px;background:linear-gradient(180deg,var(--nj-raise),var(--nj-bg) 220px);box-shadow:0 30px 60px -30px rgba(0,0,0,.8)}
.nj-sheet::before{content:"";position:absolute;inset:-1px -1px auto;height:2px;border-radius:20px 20px 0 0;background:linear-gradient(90deg,transparent,var(--nj-accent),transparent)}
.nj-top{display:flex;flex-wrap:wrap;justify-content:space-between;gap:20px;padding-bottom:28px;border-bottom:1px solid var(--nj-line)}
.nj-brand{display:flex;align-items:center;gap:12px;font-weight:700;font-size:1.1rem}
.nj-brand svg{color:var(--nj-accent)}
.nj-k{font-size:.72rem;font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:var(--nj-muted);margin-bottom:6px}
.nj-title{font-size:2rem;font-weight:800;letter-spacing:-.03em;line-height:1.1}
.nj-num{color:var(--nj-muted);font-variant-numeric:tabular-nums}
.nj-num b{color:var(--nj-ink)}
.nj-head{text-align:right}
.nj-meta{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:24px;padding:26px 0}
.nj-meta address{font-style:normal;color:var(--nj-muted)}
.nj-meta strong{display:block;color:var(--nj-ink)}
.nj-dl{display:grid;gap:2px;font-variant-numeric:tabular-nums}
.nj-dl div{display:flex;justify-content:space-between;gap:10px}
.nj-dl dt{color:var(--nj-muted)}
.nj-dl dd{font-weight:600}
.nj-table{width:100%;border-collapse:collapse;font-variant-numeric:tabular-nums}
.nj-table caption{text-align:left;font-weight:700;padding-bottom:10px}
.nj-table thead th{padding:10px 12px;border-bottom:1px solid var(--nj-line);font-size:.72rem;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--nj-muted);text-align:left}
.nj-table tbody :is(th,td){padding:14px 12px;border-bottom:1px solid var(--nj-line);text-align:left;font-weight:400;vertical-align:top}
.nj-table tbody th{font-weight:600}
.nj-table tbody th span{display:block;font-weight:400;font-size:.84rem;color:var(--nj-muted)}
.nj-table :is(th,td):first-child{padding-left:0}
.nj-table :is(th,td):last-child{padding-right:0}
.nj-table .num{text-align:right;white-space:nowrap}
.nj-table tfoot :is(th,td){padding:5px 12px;text-align:right;font-weight:400}
.nj-table tfoot th{color:var(--nj-muted)}
.nj-table tfoot tr:first-child>*{padding-top:16px}
.nj-grand>*{padding-top:14px!important;font-size:1.25rem;font-weight:800!important;color:var(--nj-ink)!important}
.nj-grand td{color:var(--nj-accent)!important}
.nj-foot{display:grid;grid-template-columns:1fr 1fr;gap:24px;margin-top:30px;padding-top:22px;border-top:1px solid var(--nj-line);font-size:.86rem;color:var(--nj-muted)}
.nj-foot a{color:var(--nj-accent)}
/* compact card */
.nj-card{max-width:420px;padding:24px;border:1px solid var(--nj-line);border-radius:18px;background:var(--nj-bg)}
.nj-cardtop{display:flex;justify-content:space-between;align-items:flex-start;gap:12px}
.nj-pill{display:inline-flex;align-items:center;gap:6px;padding:3px 10px;border-radius:999px;border:1px solid #6b5a1f;background:#2a2410;color:#fcd34d;font-size:.76rem;font-weight:700}
.nj-pill::before{content:"";width:7px;height:7px;border-radius:50%;background:currentColor}
.nj-big{margin-top:14px!important;font-size:2.1rem;font-weight:800;letter-spacing:-.03em;font-variant-numeric:tabular-nums}
.nj-left{font-size:.9rem;font-weight:500;letter-spacing:0;color:var(--nj-muted)}
.nj-progress{display:block;width:100%;height:8px;margin-top:14px;border:0;border-radius:99px;background:var(--nj-track);overflow:hidden;-webkit-appearance:none;appearance:none}
.nj-progress::-webkit-progress-bar{background:var(--nj-track);border-radius:99px}
.nj-progress::-webkit-progress-value{background:linear-gradient(90deg,#7c3aed,var(--nj-accent));border-radius:99px}
.nj-progress::-moz-progress-bar{background:linear-gradient(90deg,#7c3aed,var(--nj-accent));border-radius:99px}
.nj-paid{display:flex;justify-content:space-between;margin-top:8px!important;font-size:.84rem;color:var(--nj-muted);font-variant-numeric:tabular-nums}
.nj-card .nj-dl{margin-top:18px;padding-top:16px;border-top:1px solid var(--nj-line)}
.nj-actions{display:flex;flex-wrap:wrap;gap:10px;margin-top:20px}
@media (max-width:720px){.nj-sheet{padding:28px 20px}.nj-meta{grid-template-columns:1fr 1fr}.nj-foot{grid-template-columns:1fr}.nj-head{text-align:left}}
@media (max-width:480px){.nj{font-size:13px}.nj-sheet{padding:24px 14px}.nj-meta{grid-template-columns:1fr}.nj-table thead th,.nj-table tbody :is(th,td){padding-left:4px;padding-right:4px}.nj-table{font-size:12.5px}}
/* print: light palette, no glow, no gradient, buttons hidden */
@page{size:auto;margin:12mm}
@media print{
  .nj{--nj-bg:#fff;--nj-raise:#fff;--nj-ink:#111827;--nj-muted:#4b5563;--nj-line:#d1d5db;--nj-accent:#5b21b6;--nj-track:#e5e7eb;color-scheme:light;max-width:none;font-size:10pt}
  .nj-toolbar,.nj-actions{display:none!important}
  .nj-sheet,.nj-card{padding:0;border:0;border-radius:0;background:#fff;box-shadow:none}
  .nj-sheet::before{display:none}
  .nj-break{break-before:page}
  .nj-table thead{display:table-header-group}
  .nj-table tr,.nj-foot{break-inside:avoid}
  .nj-pill{background:#fff;border-color:#92400e;color:#92400e}
  .nj-progress{border:1px solid #9ca3af;-webkit-print-color-adjust:exact;print-color-adjust:exact}
  .nj-foot a{color:inherit;text-decoration:none}
}
@media (prefers-reduced-motion:reduce){.nj-btn{transition:none}}
JavaScript
// Print buttons are hidden until JavaScript can run them; the browser's own Print menu works either way.
document.querySelectorAll('[data-print]').forEach(function (btn) {
  btn.hidden = false;
  btn.addEventListener('click', function () { window.print(); });
});

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<div><span class="lbl">Dark invoice that prints light</span>
<article class="nj" data-bs-theme="dark" aria-labelledby="nj1-title">
  <div class="nj-toolbar d-print-none"><button class="btn nj-btn" type="button" data-print hidden><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 9V3h12v6M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"/><path d="M6 14h12v7H6z"/></svg>Print or save as PDF</button></div>
  <div class="nj-sheet card">
    <header class="nj-top">
      <p class="nj-brand"><svg width="30" height="30" viewBox="0 0 30 30" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round" aria-hidden="true"><path d="M3 18c5-1 8-5 12-11 4 6 7 10 12 11-5 1-8 3-12 6-4-3-7-5-12-6z"/><circle cx="15" cy="15" r="1.6" fill="currentColor"/></svg>Nightjar Labs</p>
      <div class="nj-head"><h2 class="nj-title" id="nj1-title">Invoice</h2><p class="nj-num">No. <b>NJ-0412</b></p></div>
    </header>
    <div class="nj-meta">
      <div><h3 class="nj-k">From</h3><address><strong>Nightjar Labs OÜ</strong>Kalamaja 14<br>10415 Tallinn, Estonia<br>VAT EE102938475</address></div>
      <div><h3 class="nj-k">Bill to</h3><address><strong>Polaris Freight GmbH</strong>Speicherstraße 7<br>20457 Hamburg, Germany<br>VAT DE298765432</address></div>
      <div><h3 class="nj-k">Details</h3><dl class="nj-dl"><div><dt>Issued</dt><dd>16 Oct 2026</dd></div><div><dt>Due</dt><dd>30 Oct 2026</dd></div><div><dt>Project</dt><dd>Fleet app</dd></div></dl></div>
    </div>
    <table class="table nj-table caption-top">
      <caption>Sprint 14 · 1–14 Oct 2026</caption>
      <thead><tr><th scope="col">Description</th><th scope="col" class="num">Qty</th><th scope="col" class="num">Rate</th><th scope="col" class="num">Amount</th></tr></thead>
      <tbody>
        <tr><th scope="row">Mobile app sprint<span>Two weeks, fixed price</span></th><td class="num">1</td><td class="num">€9,600.00</td><td class="num">€9,600.00</td></tr>
        <tr><th scope="row">Carrier tracking API<span>Integration, hours</span></th><td class="num">32</td><td class="num">€145.00</td><td class="num">€4,640.00</td></tr>
        <tr><th scope="row">QA and device testing<span>Hours</span></th><td class="num">18</td><td class="num">€95.00</td><td class="num">€1,710.00</td></tr>
        <tr><th scope="row">Cloud hosting<span>October usage</span></th><td class="num">1</td><td class="num">€486.20</td><td class="num">€486.20</td></tr>
      </tbody>
      <tfoot>
        <tr><th scope="row" colspan="3">Subtotal</th><td class="num">€16,436.20</td></tr>
        <tr><th scope="row" colspan="3">VAT 0%, reverse charge</th><td class="num">€0.00</td></tr>
        <tr class="nj-grand"><th scope="row" colspan="3">Total due (EUR)</th><td class="num">€16,436.20</td></tr>
      </tfoot>
    </table>
    <footer class="nj-foot">
      <div><h3 class="nj-k">Bank transfer</h3><p>IBAN EE38 2200 0000 0000 4821<br>BIC NJLBEE2X · Reference NJ-0412</p></div>
      <div><h3 class="nj-k">Note</h3><p>VAT is due from the customer under the reverse charge rule. Questions: <a href="mailto:[email protected]">[email protected]</a></p></div>
    </footer>
  </div>
</article>
</div>
<div><span class="lbl">Dark invoice card with partial payment</span>
<article class="nj nj-card nj-break card" data-bs-theme="dark" aria-labelledby="nj2-title">
  <div class="nj-cardtop">
    <div><h2 class="nj-k" id="nj2-title">Invoice NJ-0398</h2><p>Polaris Freight GmbH</p></div>
    <span class="badge rounded-pill nj-pill">Partly paid</span>
  </div>
  <p class="nj-big">€4,000.00 <span class="nj-left">left to pay</span></p>
  <div class="progress" role="progressbar" aria-labelledby="nj2-paid" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100"><div class="progress-bar" style="width:60%"></div></div>
  <p class="nj-paid" id="nj2-paid"><span>€6,000.00 of €10,000.00 paid</span><span>60%</span></p>
  <dl class="nj-dl">
    <div><dt>Issued</dt><dd>1 Oct 2026</dd></div>
    <div><dt>Due</dt><dd>24 Oct 2026, in 5 days</dd></div>
    <div><dt>Last payment</dt><dd>12 Oct 2026</dd></div>
  </dl>
  <div class="nj-actions d-print-none"><a class="btn nj-btn" href="#pay-nj-0398">Pay €4,000.00</a><a class="btn nj-btn nj-btn--ghost" href="#nj-0398-pdf">Download PDF</a></div>
</article>
</div>
CSS
/* Bootstrap dark colour mode (data-bs-theme="dark") on .card, .table, .btn, .badge and .progress; print switches the
   Bootstrap variables and the --nj tokens back to light.
   Dark invoice. On screen it is a dark card; in print every colour token flips to a light palette, so Print or
   Save as PDF gives black text on white paper and no ink-heavy background. Grey text keeps 4.5:1 on the dark card. */
.nj{--nj-bg:#141a24;--nj-raise:#1a2130;--nj-ink:#e6eaf2;--nj-muted:#a3acbd;--nj-line:#2a3446;--nj-accent:#a78bfa;--nj-accent-ink:#17112b;--nj-glow:rgba(167,139,250,.35);--nj-track:#2a3446;
  box-sizing:border-box;max-width:820px;margin:0 auto;color:var(--nj-ink);font-size:14px;line-height:1.55;color-scheme:dark}
.nj *,.nj *::before,.nj *::after{box-sizing:border-box}
:where(.nj) :is(h2,h3,p,dl,dd,table,address){margin:0}
.nj{--bs-body-bg:var(--nj-bg);--bs-body-color:var(--nj-ink);--bs-secondary-color:var(--nj-muted);--bs-border-color:var(--nj-line);--bs-card-bg:transparent;--bs-card-border-width:0}
.nj .table{--bs-table-bg:transparent;--bs-table-color:var(--nj-ink);--bs-table-border-color:var(--nj-line);margin:0}
.nj .table>tfoot>tr>*{border-bottom:0}
.nj .table caption{color:var(--nj-ink)}
.nj dt{font-weight:400}
.nj .btn{--bs-btn-font-size:inherit;--bs-btn-font-weight:700;--bs-btn-padding-x:18px;--bs-btn-padding-y:10px;--bs-btn-border-radius:10px;--bs-btn-border-width:0}
.nj .btn:focus-visible{box-shadow:none}
.nj .progress{--bs-progress-height:8px;--bs-progress-bg:var(--nj-track);--bs-progress-border-radius:99px;margin-top:14px}
.nj .progress-bar{background:linear-gradient(90deg,#7c3aed,var(--nj-accent));border-radius:99px}
.nj .badge{--bs-badge-font-size:.76rem;--bs-badge-font-weight:700;--bs-badge-padding-x:10px;--bs-badge-padding-y:4px}
.nj .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
.nj-toolbar{display:flex;justify-content:flex-end;margin-bottom:14px}
.nj-btn{display:inline-flex;align-items:center;gap:8px;padding:10px 18px;border:0;border-radius:10px;background:var(--nj-accent);color:var(--nj-accent-ink);font:inherit;font-weight:700;text-decoration:none;cursor:pointer;
  box-shadow:0 0 0 0 var(--nj-glow);transition:box-shadow .2s,background-color .2s}
.nj-btn[hidden]{display:none}
.nj-btn:hover{background:#b9a3fc;box-shadow:0 0 24px 2px var(--nj-glow)}
.nj-btn--ghost{background:transparent;color:var(--nj-ink);box-shadow:inset 0 0 0 1px #5d6880}
.nj-btn--ghost:hover{background:var(--nj-raise);box-shadow:inset 0 0 0 1px var(--nj-accent)}
.nj :is(a,button):focus-visible{outline:2px solid var(--nj-accent);outline-offset:3px}
.nj-sheet{position:relative;padding:44px;border:1px solid var(--nj-line);border-radius:20px;background:linear-gradient(180deg,var(--nj-raise),var(--nj-bg) 220px);box-shadow:0 30px 60px -30px rgba(0,0,0,.8)}
.nj-sheet::before{content:"";position:absolute;inset:-1px -1px auto;height:2px;border-radius:20px 20px 0 0;background:linear-gradient(90deg,transparent,var(--nj-accent),transparent)}
.nj-top{display:flex;flex-wrap:wrap;justify-content:space-between;gap:20px;padding-bottom:28px;border-bottom:1px solid var(--nj-line)}
.nj-brand{display:flex;align-items:center;gap:12px;font-weight:700;font-size:1.1rem}
.nj-brand svg{color:var(--nj-accent)}
.nj-k{font-size:.72rem;font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:var(--nj-muted);margin-bottom:6px}
.nj-title{font-size:2rem;font-weight:800;letter-spacing:-.03em;line-height:1.1}
.nj-num{color:var(--nj-muted);font-variant-numeric:tabular-nums}
.nj-num b{color:var(--nj-ink)}
.nj-head{text-align:right}
.nj-meta{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:24px;padding:26px 0}
.nj-meta address{font-style:normal;color:var(--nj-muted)}
.nj-meta strong{display:block;color:var(--nj-ink)}
.nj-dl{display:grid;gap:2px;font-variant-numeric:tabular-nums}
.nj-dl div{display:flex;justify-content:space-between;gap:10px}
.nj-dl dt{color:var(--nj-muted)}
.nj-dl dd{font-weight:600}
.nj-table{width:100%;border-collapse:collapse;font-variant-numeric:tabular-nums}
.nj-table caption{text-align:left;font-weight:700;padding-bottom:10px}
.nj-table thead th{padding:10px 12px;border-bottom:1px solid var(--nj-line);font-size:.72rem;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--nj-muted);text-align:left}
.nj-table tbody :is(th,td){padding:14px 12px;border-bottom:1px solid var(--nj-line);text-align:left;font-weight:400;vertical-align:top}
.nj-table tbody th{font-weight:600}
.nj-table tbody th span{display:block;font-weight:400;font-size:.84rem;color:var(--nj-muted)}
.nj-table :is(th,td):first-child{padding-left:0}
.nj-table :is(th,td):last-child{padding-right:0}
.nj-table .num{text-align:right;white-space:nowrap}
.nj-table tfoot :is(th,td){padding:5px 12px;text-align:right;font-weight:400}
.nj-table tfoot th{color:var(--nj-muted)}
.nj-table tfoot tr:first-child>*{padding-top:16px}
.nj-grand>*{padding-top:14px!important;font-size:1.25rem;font-weight:800!important;color:var(--nj-ink)!important}
.nj-grand td{color:var(--nj-accent)!important}
.nj-foot{display:grid;grid-template-columns:1fr 1fr;gap:24px;margin-top:30px;padding-top:22px;border-top:1px solid var(--nj-line);font-size:.86rem;color:var(--nj-muted)}
.nj-foot a{color:var(--nj-accent)}
/* compact card */
.nj-card{max-width:420px;padding:24px;border:1px solid var(--nj-line);border-radius:18px;background:var(--nj-bg)}
.nj-cardtop{display:flex;justify-content:space-between;align-items:flex-start;gap:12px}
.nj-pill{display:inline-flex;align-items:center;gap:6px;padding:3px 10px;border-radius:999px;border:1px solid #6b5a1f;background:#2a2410;color:#fcd34d;font-size:.76rem;font-weight:700}
.nj-pill::before{content:"";width:7px;height:7px;border-radius:50%;background:currentColor}
.nj-big{margin-top:14px!important;font-size:2.1rem;font-weight:800;letter-spacing:-.03em;font-variant-numeric:tabular-nums}
.nj-left{font-size:.9rem;font-weight:500;letter-spacing:0;color:var(--nj-muted)}
.nj-progress{display:block;width:100%;height:8px;margin-top:14px;border:0;border-radius:99px;background:var(--nj-track);overflow:hidden;-webkit-appearance:none;appearance:none}
.nj-progress::-webkit-progress-bar{background:var(--nj-track);border-radius:99px}
.nj-progress::-webkit-progress-value{background:linear-gradient(90deg,#7c3aed,var(--nj-accent));border-radius:99px}
.nj-progress::-moz-progress-bar{background:linear-gradient(90deg,#7c3aed,var(--nj-accent));border-radius:99px}
.nj-paid{display:flex;justify-content:space-between;margin-top:8px!important;font-size:.84rem;color:var(--nj-muted);font-variant-numeric:tabular-nums}
.nj-card .nj-dl{margin-top:18px;padding-top:16px;border-top:1px solid var(--nj-line)}
.nj-actions{display:flex;flex-wrap:wrap;gap:10px;margin-top:20px}
@media (max-width:720px){.nj-sheet{padding:28px 20px}.nj-meta{grid-template-columns:1fr 1fr}.nj-foot{grid-template-columns:1fr}.nj-head{text-align:left}}
@media (max-width:480px){.nj{font-size:13px}.nj-sheet{padding:24px 14px}.nj-meta{grid-template-columns:1fr}.nj-table thead th,.nj-table tbody :is(th,td){padding-left:4px;padding-right:4px}.nj-table{font-size:12.5px}}
/* print: light palette, no glow, no gradient, buttons hidden */
@page{size:auto;margin:12mm}
@media print{
  .nj{--nj-bg:#fff;--nj-raise:#fff;--nj-ink:#111827;--nj-muted:#4b5563;--nj-line:#d1d5db;--nj-accent:#5b21b6;--nj-track:#e5e7eb;--bs-body-bg:#fff;--bs-body-color:#111827;--bs-secondary-color:#4b5563;--bs-border-color:#d1d5db;color-scheme:light;max-width:none;font-size:10pt}
  .nj-toolbar,.nj-actions{display:none!important}
  .nj-sheet,.nj-card{padding:0;border:0;border-radius:0;background:#fff;box-shadow:none}
  .nj-sheet::before{display:none}
  .nj-break{break-before:page}
  .nj-table thead{display:table-header-group}
  .nj-table tr,.nj-foot{break-inside:avoid}
  .nj-pill{background:#fff;border-color:#92400e;color:#92400e}
  .nj .progress{border:1px solid #9ca3af;-webkit-print-color-adjust:exact;print-color-adjust:exact}
  .nj-foot a{color:inherit;text-decoration:none}
}
@media (prefers-reduced-motion:reduce){.nj-btn{transition:none}}
JavaScript
// Print buttons are hidden until JavaScript can run them; the browser's own Print menu works either way.
document.querySelectorAll('[data-print]').forEach(function (btn) {
  btn.hidden = false;
  btn.addEventListener('click', function () { window.print(); });
});

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#a3acbd] uppercase print:hidden">Dark invoice that prints light</span>
<article data-nj class="mx-auto max-w-[820px] text-[14px] leading-[1.55] text-nj-ink [color-scheme:dark] max-[480px]:text-[13px] print:max-w-none print:text-[10pt]" aria-labelledby="nj1-title">
  <div class="mb-3.5 flex justify-end print:hidden"><button class="inline-flex cursor-pointer items-center gap-2 rounded-[10px] bg-nj-accent px-[18px] py-2.5 font-bold text-nj-accent-ink transition-[box-shadow,background-color] duration-200 hover:bg-[#b9a3fc] hover:shadow-[0_0_24px_2px_rgba(167,139,250,.35)] focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-nj-accent motion-reduce:transition-none" type="button" data-print hidden><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 9V3h12v6M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"/><path d="M6 14h12v7H6z"/></svg>Print or save as PDF</button></div>
  <div class="relative rounded-[20px] border border-nj-line bg-linear-to-b from-nj-raise to-nj-bg to-[220px] p-11 shadow-[0_30px_60px_-30px_rgba(0,0,0,.8)] before:absolute before:inset-x-[-1px] before:top-[-1px] before:h-0.5 before:rounded-t-[20px] before:bg-linear-to-r before:from-transparent before:via-nj-accent before:to-transparent max-[720px]:px-5 max-[720px]:py-7 max-[480px]:px-3.5 max-[480px]:py-6 print:rounded-none print:border-0 print:bg-none print:bg-white print:p-0 print:shadow-none print:before:hidden">
    <header class="flex flex-wrap justify-between gap-5 border-b border-nj-line pb-7">
      <p class="flex items-center gap-3 text-[1.1rem] font-bold"><svg class="text-nj-accent" width="30" height="30" viewBox="0 0 30 30" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round" aria-hidden="true"><path d="M3 18c5-1 8-5 12-11 4 6 7 10 12 11-5 1-8 3-12 6-4-3-7-5-12-6z"/><circle cx="15" cy="15" r="1.6" fill="currentColor"/></svg>Nightjar Labs</p>
      <div class="text-right max-[720px]:text-left"><h2 class="text-[2rem] leading-[1.1] font-extrabold tracking-[-.03em]" id="nj1-title">Invoice</h2><p class="text-nj-muted tabular-nums">No. <b class="text-nj-ink">NJ-0412</b></p></div>
    </header>
    <div class="grid grid-cols-3 gap-6 py-[26px] max-[720px]:grid-cols-2 max-[480px]:grid-cols-1">
      <div><h3 class="mb-1.5 text-[.72rem] font-semibold tracking-[.12em] text-nj-muted uppercase">From</h3><address class="text-nj-muted not-italic"><strong class="block text-nj-ink">Nightjar Labs OÜ</strong>Kalamaja 14<br>10415 Tallinn, Estonia<br>VAT EE102938475</address></div>
      <div><h3 class="mb-1.5 text-[.72rem] font-semibold tracking-[.12em] text-nj-muted uppercase">Bill to</h3><address class="text-nj-muted not-italic"><strong class="block text-nj-ink">Polaris Freight GmbH</strong>Speicherstraße 7<br>20457 Hamburg, Germany<br>VAT DE298765432</address></div>
      <div><h3 class="mb-1.5 text-[.72rem] font-semibold tracking-[.12em] text-nj-muted uppercase">Details</h3><dl class="grid gap-0.5 tabular-nums [&_dd]:font-semibold [&_dt]:text-nj-muted [&>div]:flex [&>div]:justify-between [&>div]:gap-2.5"><div><dt>Issued</dt><dd>16 Oct 2026</dd></div><div><dt>Due</dt><dd>30 Oct 2026</dd></div><div><dt>Project</dt><dd>Fleet app</dd></div></dl></div>
    </div>
    <table class="w-full border-collapse tabular-nums max-[480px]:text-[12.5px] [&_.num]:text-right [&_.num]:whitespace-nowrap [&_:is(th,td):first-child]:pl-0 [&_:is(th,td):last-child]:pr-0">
      <caption class="pb-2.5 text-left font-bold">Sprint 14 · 1–14 Oct 2026</caption>
      <thead><tr class="[&>th]:border-b [&>th]:border-nj-line [&>th]:px-3 [&>th]:py-2.5 [&>th]:text-left [&>th]:text-[.72rem] [&>th]:font-semibold [&>th]:tracking-[.1em] [&>th]:text-nj-muted [&>th]:uppercase max-[480px]:[&>th]:px-1"><th scope="col">Description</th><th scope="col" class="num">Qty</th><th scope="col" class="num">Rate</th><th scope="col" class="num">Amount</th></tr></thead>
      <tbody class="[&_:is(th,td)]:border-b [&_:is(th,td)]:border-nj-line [&_:is(th,td)]:px-3 [&_:is(th,td)]:py-3.5 [&_:is(th,td)]:text-left [&_:is(th,td)]:align-top max-[480px]:[&_:is(th,td)]:px-1 [&_th]:font-semibold [&_th_span]:block [&_th_span]:text-[.84rem] [&_th_span]:font-normal [&_th_span]:text-nj-muted">
        <tr><th scope="row">Mobile app sprint<span>Two weeks, fixed price</span></th><td class="num">1</td><td class="num">€9,600.00</td><td class="num">€9,600.00</td></tr>
        <tr><th scope="row">Carrier tracking API<span>Integration, hours</span></th><td class="num">32</td><td class="num">€145.00</td><td class="num">€4,640.00</td></tr>
        <tr><th scope="row">QA and device testing<span>Hours</span></th><td class="num">18</td><td class="num">€95.00</td><td class="num">€1,710.00</td></tr>
        <tr><th scope="row">Cloud hosting<span>October usage</span></th><td class="num">1</td><td class="num">€486.20</td><td class="num">€486.20</td></tr>
      </tbody>
      <tfoot class="[&_:is(th,td)]:px-3 [&_:is(th,td)]:py-[5px] [&_:is(th,td)]:text-right [&_th]:font-normal [&_th]:text-nj-muted [&>tr:first-child>*]:pt-4">
        <tr><th scope="row" colspan="3">Subtotal</th><td class="num">€16,436.20</td></tr>
        <tr><th scope="row" colspan="3">VAT 0%, reverse charge</th><td class="num">€0.00</td></tr>
        <tr class="[&>*]:pt-3.5! [&>*]:text-[1.25rem] [&>*]:font-extrabold! [&>th]:text-nj-ink! [&>td]:text-nj-accent"><th scope="row" colspan="3">Total due (EUR)</th><td class="num">€16,436.20</td></tr>
      </tfoot>
    </table>
    <footer class="mt-[30px] grid grid-cols-2 gap-6 border-t border-nj-line pt-[22px] text-[.86rem] text-nj-muted max-[720px]:grid-cols-1 print:break-inside-avoid">
      <div><h3 class="mb-1.5 text-[.72rem] font-semibold tracking-[.12em] uppercase">Bank transfer</h3><p>IBAN EE38 2200 0000 0000 4821<br>BIC NJLBEE2X · Reference NJ-0412</p></div>
      <div><h3 class="mb-1.5 text-[.72rem] font-semibold tracking-[.12em] uppercase">Note</h3><p>VAT is due from the customer under the reverse charge rule. Questions: <a class="text-nj-accent underline focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-nj-accent print:text-inherit print:no-underline" href="mailto:[email protected]">[email protected]</a></p></div>
    </footer>
  </div>
</article>
</div>
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#a3acbd] uppercase print:hidden">Dark invoice card with partial payment</span>
<article data-nj class="mx-auto max-w-[420px] rounded-[18px] border border-nj-line bg-nj-bg p-6 text-[14px] leading-[1.55] text-nj-ink [color-scheme:dark] print:max-w-none print:break-before-page print:rounded-none print:border-0 print:p-0" aria-labelledby="nj2-title">
  <div class="flex items-start justify-between gap-3">
    <div><h2 class="mb-1.5 text-[.72rem] font-semibold tracking-[.12em] text-nj-muted uppercase" id="nj2-title">Invoice NJ-0398</h2><p>Polaris Freight GmbH</p></div>
    <span class="inline-flex items-center gap-1.5 rounded-full border border-[#6b5a1f] bg-[#2a2410] px-2.5 py-[3px] text-[.76rem] font-bold text-[#fcd34d] before:size-[7px] before:rounded-full before:bg-current print:border-[#92400e] print:bg-white print:text-[#92400e]">Partly paid</span>
  </div>
  <p class="mt-3.5 text-[2.1rem] font-extrabold tracking-[-.03em] tabular-nums">€4,000.00 <span class="text-[.9rem] font-medium tracking-normal text-nj-muted">left to pay</span></p>
  <progress class="mt-3.5 block h-2 w-full appearance-none overflow-hidden rounded-full border-0 bg-nj-track [&::-moz-progress-bar]:rounded-full [&::-moz-progress-bar]:bg-linear-to-r [&::-moz-progress-bar]:from-[#7c3aed] [&::-moz-progress-bar]:to-nj-accent [&::-webkit-progress-bar]:rounded-full [&::-webkit-progress-bar]:bg-nj-track [&::-webkit-progress-value]:rounded-full [&::-webkit-progress-value]:bg-linear-to-r [&::-webkit-progress-value]:from-[#7c3aed] [&::-webkit-progress-value]:to-nj-accent print:border print:border-[#9ca3af] print:[print-color-adjust:exact]" value="60" max="100" aria-labelledby="nj2-paid">60%</progress>
  <p class="mt-2 flex justify-between text-[.84rem] text-nj-muted tabular-nums" id="nj2-paid"><span>€6,000.00 of €10,000.00 paid</span><span>60%</span></p>
  <dl class="mt-[18px] grid gap-0.5 border-t border-nj-line pt-4 tabular-nums [&_dd]:font-semibold [&_dt]:text-nj-muted [&>div]:flex [&>div]:justify-between [&>div]:gap-2.5">
    <div><dt>Issued</dt><dd>1 Oct 2026</dd></div>
    <div><dt>Due</dt><dd>24 Oct 2026, in 5 days</dd></div>
    <div><dt>Last payment</dt><dd>12 Oct 2026</dd></div>
  </dl>
  <div class="mt-5 flex flex-wrap gap-2.5 print:hidden"><a class="inline-flex items-center gap-2 rounded-[10px] bg-nj-accent px-[18px] py-2.5 font-bold text-nj-accent-ink transition-[box-shadow,background-color] duration-200 hover:bg-[#b9a3fc] hover:shadow-[0_0_24px_2px_rgba(167,139,250,.35)] focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-nj-accent motion-reduce:transition-none" href="#pay-nj-0398">Pay €4,000.00</a><a class="inline-flex items-center gap-2 rounded-[10px] px-[18px] py-2.5 font-bold text-nj-ink shadow-[inset_0_0_0_1px_#5d6880] transition-[box-shadow,background-color] duration-200 hover:bg-nj-raise hover:shadow-[inset_0_0_0_1px_var(--color-nj-accent)] focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-nj-accent motion-reduce:transition-none" href="#nj-0398-pdf">Download PDF</a></div>
</article>
</div>
CSS
@theme {
  --color-nj-bg: #141a24;
  --color-nj-raise: #1a2130;
  --color-nj-ink: #e6eaf2;
  --color-nj-muted: #a3acbd;
  --color-nj-line: #2a3446;
  --color-nj-accent: #a78bfa;
  --color-nj-accent-ink: #17112b;
  --color-nj-track: #2a3446;
}
/* In print the colour tokens flip to a light palette, so the dark invoice prints as black on white. */
@page { size: auto; margin: 12mm; }
@media print {
  [data-nj] { --color-nj-bg: #fff; --color-nj-raise: #fff; --color-nj-ink: #111827; --color-nj-muted: #4b5563; --color-nj-line: #d1d5db; --color-nj-accent: #5b21b6; --color-nj-track: #e5e7eb; color-scheme: light; }
  thead { display: table-header-group; }
  tr { break-inside: avoid; }
}
JavaScript
// Print buttons are hidden until JavaScript can run them; the browser's own Print menu works either way.
document.querySelectorAll('[data-print]').forEach(function (btn) {
  btn.hidden = false;
  btn.addEventListener('click', function () { window.print(); });
});

About this set

A dark invoice and a dark invoice card for night-themed apps that still print as a normal light document. Dark invoice that prints light is a software studio’s invoice in euros: a violet glow line on a deep slate card, From, Bill to and details columns with VAT numbers, a sprint line items table, a reverse charge VAT line, a total in the accent colour, and bank details. Dark invoice card with partial payment shows an invoice that is partly paid: a status pill, the amount left to pay, a native progress element for the share already paid, dates, and Pay and Download PDF links. Everything is driven by colour tokens, and an @media print block flips every token to a light palette, so Print or Save as PDF gives black text on white paper with no ink-heavy background, glow or gradient, and the buttons disappear. Grey text keeps at least 4.5:1 against the dark card, and color-scheme:dark keeps native controls dark on screen. The Bootstrap version runs in data-bs-theme=”dark” and switches Bootstrap’s own variables back to light in print.

What’s included

  • Dark on screen, light in print and PDF through token swaps
  • Native progress element for a partly paid invoice
  • Reverse charge VAT invoice in euros
  • Bootstrap version uses data-bs-theme="dark"
  • Print button hidden in print and until JavaScript runs

Accessibility

The invoices are articles named by their headings, the items are a captioned table with scoped headers, and the progress bar is labelled by the visible paid text. Muted text on the dark card measures over 4.5:1, links and buttons show a violet focus outline, and the glow transition stops under prefers-reduced-motion.

Customise it

Change --nj-bg, --nj-ink, --nj-muted, --nj-line and --nj-accent on .nj, and their light values in the @media print block. Bootstrap maps them onto --bs-body-bg and the other theme variables; in Tailwind edit the nj-* colours in @theme and the print overrides on [data-nj].