The Weight of What You Did Not Write: A Zero-Dependency Philosophy for Web Performance

An investigation into what the browser can now do on its own, what it still cannot, and why every line of code you did not ship is a line no one has to pay for.
I. The Phone That Waits
Picture a phone that is not new. Its screen is a little scratched, its battery holds perhaps two-thirds of what it once did, and somewhere in its modest allotment of memory, a browser tab is trying to render a website. The website was built by people who have never held this particular phone, tested on networks faster than the one it is currently gasping through, on machines with more memory than this device will ever have. The page requests a framework runtime, a component library, an animation library, a date-formatting library, an icon library, a state-management library, and, buried three dependencies deep in a lockfile nobody has opened in a year, a scattering of small utilities nobody remembers choosing. Each of these was a reasonable decision on its own, made by a reasonable developer, on a reasonable day. Together, they are a tax — paid not by the person who wrote the code, but by the person holding the phone, in seconds of their life and megabytes of their data plan, waiting for a button to become clickable.
This is the quiet cruelty of modern web development: the cost of convenience is almost always deferred, and almost always deferred onto someone else's device.
The proposition of this piece is not that JavaScript is bad, or that libraries are sin. It is narrower and more useful than that: a great deal of what the modern web ships as a dependency — a downloaded, parsed, executed piece of someone else's code — is now available as a platform capability, built into the browser itself, requiring no download at all. Knowing the difference, and having the discipline to prefer the platform when it will do, is what a zero-dependency philosophy actually means in practice. It is not asceticism for its own sake. It is refusing to make someone else's low-end phone pay for a convenience you could have gotten for free.
II. The Ground Truth: What the Web Actually Weighs in 2026
The Page Has Been Gaining Weight for a Decade, and It Has Not Stopped
HTTP Archive's 2025 Web Almanac reports a median home-page weight of 2.56 megabytes on mobile and 2.86 megabytes on desktop. Its breakdown by resource type puts JavaScript at a median 632 kilobytes on mobile home pages and 697 kilobytes on desktop home pages. These are crawl measurements, not a measure of what every visitor downloads or what must load before content appears.
The comparison is useful without a game-size anecdote: a web page's transferred bytes and the work required to parse and execute its JavaScript both matter, especially on slower connections and less powerful devices.
Interaction to Next Paint Is Where the Bloat Becomes Visible to Users
In March 2024, Google replaced First Input Delay with Interaction to Next Paint (INP) as a Core Web Vital, alongside Largest Contentful Paint and Cumulative Layout Shift. FID measured the delay before a page's first interaction handler ran; INP assesses responsiveness across click, tap, and keyboard interactions during a visit, reporting the longest interaction after ignoring certain outliers on pages with many interactions.
The Chrome User Experience Report (CrUX) provides field data for eligible pages and origins; it is not a census of every website. A “good” INP is 200 milliseconds or less at the 75th percentile of field page loads. INP can be affected by JavaScript work, but it is a responsiveness measure—not a direct measure of bundle size or proof that dependencies caused a particular score.
jQuery Turned Twenty, and Is Still on Two-Thirds of the Web
jQuery was introduced in January 2006, and the project released jQuery 4.0.0 in January 2026, its first major release in nearly ten years. W3Techs' daily survey currently detects jQuery on about 64% of websites and on most surveyed sites where it detects a JavaScript library. That usage statistic does not establish why a site includes the library or whether its code is actively used.
A library's cost depends on the version, build, compression, and how a site uses it. Avoid assigning a fixed interaction delay or security risk to jQuery without a reproducible measurement of the specific site and version; evaluate the shipped bundle and keep dependencies updated.

