JSPM

overflow-manager

1.0.2
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 6
  • Score
    100M100P100Q67369F
  • License MIT

Detects fixed-height containers that overflow and redistributes excess children to user-defined targets. Built for Astro + Puppeteer PDF pipelines.

Package Exports

  • overflow-manager
  • overflow-manager/browser
  • overflow-manager/browser/min

Readme

@victorarangodev/overflow-manager

Detects fixed-height containers that overflow and redistributes excess child elements to user-defined targets. Built for Astro → Puppeteer → PDF pipelines.

npm install @victorarangodev/overflow-manager

How it works

The library runs inside the browser (Chromium via Puppeteer) using real getBoundingClientRect() measurements after all CSS, fonts, and images have loaded. It iterates in passes until no container overflows, then signals Puppeteer that the PDF can be generated.

Astro build → HTML + script tag
                   ↓
         Puppeteer opens HTML
                   ↓
  overflow-manager runs in Chromium:
    1. Measures each [data-overflow-container]
    2. Moves overflowing children to [data-overflow-target]
    3. Reveals [data-overflow-page] wrappers that received content
    4. Repeats until stable
                   ↓
  window.overflowManager.done === true
                   ↓
         Puppeteer generates PDF

HTML attributes

Attribute On Required Description
data-overflow-container source div Marks a container that can overflow
data-overflow-id source div Unique ID linking source → target
data-overflow-height source div No Max height in px. Overrides CSS height
data-overflow-target target div Receives overflowing children
data-overflow-page page wrapper No Hidden initially; revealed only if its target receives content

Usage in Astro

Basic — single overflow page

---
// [id].astro
---

<!-- Page 1 — always visible -->
<Layout>
  <Section height="450px" />

  <Section height="450px">
    <div
      data-overflow-container
      data-overflow-id="body-p1"
      data-overflow-height="700"
      style="height: 700px;"
    >
      <p>Item 1</p>
      <p>Item 2</p>
      <!-- more items... -->
    </div>
  </Section>

  <Section height="450px" />
</Layout>

<!-- Page 2 — hidden by default, revealed only if body-p1 overflows -->
<div data-overflow-page="body-p1" style="display: none">
  <Layout>
    <Section height="950px">
      <div
        data-overflow-target="body-p1"
        data-overflow-container
        data-overflow-id="body-p2"
        data-overflow-height="700"
        style="height: 700px;"
      >
      </div>
    </Section>
  </Layout>
</div>

<script src="/overflow-manager.js"></script>

Chained pages — unlimited overflow

<!-- Page 1 -->
<Layout>
  <div data-overflow-container data-overflow-id="p1"
       data-overflow-height="700" style="height:700px">
    <!-- content -->
  </div>
</Layout>

<!-- Page 2 -->
<div data-overflow-page="p1" style="display:none">
  <Layout>
    <div data-overflow-target="p1"
         data-overflow-container data-overflow-id="p2"
         data-overflow-height="700" style="height:700px">
    </div>
  </Layout>
</div>

<!-- Page 3 -->
<div data-overflow-page="p2" style="display:none">
  <Layout>
    <div data-overflow-target="p2"
         data-overflow-container data-overflow-id="p3"
         data-overflow-height="700" style="height:700px">
    </div>
  </Layout>
</div>

Including the script

Option A — public folder (recommended)

cp node_modules/@victorarangodev/overflow-manager/dist/overflow-manager.browser.global.js \
   public/overflow-manager.js
<script src="/overflow-manager.js"></script>

Option B — ES module

<script>
  import { OverflowManager } from '@victorarangodev/overflow-manager';
  const manager = new OverflowManager({ settleDelay: 50 });
  window.overflowManager = manager;
  manager.runAsync();
</script>

Puppeteer integration

const page = await browser.newPage();
await page.goto(`file://${htmlPath}`, { waitUntil: 'networkidle0' });

await page.waitForFunction(
  () => window.overflowManager?.done === true,
  { timeout: 15_000 }
);

await page.pdf({ path: outputPath, printBackground: true });

Always use waitUntil: 'networkidle0' — ensures web fonts are loaded before measurement.


API

new OverflowManager(options?)

Option Type Default Description
settleDelay number 0 ms between async passes (0 = double rAF)
maxPasses number 100 Safety cap on iterations
onPass fn Called after each pass with (pass, moved)
onDone fn Called when processing finishes

Methods

Method Returns Description
.run() OverflowManagerResult Synchronous execution
.runAsync() Promise<Result> Async execution — recommended

Result

interface OverflowManagerResult {
  passes: number;      // passes executed
  totalMoved: number;  // total elements redistributed
  done: boolean;       // always true
}

DOM event

document.addEventListener('overflow-manager:done', (e) => {
  console.log(e.detail); // OverflowManagerResult
});

Important notes

  • No overflow: hidden on the source container — children must visually protrude for getBoundingClientRect() to detect them.
  • Direct children only — one child per logical unit (paragraph, card, row).
  • Web fonts — use waitUntil: 'networkidle0' or set settleDelay: 100.
  • data-overflow-page — place it outside and after its Layout, in DOM order.

License

MIT © victorarangodev