css-box-model

Computes precise margin, border, padding, and content box measurements for a DOM element, going beyond getBoundingClientRect.

Library
npm
v1.2.1
170stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
35/100Needs Attention
Development Activity0
Maintenance20
Community40
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
68/100Good
Architecture78
Code Quality80
Innovation60
Learning Curve55

css-box-model is a small utility that returns accurate, well-named CSS box model information for a DOM element. Rather than relying solely on Element.getBoundingClientRect(), which only reports the border box, it reads computed styles to also produce the margin box, padding box, and content box — each expressed as a Rect with top/right/bottom/left, width/height, x/y, and a computed center point.

The library exposes both DOM-reading helpers (getBox) and pure calculation functions (calculateBox, createBox, offset, withScroll) that operate on plain objects, making it possible to compute box models without touching the DOM at all — useful for testing or for recalculating a box after a scroll or drag offset. It was built by Alex Reardon to support precise positioning math in react-beautiful-dnd and ships with both Flow types and a hand-maintained TypeScript declaration file.

What You Get

  • getBox(el) — reads an element’s getBoundingClientRect() and computed styles to return a complete BoxModel with margin, border, padding, and content boxes
  • calculateBox(borderBox, styles) — computes a BoxModel from an already-obtained rect and CSSStyleDeclaration, skipping a second DOM read
  • createBox({ borderBox, margin, border, padding }) — builds a BoxModel from plain spacing objects with no DOM access at all
  • offset(box, change) and withScroll(box, scroll?) — recompute a box model after a position or scroll shift
  • Shipped Flow types and a src/index.d.ts TypeScript declaration file for the BoxModel, Rect, Spacing, and Position types

Common Use Cases

  • Drag-and-drop libraries - need exact border/margin/padding boxes to compute collision and drop-target boundaries as elements move
  • Custom scroll or virtualization code - recalculating an element’s box after a scroll offset without re-reading the DOM
  • Layout measurement utilities - building tooltips, popovers, or resize handles that need more than the raw border box from getBoundingClientRect
  • Testing layout logic - using createBox to construct box models from fixed values in unit tests, without a real DOM

Under The Hood

Architecture The library is a single-file module (src/index.js, ~243 lines) exporting a flat set of composable pure functions — getRect, expand, shrink, shift, createBox, offset, withScroll, calculateBox — plus one DOM-touching entry point, getBox. Only getBox and calculateBox actually read from the browser (getBoundingClientRect, getComputedStyle, window.pageXOffset); every other function is pure, operating on plain Spacing/Rect objects, which is what lets offset, withScroll, and createBox be used and unit-tested with no DOM at all. There’s no internal layering or dependency injection at this scale — the pipeline is raw values → Spacing → Rect → composed BoxModel, all inside one module. The single external dependency, tiny-invariant, is used only for a narrow runtime assertion inside the pixel parser.

Tech Stack Written in JavaScript with inline Flow type annotations rather than TypeScript, with a hand-maintained src/index.d.ts shipped separately for TypeScript consumers via the package’s types field. Built with Rollup (rollup-plugin-babel, rollup-plugin-node-resolve, rollup-plugin-replace, rollup-plugin-terser) into both CommonJS and ESM bundles, plus a generated Flow declaration file for the CJS build; Babel’s preset-env and preset-flow strip the Flow syntax during compilation. Testing runs on Jest. CI is Travis, running a validate step (Prettier check plus flow check) followed by the test suite, with greenkeeper-lockfile handling automated dependency-lock updates.

Code Quality Eight Jest spec files cover the module one exported function at a time (calculate-box, expand, shrink, get-rect, offset, with-scroll, pixel-parsing), sharing a common test fixture — thorough coverage for a single-file library. Error handling is narrow and deliberate: the pixel parser returns 0 for non-pixel computed-style values (documented inline, covering display:none and jsdom quirks) and only throws via tiny-invariant when a parsed value is genuinely NaN. Naming mirrors the CSS spec directly (marginBox/borderBox/paddingBox/contentBox), and Flow’s exact object types plus a CI-enforced flow check provide type safety; Prettier enforces formatting, though no ESLint config is present.

What Makes It Unique getBoundingClientRect() alone only ever returns the border box; this library fills that well-known gap by deriving the margin, padding, and content boxes from computed styles. Its distinguishing design choice is separating DOM-reading (getBox) from pure calculation (calculateBox, createBox, offset), letting a consumer recompute a box model after a scroll or drag delta without touching the DOM again — the exact pattern its author needed for react-beautiful-dnd’s drag-collision math. The box arithmetic itself isn’t novel, but the pure, composable, DOM-optional API shape is a deliberate and uncommon choice for a browser-measurement utility.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search