Every Dependency Is Also a Trust Relationship, and Trust Has Been Breaking
The bytes are one cost. A deep dependency tree also increases the number of packages and publishing accounts a project relies on. On 8 September 2025, an attacker took control of the npm account of a maintainer known as Qix and published malicious releases of popular packages, including chalk and debug. Wiz reported that the malicious code targeted cryptocurrency transactions in browsers. The incident is a concrete reminder to review dependency changes and use appropriate supply-chain safeguards; it does not mean every dependency is unsafe.
A package manager makes dependency updates convenient, but update automation should be paired with review and controls appropriate to the project. This is a general supply-chain consideration; it does not justify attributing separate incidents to the same actor or describing a particular package as compromised without a reliable incident source.
None of this is an argument that open-source software is untrustworthy in general. It is an argument that dependency count is a security metric, not merely a convenience metric — every additional package in a lockfile is another maintainer whose inbox, another CI token, another supply-chain link that a project's security now silently depends on, whether or not anyone remembers agreeing to that dependency.
III. The Principle: Prefer the Platform
None of the preceding is an argument for writing everything by hand in raw JavaScript out of stubbornness. It is an argument for a specific ordering of preference, applied deliberately at the moment a developer reaches for a new dependency: first, ask whether the browser platform itself — native CSS, or a built-in browser API — already does this, well, for free, with no download, no parse cost, and no third-party maintainer standing between your users and a working page. Only when the honest answer is no should a library enter the conversation, and even then, the smallest, most narrowly scoped one available should win over the general-purpose framework.
By 2026, the honest answer to “does the platform already do this” has changed for a genuinely large number of interface patterns that, as recently as three or four years ago, reliably required JavaScript. What follows is a survey of where that line currently sits — not where a marketing page claims it sits, but where actual, checkable browser support sits as of the middle of 2026.
IV. What the Browser Now Does For You
Layout That Responds to Its Container, Not the Viewport
Container queries let styles respond to a container's size rather than the overall viewport. They are supported in current major browsers, but teams should check compatibility for their supported browser range. A component that uses ResizeObserver only to measure a parent and toggle a class may be expressible as a native @container query.
Conditional Styling Without a Class-Toggling Script
The :has() selector allows an element to be styled based on matching descendants. It is supported in current major browsers, though projects with older-browser requirements should check compatibility. Some class-toggling scripts can be replaced with CSS selectors where the behavior is purely presentational.
Nesting, Without a Preprocessor
Native CSS nesting — writing selectors inside one another the way Sass long allowed — is now supported directly by the browser, meaning a meaningful share of projects that pulled in a Sass or PostCSS build step purely for nesting syntax no longer need to. The nesting itself ships with zero runtime weight, because it is compiled away by the browser's own CSS parser, not by a bundler.
Page and State Transitions, Without a Router Library's Animation Layer
The View Transitions API can animate between DOM states and, in supported browsers, between document navigations. Feature support and behavior vary by browser and version, so check compatibility before making it the only navigation experience. View transitions can replace some animation code, but they do not replace a router's navigation, data-loading, or history responsibilities. Respect users' prefers-reduced-motion settings when adding motion.
Scroll-Linked Effects, Off the Main Thread
Scroll-driven animations tie effects to scroll position rather than elapsed time. CSS can avoid some JavaScript scroll-handler work, but support and performance depend on the browser and the animated properties; not every CSS scroll animation runs entirely off the main thread. Check compatibility and profile on representative devices.
Popovers, Menus, and Positioning with Browser APIs
The Popover API provides browser-managed top-layer display and light-dismiss for popovers; it is suitable for some non-modal menus and panels, but it does not itself supply modal-dialog behavior. Use a modal-capable element such as <dialog> when the interaction is modal. CSS Anchor Positioning can tether an element to another element, but browser support is still more variable than for the established CSS features above. Check current compatibility and provide a fallback when needed.
Colour and Theming Without a Preprocessor's Colour Functions
Native CSS now includes color-mix(), the light-dark() function for one-line light/dark theme switching, and the OKLCH colour space for perceptually uniform colour manipulation — collectively covering a large share of what Sass's colour functions, or a small JavaScript theming utility, used to be reached for.
Breaking Up Long Tasks Without a Scheduling Library
The platform also offers native tools for common tasks. Where supported, scheduler.yield() can yield control during long-running work; check browser support and provide a fallback. IntersectionObserver and ResizeObserver are browser APIs that can handle visibility and size observation without a third-party package. The Web Animations API supports JavaScript-controlled animation through element.animate(), while specialized libraries may still be useful for complex sequencing or physics-based motion.
Native Elements That Used to Be Widgets
A modal <dialog>, opened with showModal(), provides native modal behavior and focus management; light-dismiss behavior depends on supported dialog features and configuration. The <details> and <summary> elements provide a native disclosure widget without JavaScript. HTML constraint validation (required, pattern, and type="email") covers basic field-level checks, but not every form library use case.
V. Where the Platform Genuinely Is Not Enough Yet
A zero-dependency philosophy that pretends the platform is finished is not rigour, it is denial, and denial produces worse outcomes than honest dependency management does. As of mid-2026, several real gaps remain, and pretending otherwise sends teams chasing broken production experiences in the name of purity:
- Complex, cross-field, asynchronous form validation at genuine scale still generally benefits from a small, purpose-built library rather than hand-rolled logic — the native validation API covers the straightforward eighty percent, not the harder twenty.
- Anchor positioning, as noted above, still needs a JavaScript fallback path for full cross-browser reliability in 2026; teams shipping to an audience with meaningfully older browsers should not treat it as universally safe yet.
- Complex state management in large, deeply interactive applications is a genuinely different problem than the interface-polish patterns discussed above, and a lightweight state library is frequently still the right call — the zero-dependency argument is about interface chrome and animation, not an argument against all abstraction everywhere.
- Charting, rich text editing, and drag-and-drop at production quality remain areas where a well-chosen, narrowly scoped library reliably beats a hand-rolled equivalent, because the edge cases these problems accumulate over years of real-world use are not trivially reproducible from a specification document.
The discipline, in other words, is not zero dependencies as an absolute. It is treating every dependency as a decision that must justify its weight, its maintenance burden, and its position in the trust chain — rather than a decision made once, by habit, at the start of a project, and never revisited.
VI. The Housekeeping Nobody Enjoys, But That Compounds
Two further, less glamorous practices consistently separate genuinely lean production sites from ones that only look lean in a demo:
Check what your build tooling is actually shipping, not what you assume it ships. An outdated browser target can lead build tools to transform syntax or include compatibility code that your current audience does not need. Review your supported-browser policy and inspect generated bundles before removing transformations or polyfills; older devices and browsers may still be part of your actual audience.
Measure transfer size, not raw file size. A 300-kilobyte raw JavaScript file might compress to 80 kilobytes over the wire with Brotli — the number that actually determines how long a user on a constrained connection waits. Bundle visualizer tooling built into modern bundlers can report gzip and Brotli sizes directly, and any serious performance review should be reading those figures, not the uncompressed ones, when deciding whether a dependency's weight is acceptable.
VII. A Practical Test Before Adding Anything
Before a new dependency enters a project, three questions, asked in order, do most of the useful work:
- Does native CSS already do this? Check the feature's Baseline status on MDN directly, rather than relying on memory or a two-year-old blog post — this landscape has moved quickly, and yesterday's “not ready yet” is often today's universal support.
- Does a built-in browser API already do this —
IntersectionObserver,ResizeObserver, the Web Animations API,<dialog>, native form validation — without a third-party runtime? - If neither, is there a genuinely small, narrowly scoped library that solves exactly this one problem, rather than a general-purpose framework whose one useful feature is accompanied by fifty unused ones?
Only after all three questions have been asked honestly does reaching for a dependency stop being a habit and start being a decision — one made in view of its real cost: the kilobytes it adds to a page a stranger's ageing phone must parse before responding to a tap, and the maintainer's inbox it quietly adds to the list of things that must never be compromised for the site to stay safe.
VIII. Closing
There is a kind of craftsmanship that shows itself not in what was added, but in what was correctly left out — the carpenter who does not reach for another brace because the joint was already sound, the storyteller who trusts a single well-placed silence more than another sentence. The web, over the last decade, largely forgot this instinct. It reached for another library the way a nervous cook reaches for another spice, convinced that more was the same thing as better.
It was not. The nurse's clinic still runs on a mid-range phone. The trader's browser tab is still competing with three other open apps for the same gigabyte of RAM. The bars on the signal indicator still fall to nothing more often than any of us building these pages would like to admit. Every kilobyte a team chooses not to ship is a kilobyte that phone did not have to fight through, on a network that did not have to carry it, paid for out of a data budget that was never unlimited to begin with. That is not a constraint to apologise for. It is, done well, the whole discipline.
Sources
- HTTP Archive, Web Almanac 2025 — Page Weight
- web.dev, Interaction to Next Paint (INP) and INP becomes a Core Web Vital
- W3Techs, jQuery usage statistics (survey updated daily)
- jQuery project, jQuery 4.0.0 release announcement
- Wiz, Analysis of the September 2025 npm attack on chalk and debug
- MDN Web Docs, Container queries, :has(), View Transitions, and Anchor Positioning
- MDN Web Docs, Popover API, <dialog>, and scroll-driven animations
- Chrome Developers, Chrome UX Report methodology
Measurements and product behavior are linked to their original or maintained documentation above. Usage and compatibility data can change; consult the cited sources for their current methodology, version, and coverage.