
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
keeptrack-css
Advanced tools
Read computed CSS property values from elements and expose them as CSS custom properties (variables)
KeepTrack reads computed CSS property values from elements and exposes them as CSS custom properties (variables). This lets you use values like an element's rendered height or background-color elsewhere in your CSS — something not normally possible.
It automatically updates when elements resize, when the DOM changes, or (optionally) on every animation frame for non-layout properties.
--scrollbar-width custom property is available in the document root so you can do things like calc(100vw - var(--scrollbar-width)).data-keeptrack-scroll-padding to one or more sticky or fixed elements, anchor links will take this into account when jumping to that anchor. Also works on pageload and using back/forward navigation.display: grid or even subgrid can't help you out there.npm install keeptrack-css
Include via a <script> tag, CommonJS, or ES modules:
<script src="keepTrack.min.js"></script>
// CommonJS
const KeepTrack = require('keeptrack-css');
// ES modules
import KeepTrack from 'keeptrack-css';
const tracker = KeepTrack();
KeepTrackis a factory function —newis optional and has no effect.
Add data-keeptrack to any HTML element with a comma-separated list of CSS properties to track:
<div data-keeptrack="height">...</div>
This sets --height as an inline CSS variable on the element itself, updated whenever the element resizes.
You can track multiple properties:
<div data-keeptrack="height, width, padding-top">...</div>
The target for the CSS variable depends on the element's attributes:
<!-- Input -->
<div data-keeptrack="height">...</div>
<!-- Result -->
<div data-keeptrack="height" style="--height: 64px">...</div>
Multiple properties:
<!-- Input -->
<div data-keeptrack="height, width, padding-top">...</div>
<!-- Result -->
<div data-keeptrack="height, width, padding-top" style="--height: 64px; --width: 320px; --padding-top: 16px">...</div>
id or data-keeptrack-id)If the element has an id, the variable is set on :root with the id as a prefix:
<!-- Input -->
<header id="site-header" data-keeptrack="height">...</header>
<!-- Result: sets --site-header-height on :root -->
<html style="--site-header-height: 80px">
...
<header id="site-header" data-keeptrack="height">...</header>
...
</html>
You can also set a custom prefix with data-keeptrack-id (useful when you cannot set a real id):
<!-- Input -->
<header data-keeptrack="height" data-keeptrack-id="site-header">...</header>
<!-- Result: sets --site-header-height on :root -->
<html style="--site-header-height: 80px">
...
<header data-keeptrack="height" data-keeptrack-id="site-header">...</header>
...
</html>
main {
padding-top: var(--site-header-height);
}
data-keeptrack-target-parent)You can set the variable on a parent or any other element. The attribute accepts either a number (levels to traverse up) or a CSS selector.
<!-- Traverse 2 levels up -->
<!-- Input -->
<div class="grandparent">
<div class="parent">
<div data-keeptrack="height" data-keeptrack-target-parent="2">...</div>
</div>
</div>
<!-- Result: --height is set on .grandparent -->
<div class="grandparent" style="--height: 64px">
<div class="parent">
<div data-keeptrack="height" data-keeptrack-target-parent="2">...</div>
</div>
</div>
<!-- Closest ancestor matching the selector -->
<!-- Input -->
<div class="wrapper">
<div>
<div data-keeptrack="height" data-keeptrack-target-parent=".wrapper">...</div>
</div>
</div>
<!-- Result: --height is set on .wrapper -->
<div class="wrapper" style="--height: 64px">
<div>
<div data-keeptrack="height" data-keeptrack-target-parent=".wrapper">...</div>
</div>
</div>
When using a selector, KeepTrack first tries el.closest(selector) to find the nearest ancestor. If no ancestor matches, it falls back to document.querySelector(selector).
If the element has data-keeptrack-id or an id, the variable name includes that prefix (data-keeptrack-id takes precedence):
<!-- Input -->
<div class="layout">
<div id="sidebar" data-keeptrack="width" data-keeptrack-target-parent=".layout">...</div>
</div>
<!-- Result: --sidebar-width is set on .layout -->
<div class="layout" style="--sidebar-width: 250px">
<div id="sidebar" data-keeptrack="width" data-keeptrack-target-parent=".layout">...</div>
</div>
By default, KeepTrack sets --scrollbar-width on :root, updated on viewport resize. You can also enable --scrollbar-height.
--scrollbar-width is the width (thickness) of the vertical scrollbar--scrollbar-height is the height (thickness) of the horizontal scrollbarKeepTrack({
scrollbarWidth: true, // default: true
scrollbarHeight: true // default: false
});
.full-width {
width: calc(100vw - var(--scrollbar-width));
}
Add data-keeptrack-scroll-padding to any element to automatically set scroll-padding-top on :root. This fixes anchor links (<a href="#section">) being hidden behind sticky headers. The element does not need data-keeptrack — data-keeptrack-scroll-padding works on its own.
<!-- Input -->
<header id="site-header" data-keeptrack="height" data-keeptrack-scroll-padding>
...
</header>
<main>
<section id="about">...</section>
</main>
<!-- Result: scroll-padding-top is set on :root to the header's height -->
<html style="--site-header-height: 80px; scroll-padding-top: 80px">
...
</html>
If multiple elements have data-keeptrack-scroll-padding, their heights are summed:
<header data-keeptrack="height" data-keeptrack-scroll-padding>...</header>
<nav data-keeptrack="height" data-keeptrack-scroll-padding>...</nav>
<!-- scroll-padding-top = header height + nav height -->
When detectSticky is enabled, only elements that are currently stuck contribute to scroll-padding-top. When clicking an anchor link, KeepTrack predicts which sticky elements will be stuck at the target position and adjusts scroll-padding-top before the browser scrolls. This ensures correct scroll offsets even when a sticky element's container ends before the anchor target.
Enable detectSticky to detect when position: sticky elements become stuck. KeepTrack checks on scroll and exposes the state as:
data-keeptrack-stuck attribute on the element (for CSS targeting)--[prefix]-stuck CSS variable on :root (1 when stuck, 0 when not) if the element has an id or data-keeptrack-id--stuck CSS variable on the element itself if it has no idKeepTrack({ detectSticky: true });
<!-- Input -->
<header id="site-header" data-keeptrack="height" style="position: sticky; top: 0">
...
</header>
<!-- Result when stuck -->
<html style="--site-header-height: 80px; --site-header-stuck: 1">
...
<header id="site-header" data-keeptrack="height" data-keeptrack-stuck style="position: sticky; top: 0">
...
</header>
...
</html>
/* Style changes when stuck */
[data-keeptrack-stuck] {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
The onChange callback also fires for sticky state changes with prop set to "stuck":
KeepTrack({
detectSticky: true,
onChange(el, prop, value) {
if (prop === 'stuck') {
console.log(el, value === '1' ? 'is stuck' : 'is not stuck');
}
}
});
top resolution (calc/var) and cachingKeepTrack resolves sticky element top values to pixels for sticky detection and anchor prediction. It supports
px, em, rem, %, and complex values like calc(...) and var(...).
Resolved top values are cached and only recomputed on resize, DOM mutations, or recalculate(). This caching only
affects sticky detection and anchor prediction. If your top value actually changes during scroll (rare), you can opt
into per-frame updates:
KeepTrack({ stickyTopDynamic: true });
KeepTrack({
scrollbarWidth: true, // Track scrollbar width as --scrollbar-width on :root
scrollbarHeight: false, // Track scrollbar height as --scrollbar-height on :root
debounceTime: 250, // Debounce delay in ms for resize and DOM changes
poll: false, // Enable requestAnimationFrame polling for non-layout changes
detectSticky: false, // Detect when sticky elements become stuck
stickyTopDynamic: false, // Update sticky top values every frame
onChange: null // Callback when a tracked value changes
});
pollEnable this to track properties that don't affect element size, like background-color, color, or font-size. When enabled, KeepTrack checks all tracked values every animation frame and only updates when a value has changed.
KeepTrack({ poll: true });
If the browser doesn't support ResizeObserver, enable poll to keep values in sync with size changes.
detectStickyEnable this to detect when position: sticky elements are stuck. Uses a passive scroll listener throttled with requestAnimationFrame for minimal performance impact.
KeepTrack({ detectSticky: true });
stickyTopDynamicWhen false (default), KeepTrack caches resolved sticky top values for performance. This only affects sticky
detection and anchor prediction. Set to true if your top value changes during scroll.
KeepTrack({ stickyTopDynamic: true });
onChangeCalled whenever a tracked value changes (including sticky state). Receives the element, the property name, and the new value:
KeepTrack({
onChange(el, prop, value) {
console.log(`${prop} changed to ${value}`, el);
}
});
init(options)Re-initializes with new options. Cleans up the previous instance first.
tracker.init({ poll: true });
destroy()Removes all event listeners, observers, and stops polling. Also cleans up all CSS variables, scroll-padding-top, and data-keeptrack-stuck attributes set by KeepTrack.
tracker.destroy();
recalculate()Manually trigger a recalculation of all tracked elements and scrollbar dimensions.
tracker.recalculate();
observe(element)Programmatically start tracking an element (must have a data-keeptrack attribute):
tracker.observe(document.querySelector('.my-element'));
unobserve(element)Stop tracking an element, remove its CSS variables, and clean up its caches:
tracker.unobserve(document.querySelector('.my-element'));
KeepTrack uses multiple mechanisms to detect changes:
[data-keeptrack] elementsdata-keeptrack is dynamically added/removed from elements, or when their data-keeptrack-target-parent, data-keeptrack-scroll-padding, data-keeptrack-id, or id attributes changedetectSticky: true) detects when position: sticky elements become stuck, using a passive listener throttled with requestAnimationFramepoll: true) catches computed style changes that don't affect element size, like color or font changesAll paths use a value cache to avoid unnecessary setProperty calls when nothing has changed. Calling destroy() or unobserve() fully cleans up any CSS variables and attributes that were set.
FAQs
Read computed CSS property values from elements and expose them as CSS custom properties (variables)
We found that keeptrack-css demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.