Code Cleanup: How to Find and Remove Dead CSS and JavaScript That Slows Down Your Website
A website can run for years without anyone deliberately deciding to make it bloated.
It happens gradually.
A new landing page is added.
A slider plugin is installed.
Someone changes the theme.
A developer adds a temporary CSS override.
Marketing installs another analytics tool.
A popup campaign runs for three months.
WooCommerce gets added.
The homepage is redesigned.
The old modal disappears, but its CSS stays.
The new gallery uses a different JavaScript library, but the old library remains in app.js.
Nothing looks dramatically wrong on any one day.
Five years later, the browser loads:
Thousands of CSS declarations
Multiple JavaScript libraries
Old widgets
Duplicate styles
Abandoned event handlers
Scripts needed only on one page
Framework code that most pages never use
The website still works.
It just carries a lot of historical baggage.
Code cleanup is the process of identifying and safely removing or restructuring CSS and JavaScript that no longer contributes to the current website experience.
The word safely matters.
Finding code that appears unused is fairly easy.
Proving that it is safe to remove is much harder.
Chrome’s current Coverage documentation makes the same distinction: the browser can show which CSS and JavaScript went unused during a recording, but restructuring an application so each page receives only the code it needs depends on the application itself.
A professional cleanup process therefore has two jobs:
Find unnecessary code.
Make sure removing it does not break anything people actually use.
What Is Dead CSS?
Dead CSS is styling that no longer serves a useful purpose in the website.
Example:
.old-home-banner { display: block; background: #333; padding: 40px; }If .old-home-banner disappeared from the entire project two years ago, the rule is probably dead.
Other examples include:
Styles from a previous theme
CSS for a deleted modal
Old Bootstrap overrides
Abandoned landing-page styles
Old browser hacks
Duplicate selectors
Styles for removed plugins
Media queries for components that no longer exist
But there is an important distinction.
A CSS rule can be unused on the homepage while still being required elsewhere.
For example:
.checkout-error { border: 2px solid red; }The homepage may never use .checkout-error.
Checkout validation absolutely might.
That CSS is not dead.
It is simply not used during that particular homepage session.
What Is Dead JavaScript?
Dead JavaScript is JavaScript that no longer provides useful functionality.
Examples include:
Functions nobody calls
Code for a removed UI component
Abandoned event handlers
Old AJAX functions
Legacy tracking code
Duplicate libraries
Deprecated feature flags
Old slider code
Libraries installed for one feature that no longer exists
Simple example:
function openOldPopup() { document.querySelector('.old-popup').classList.add('open'); }If the popup was removed from every page and nothing calls this function anymore, it is a strong dead-code candidate.
JavaScript deserves particular attention because sending excessive JavaScript creates more than download cost. The browser may need to download, parse, compile, and initialize portions of it before the application becomes fully usable. web.dev specifically notes that unnecessary JavaScript can consume bandwidth and contribute to main-thread work and responsiveness problems.
Dead Code vs. Unused Code
These terms overlap, but they are not identical.
Dead Code
Code that no longer has a legitimate path or purpose in the current application.
Example:
function initializeFlashBanner() { // Feature removed years ago. }Unused Code
Code that was loaded but was not used during the particular page session you observed.
That could still be valid application code.
Conditional Code
Code that runs only under specific circumstances.
Examples:
User opens a modal
Mobile menu opens
Form validation fails
Checkout page loads
Admin user logs in
Customer account is opened
Screen width becomes smaller
Dark mode is active
This distinction is fundamental.
Coverage tells you what happened during a recording. It does not understand every future state of your business application.
Why Websites Accumulate Unused Code
Technical debt rarely arrives in one giant package.
It grows through ordinary work.
Redesigns
A new design replaces the old interface, but developers keep old.css because deleting it seems risky.
Plugins
A WordPress plugin loads a stylesheet globally even though the widget appears on one page.
Page Builders
A page builder may generate global CSS and widget-specific assets across many templates.
Framework Migrations
A website gradually moves from Bootstrap to custom CSS.
Both remain active for years.
Temporary Fixes
A developer adds:
.header { margin-top: 2px !important; }for a specific problem.
Two redesigns later, nobody remembers why.
Multiple Developers
One developer creates:
.buttonAnother adds:
.btnA third creates:
.button-primaryEventually nobody knows which button system is canonical.
Marketing Tools
Analytics, chat, heatmaps, ad pixels, popup systems, and A/B experiments accumulate.
Legacy Browser Code
Old compatibility rules can survive long after the browser versions they supported disappeared from the company’s target matrix.
This is normal.
The problem is allowing technical debt to accumulate forever without review.
Does Unused CSS Slow Down a Website?
Yes, but scale matters.
Twenty unused CSS declarations are not a crisis.
A 400 KB stylesheet where most rules are irrelevant to the page is a more meaningful problem.
External CSS can affect:
Network transfer
Parsing
Render blocking
Style calculation
Maintainability
web.dev notes that browsers must download and parse stylesheets before rendering and recommends using Chrome Coverage to identify significant amounts of CSS that are irrelevant to the current page.
Unused CSS also makes development harder.
A developer debugging one element may have to work through several overlapping generations of rules before discovering which one actually controls the page.
Does Unused JavaScript Slow Down a Website?
Unused JavaScript can be more expensive.
Consider a large bundle.
Even if only part of its functionality is required on the current page, the browser may still have to:
Request the bundle.
Download it.
Decompress it.
Parse it.
Compile it.
Execute initialization code.
Retain associated state.
The exact cost depends on the code and the browser.
But the general principle is simple:
JavaScript is not just a file-transfer problem. It consumes browser processing resources too.
This is especially noticeable on lower-powered mobile devices.
Dead Code and Core Web Vitals
The current Core Web Vitals are:
Largest Contentful Paint
Interaction to Next Paint
Cumulative Layout Shift
Navasartov’s existing Core Web Vitals guide explains the current good-experience thresholds as LCP at 2.5 seconds or less, INP at 200 milliseconds or less, and CLS at 0.1 or less at the relevant 75th-percentile field distribution.
Unused code can contribute to those problems, but the relationship is not automatic.
LCP
Large render-blocking stylesheets or JavaScript can compete with resources that are important to initial rendering.
INP
Heavy JavaScript initialization and long main-thread work can delay how quickly a page responds visually after interaction.
CLS
Cleanup can actually make CLS worse if necessary layout CSS is removed or loaded too late.
This is why optimization must be tested.
A smaller file is not automatically a better website.
Download Cost vs. Processing Cost
Imagine a 300 KB JavaScript bundle.
Its cost is not simply:
300 KB transferred
The browser also has computational work.
By comparison, CSS needs to be downloaded, parsed, and incorporated into styling/layout behavior.
JavaScript may additionally create execution and main-thread cost.
This is one reason removing or splitting large unused JavaScript often deserves higher priority than obsessing over a handful of unused CSS utilities.
Mobile Users Notice Bloat More
A powerful desktop developer machine can hide frontend problems.
A customer may have:
Older Android phone
Slower CPU
Limited memory
Cellular connection
Battery-saving mode
Several other apps running
A website that feels fine on a high-end desktop may feel sluggish there.
Performance testing should therefore include:
Mobile simulation
CPU throttling
Network throttling
Real mobile devices
Dead Code Also Hurts Maintainability
Consider:
.button { ... } .btn { ... } .button-primary { ... } .old-button { ... } .cta-button { ... }Which one is current?
Nobody knows.
So the next developer adds:
.new-main-button { ... }Now you have six.
Dead code creates:
Specificity conflicts
Fear of deletion
Larger files
Slower debugging
Harder onboarding
More regressions
More uncertainty
Performance is only one reason to clean it.
PART II — FINDING UNUSED CSS AND JAVASCRIPTChrome DevTools Coverage
Chrome DevTools Coverage is one of the most practical places to begin.
As of 2026, the current path is:
DevTools → More options → More tools → Coverage
You can also open the Command Menu and search for:
Coverage
Chrome’s current documentation shows that Coverage reports:
Resource URL
Type
Total bytes
Unused bytes
Used/unused visualization
and can record coverage while you continue interacting with the page.
Practical Coverage Workflow
Open the page in Chrome.
Open DevTools.
Open Coverage.
Start recording and reload.
Wait for the page to finish loading.
Interact with the interface.
Open menus.
Open modals.
Trigger validation.
Resize the viewport.
Test mobile navigation.
Stop recording.
Review the report.
Do not stop after step five.
That would only tell you which code the initial page load required.
Why Coverage Results Must Be Interpreted Carefully
Suppose your CSS contains:
.modal-open { overflow: hidden; } .modal { opacity: 0; } .modal.is-open { opacity: 1; }You record Coverage.
You never open the modal.
Chrome may report those ranges as unused.
That is expected.
The browser has no reason to mark them used because the relevant state never happened.
Before treating Coverage as deletion evidence, test:
Dropdowns
Tabs
Accordions
Modals
Forms
Error states
Success states
Mobile menus
Hover
Focus
Authentication
User account states
Checkout
Admin interfaces where relevant
Coverage is an observation tool.
It is not a proof engine.
Reading CSS Coverage
Current Chrome Coverage displays used and unused byte information and lets you open a resource in Sources for a more detailed breakdown. Chrome’s current Coverage guide uses green for used portions and gray for unused portions in the summary visualization; older tutorials may show different color conventions, so rely on the current DevTools labels rather than screenshots from years ago.
A stylesheet showing:
Total: 200 KB Unused: 170 KBdeserves investigation.
It does not automatically deserve deletion.
Possible explanations:
It is a global stylesheet serving many pages.
The page is loading an entire framework.
A plugin adds global styles.
The file contains genuinely obsolete code.
Your job is to determine which explanation applies.
JavaScript Coverage
The same panel can identify JavaScript bundles where a large proportion was not used during the recording.
Example:
gallery-library.js Total: 400 KB Unused during session: 340 KBQuestions to ask:
Is the library needed at all?
Is one small feature importing the whole package?
Does it belong only on gallery pages?
Could it load after the gallery opens?
Can the build system tree-shake it?
Is another library already providing the same functionality?
Possible outcomes include:
Delete
Replace
Import fewer modules
Lazy load
Page-specific load
Split bundle
Lighthouse and Unused Code in 2026
Older Lighthouse tutorials frequently show standalone opportunities labeled:
Remove unused CSS
Remove unused JavaScript
Eliminate render-blocking resources
Those older documentation pages still explain useful concepts.
But Lighthouse’s interface has evolved.
Lighthouse 13 consolidated many older performance audits into shared Performance Insights, and several older audit IDs were removed or replaced. Performance scoring is still based on performance metrics rather than the number of warnings displayed.
The practical lesson:
Do not build your maintenance process around an exact Lighthouse label remaining unchanged forever.
Use Lighthouse to identify areas worth investigating.
Use Coverage, Network, source inspection, bundle analysis, and testing to make the actual code decision.
PageSpeed Insights
PageSpeed Insights is useful for prioritizing production problems.
It can combine:
Lighthouse laboratory testing
Field data from eligible Chrome users
Those answer different questions.
Field Data
What real users experienced.
Lab Data
What happened in a controlled test.
Coverage
Which CSS or JavaScript was used during your specific browser recording.
Do not expect one tool to answer all three questions.
Firefox Developer Tools
Firefox provides:
Page Inspector
JavaScript Debugger
Network Monitor
Performance Panel
Style Editor
Accessibility Inspector
Responsive Design Mode
Mozilla’s current documentation does not present a direct Firefox tool that should be described as an exact clone of Chrome’s Coverage panel. Use Firefox for cross-browser inspection, CSS behavior, network requests, JavaScript debugging, and performance profiling instead of pretending every DevTools product has identical features.
Safari Web Inspector
Safari Web Inspector provides:
Elements
Sources
Network
Timelines
Storage
JavaScript debugging
Apple documents Timelines as a way to analyze network requests, JavaScript events, layout/rendering, memory, and CPU impact.
Again, do not assume Safari has an identical Coverage workflow.
If the problem happens in Safari, debug it in Safari.
PART III — MANUAL CSS AUDITINGSearch the Codebase
One of the least glamorous techniques is also one of the most useful.
Suppose you suspect:
.legacy-sidebarSearch the whole project for:
legacy-sidebarLook in:
HTML
PHP
JavaScript
React/Vue components
Twig
Blade
templates
CMS files
No matches?
That increases confidence.
It still may not prove deletion is safe.
Search JavaScript Too
JavaScript may create the class dynamically:
element.classList.add('active');A search limited to HTML templates can miss that.
PHP can also create classes conditionally:
<div class=":">That is why CSS-cleanup tools can fail when they understand source files only as text.
CMS Content Can Hide CSS Usage
WordPress and other CMS platforms may store markup inside the database.
That may include:
Block classes
Shortcodes
Plugin output
Page-builder markup
Custom HTML
A class absent from the Git repository may still exist in published content.
Before deleting CSS from a CMS project, inspect:
Generated frontend HTML
Database-backed templates
Page-builder output
Plugin markup
PurgeCSS
PurgeCSS remains an actively documented tool for analyzing content and removing CSS selectors that appear unused.
Its configuration supports:
Content paths
CSS inputs
Custom extractors
Safelists
Blocklists
Removed-selector reporting
Current PurgeCSS documentation continues to provide explicit safelist support for selectors or regular-expression patterns that should be retained.
Conceptually:
Scan source/content.
Discover selectors.
Compare against CSS.
Remove selectors that appear unnecessary.
This can work very well for predictable static or component-based projects.
It requires care on dynamic systems.
Safelists
Imagine your application dynamically adds:
modal-open is-loading has-error activeA cleanup tool may not discover those classes in ordinary static markup.
A documented safelist can preserve them.
Example concept:
safelist: [ 'modal-open', 'is-loading', 'has-error', /^status-/ ]Use safelists deliberately.
Do not solve every uncertainty by safelisting 8,000 classes.
At that point, you have defeated the purpose of the cleanup.
Tailwind CSS in 2026
Modern Tailwind does not work like the huge precompiled utility files people sometimes associate with older CSS frameworks.
Tailwind scans project source files and generates CSS for utility classes it can detect. Its current v4-era documentation also includes automatic source detection and explicit @source controls.
The important warning is dynamic class construction.
This is problematic:
`bg-${color}-600`because the complete class name may not exist in source text.
Tailwind’s current documentation explicitly recommends mapping runtime values to complete, statically detectable class names instead.
Better:
const variants = { blue: 'bg-blue-600', red: 'bg-red-600' };Modern Tailwind also supports explicit source and safelisting mechanisms such as @source inline() where needed.
CSS Modules
CSS Modules can make future cleanup easier by reducing the amount of styling that exists in one global namespace.
Instead of:
style.csscontaining everything, components may own local styles.
Benefits can include:
Clearer ownership
Fewer naming conflicts
Easier component deletion
Reduced specificity battles
CSS Modules do not automatically prove that every remaining rule is used.
They simply make code ownership clearer.
Component-Based CSS
A maintainable project might look like:
styles/ base.css layout.css components/ button.css modal.css card.cssIf the modal component disappears permanently, finding its corresponding styles is easier.
Compare that with:
style-final-new-v3.csscontaining 14 years of CSS.
Architecture affects how safely future cleanup can happen.
PART IV — JAVASCRIPT DEAD-CODE ELIMINATIONTree Shaking
Tree shaking is build-time dead-code elimination.
In simplified terms, a bundler analyzes module imports and exports and attempts to omit code that is not required.
Example:
import { formatDate } from './utils.js';Suppose utils.js exports:
export function formatDate() {} export function calculateLegacyTax() {}If calculateLegacyTax() is never imported and has no required side effects, a production bundler may be able to exclude it.
webpack’s current documentation describes tree shaking as dead-code elimination based primarily on static ES module structure and emphasizes the importance of correctly identifying side effects.
Rollup similarly identifies tree shaking as a core capability and uses static module analysis to remove unnecessary code.
Tree Shaking Is Not Magic
Why might dead code survive?
Side effects
CommonJS patterns
Dynamic behavior
Runtime imports
Package configuration
Incorrect build settings
Code the bundler cannot prove is safe
A dangerous configuration is:
{ "sideEffects": false }when the package actually imports CSS or performs global initialization.
webpack’s documentation specifically warns that incorrectly marking side-effectful files as safe to drop can produce working JavaScript components with missing CSS or initialization behavior.
Always test production builds.
Code Splitting
Instead of:
app.jscontaining your entire website, split by feature or route.
Example:
core.js checkout.js gallery.js admin.jsA visitor reading a blog article probably does not need the checkout JavaScript.
webpack describes code splitting as a way to divide code into bundles that can load on demand or in parallel, reducing initial bundle size and improving resource prioritization.
Lazy Loading JavaScript
Some features are valid but do not need to load immediately.
Examples:
Video player
Map
Chat widget
Date picker
Gallery
Rich-text editor
A service page with a gallery below the fold can load the gallery module when it becomes relevant.
Dynamic Imports
Modern JavaScript allows:
const gallery = await import('./gallery.js');This can create a separate chunk in bundler-based applications.
Vite’s current production system supports async chunks and CSS code splitting associated with dynamically imported modules.
Use dynamic imports where they improve delivery architecture.
Do not turn every 400-byte utility into its own network request.
Bundle Analysis
Bundle analysis answers questions such as:
Which dependency is largest?
Why is it included?
Are two versions present?
Which route imports it?
Is one feature pulling in an entire library?
Depending on your stack, you may use:
webpack bundle visualization tooling
Rollup visualization plugins
source-map-based analysis
framework-specific analyzers
Do not choose tools only because a screenshot looks impressive.
The important output is understanding why code reached production.
Duplicate JavaScript Libraries
Legacy websites frequently contain duplication.
Examples:
jQuery loaded twice
Moment.js plus another date library
Two sliders
Two modal libraries
Multiple icon packs
Two versions of one npm dependency
This creates:
More bytes
Dependency confusion
Potential global conflicts
Harder debugging
webpack’s modern tooling includes explicit chunk-deduplication mechanisms through its code-splitting system.
PART V — WORDPRESSWhy WordPress Accumulates CSS and JavaScript
A WordPress site may contain:
Theme
Child theme
Page builder
WooCommerce
Form plugin
Slider
Popup plugin
Reviews
Analytics
Chat
Each system can enqueue frontend assets.
A plugin may need one JavaScript file on:
/contact/but load it on:
/ /about/ /blog/ /services/because global loading is easier to implement.
After enough plugins, the homepage can become a collection of scripts for features that do not exist there.
wp_enqueue_script() and wp_enqueue_style()
WordPress provides an asset dependency system.
The supported frontend mechanism uses:
wp_enqueue_script(); wp_enqueue_style();WordPress documentation describes these as the proper mechanisms for loading scripts and styles, with dependency and version information handled through registered handles.
Current wp_enqueue_script() also supports loading strategies including:
defer
async
while respecting the dependency tree.
Load WordPress Assets Only Where Needed
Conceptually:
add_action('wp_enqueue_scripts', function () { if (is_page('contact')) { wp_enqueue_script( 'contact-form', get_template_directory_uri() . '/js/contact.js', [], '1.0.0', true ); } });The idea is simple:
The contact page gets the contact script.
The homepage does not need it automatically.
Real plugins may have more complicated dependencies.
Test before dequeuing third-party assets.
Why Blindly Disabling Plugin Assets Is Risky
A script may appear irrelevant but support:
AJAX
validation
popup triggers
cart fragments
checkout
login
analytics
accessibility
A CSS file may style markup inserted only after interaction.
Before disabling an asset:
Identify its owner.
Identify dependencies.
Test the page where the feature is used.
Test the page where you want to remove it.
Test mobile.
Test logged-in/logged-out states where applicable.
Page Builders
Elementor and similar builders can contribute:
Global CSS
Widget CSS
Animation libraries
Icon sets
Third-party scripts
That does not mean:
Page builder = slow
A carefully configured builder site can perform well.
A badly engineered custom site can perform terribly.
Measure the implemented result.
WordPress Optimization Plugins
Some plugins can:
Remove unused CSS
Delay JavaScript
Defer scripts
Unload assets
Create critical CSS
Those features can be useful.
They can also cause:
Broken menus
Broken forms
Flash of unstyled content
Missing tracking
Delayed conversion scripts
Checkout failures
Never evaluate an optimization plugin only by its Lighthouse score.
A checkout that scores 100 but cannot accept payments is not optimized.
PART VI — FRAMEWORKSBootstrap
A site may include all of Bootstrap while using:
Grid
Buttons
and nothing else.
Better options may include:
Sass-based selective imports
Custom framework build
Remove framework during a redesign
Replace individual pieces gradually
Do not open a compiled bootstrap.min.css and manually delete random sections.
Change the source/build process.
Tailwind CSS
Current Tailwind generates the utilities it detects in your project instead of expecting you to ship one giant library stylesheet.
The main cleanup risk is not “Tailwind generates too much CSS.”
It is often:
Your source-detection rules do not understand how your application constructs classes.
That is why complete class names and explicit source configuration matter.
React, Vue, and SPA Bundles
JavaScript-heavy applications may accumulate:
Large component libraries
Date libraries
Chart systems
Polyfills
Multiple packages solving the same problem
Entire route trees in one initial bundle
Useful strategies:
Route-level code splitting
Component lazy loading
Tree shaking
Dependency review
Bundle visualization
Server rendering where appropriate
Do not assume SPA architecture itself is the problem.
Audit what is actually shipped.
Legacy jQuery Websites
Do not automatically remove jQuery because someone tells you it is old.
A legacy site may depend on:
jQuery UI
sliders
form plugins
custom event code
old AJAX functions
Removing the core library first can break everything.
A safer modernization sequence:
Inventory jQuery plugins.
Remove unused plugins.
Rewrite isolated features where useful.
Test.
Remove jQuery only when no dependency remains.
PART VII — THIRD-PARTY CODEThird-Party Scripts Can Be the Biggest Problem
Developers sometimes spend hours reducing:
app.cssby 12 KB while loading:
chat-widget.js heatmap.js reviews.js video-player.js marketing-suite.json every page.
Third-party code may include:
Analytics
Chat
Heatmaps
Advertising
Reviews
Maps
Video
Social widgets
Consent tools
A/B testing
You cannot tree-shake a remote third-party script you do not control.
Your options are usually:
Remove
Delay
Lazy load
Load conditionally
Replace
Prioritize large expensive resources.
Tag Managers Need Maintenance Too
A tag manager can become a historical archive.
Audit:
Old conversion tags
Duplicate analytics
Retired ad pixels
Old remarketing
Test scripts
Abandoned campaigns
This should involve marketing teams.
A developer may see a script that looks obsolete while marketing still depends on it for campaign attribution.
Old Analytics Scripts
Duplicate analytics creates two problems:
Performance
More scripts.
Data Quality
Duplicate pageviews and events.
Removing old analytics code can therefore improve both frontend delivery and reporting integrity.
PART VIII — WHEN NOT TO DELETE CODE“Unused Here” Does Not Mean “Unused Everywhere”
This is the most important rule in the article.
Chrome Coverage tells you what code was unused during the recording. Chrome’s own documentation frames the tool around analyzing the resource usage of a captured session, not proving global deadness across an entire application.
Example:
.checkout-error { color: #b91c1c; }Homepage Coverage:
unusedCheckout error state:
criticalBoth statements can be true.
Hidden Interaction States
Always test:
Hover
Focus
Focus-visible
Active
Error
Loading
Success
Disabled
Open
Closed
A menu style may exist only while the menu is open.
A validation class may appear only after invalid submission.
Responsive States
CSS can be used only under:
@media (max-width: 768px)or:
@media (min-width: 1200px)Test:
Small phone
Large phone
Tablet
Desktop
Wide desktop
Landscape
Print if supported
A desktop-only Coverage recording cannot validate your mobile CSS.
Logged-In and Logged-Out States
Applications may render different code for:
Public visitors
Customers
Staff
Administrators
A customer portal can have dozens of states that anonymous testing will never touch.
Campaign and Seasonal Code
Sometimes “unused” really means:
Not being used this month.
Before deleting campaign code, ask the business owner or marketing team whether the campaign will return.
If it is intentionally dormant:
document it
isolate it
consider moving it out of the main bundle
Dormant code does not necessarily need to remain globally loaded.
PART IX — SAFE CLEANUP PROCESSEstablish a Baseline First
Before modifying code, record:
CSS transferred
JavaScript transferred
Number of requests
Lighthouse report
Core Web Vitals where field data exists
Screenshots
Conversion flow
Console errors
Functional tests
This gives you something to compare against.
Without a baseline:
“The site feels faster”
is not a measurement.
Use Git
Before cleanup:
git status git add . git commit -m "Baseline before frontend cleanup"Then create a branch.
The point is not the exact command naming.
The point is reversibility.
If deleting one dependency breaks checkout, you want a clear diff.
Remove Code in Small Batches
Bad approach:
Delete 10,000 lines Friday afternoon and deploy.
Better:
Choose one component.
Identify dependencies.
Remove it.
Test.
Commit.
Continue.
Small changes make regressions traceable.
Test Every Important Template
Include:
Homepage
Service pages
Blog
Contact page
Landing pages
Product page
Cart
Checkout
Login
Account
Admin where relevant
A performance audit on only the homepage is not a whole-site cleanup.
Test Interactive Features
Run through:
Mobile navigation
Dropdowns
Modals
Accordions
Tabs
Forms
Errors
Filters
Search
Slider
Uploads
Date picker
Payment flow
Do not rely only on screenshots.
Test Multiple Browsers
Test current:
Chrome
Firefox
Safari
Edge
iOS Safari
Android Chrome
Navasartov’s cross-browser guide explains why identical HTML and CSS can still produce platform-specific differences. Removing a “weird Safari rule” because it appears unnecessary in Chrome can reintroduce the exact browser bug that rule originally fixed.
Visual Regression Testing
The principle is:
Before screenshot → Cleanup → After screenshot
Automated visual regression can be helpful for:
Headers
Cards
Forms
Product pages
Checkout
Breakpoints
Allow sensible tolerance.
Font antialiasing can produce minor screenshot differences that are not actual regressions.
Automated Functional Testing
High-value flows may deserve automated tests.
Examples:
Login
Contact form
Checkout
Search
Navigation
Account update
A frontend cleanup becomes safer when important workflows can be rerun consistently.
PART X — PERFORMANCE STRATEGIES BEYOND DELETION
Delete vs. Defer vs. Lazy LoadNot every unnecessary-at-load-time resource should be deleted.
| Situation | Action |
|---|---|
| Never used anywhere | Delete |
| Needed later in page lifecycle | Defer |
| Needed after interaction | Lazy load |
| Needed only on one page | Load conditionally |
Critical CSS
Critical CSS refers to styles needed for the first visible content.
A project may inline a small amount of critical styling and load the remaining stylesheet separately.
Be careful.
Hand-maintaining two versions of the same CSS can create:
Duplication
FOUC
stale rules
debugging confusion
Use a repeatable build process if you adopt critical CSS.
Inline CSS
Inlining a small amount of genuinely critical CSS can reduce a separate blocking request.
Tradeoffs include:
Larger HTML
Less independent caching
More complexity
Duplication if overused
Inline only because measurement supports it—not because “inline is faster.”
defer
Example:
<script src="/js/app.js" defer></script>Deferred external scripts can download without stopping HTML parsing and execute after document parsing, while preserving order relative to other deferred scripts.
web.dev distinguishes this from async, which can execute as soon as the script becomes available and does not provide the same execution-order guarantee.
Remember:
Deferring unnecessary JavaScript is not the same as eliminating it.
The browser still receives the code.
async
async can work well for independent scripts.
Examples may include certain:
analytics
isolated third-party tools
It is risky for chains where:
script-b.jsexpects:
script-a.jsto have already executed.
Do not treat async and defer as synonyms.
Page-Specific Bundles
Instead of:
global.css global.jscontaining everything:
Homepage
core.css home.css core.jsCheckout
core.css checkout.css core.js checkout.jsThis can reduce irrelevant code.
There is a tradeoff:
More files
More cache boundaries
More architecture
With HTTP/2 and HTTP/3, the old assumption that every extra request is disastrous is much less useful than it once was.
Optimize the actual application.
PART XI — CSS ARCHITECTUREPrevent Dead CSS From Returning
Cleanup should improve architecture, not merely reduce file size once.
Useful practices include:
Components
Naming conventions
Scoped styles
Design tokens
Documentation
Code review
Removal with feature deletion
When a developer deletes the modal:
modal.php modal.js modal.cssshould be considered together.
Avoid One Giant style.css
A large global stylesheet is not automatically wrong.
But poorly organized files become difficult to reason about.
Possible organization:
styles/ base/ layout/ components/ utilities/ pages/Use whichever architecture makes ownership clear.
There is no CSS-folder structure that magically solves technical debt.
CSS Specificity Debt
Legacy code often evolves into:
#header .navigation ul li a.button.active { color: red !important; }The next developer cannot override it cleanly.
So they add another !important.
Cleanup can also be an opportunity to reduce unnecessary specificity.
Do this cautiously.
Specificity changes can have wide effects.
Duplicate CSS Rules
Copy-and-paste development creates duplicates.
Example:
.card { border-radius: 12px; }appears in three files.
You may be able to consolidate shared rules.
But first determine whether those selectors truly represent the same component.
Two components that look identical today may intentionally have separate ownership.
PART XII — JAVASCRIPT ARCHITECTUREBreak Large Files Into Modules
Instead of:
everything.jsconsider:
navigation.js modal.js forms.js gallery.jsModules make dependencies more explicit.
They are easier to:
remove
test
split
lazy-load
They also give bundlers more structure for optimization.
Avoid Large Global JavaScript
Legacy sites sometimes place everything under:
window.App = {};or dozens of global functions.
That makes it difficult to know:
who calls what
whether something is still required
which script owns an event
Modules improve clarity.
Remove Old Feature Flags
Example:
if (oldCheckoutExperiment) { ... }The experiment ended 18 months ago.
Nobody deletes it.
Eventually production contains ten historical branches.
Once the business has permanently selected a variation, remove the losing implementation after appropriate confirmation and testing.
Remove Polyfills Carefully
Browser support improves.
A polyfill once required for an older browser may become unnecessary.
Before deletion, verify:
Supported browser matrix
Customer analytics
contractual compatibility requirements
Do not remove compatibility code merely because your own laptop does not need it.
PART XIII — SECURITY AND DEAD CODEDead Code Can Increase Security Burden
Unused code can create maintenance and security consequences.
Examples:
Old npm dependency
Disabled WordPress plugin left installed
Abandoned endpoint
Legacy script with known vulnerabilities
Old admin feature nobody uses
If the code is unnecessary, there is little benefit in continuing to:
patch it
audit it
expose it
troubleshoot it
Removing genuinely unused dependencies reduces the amount of software you must maintain.
Old JavaScript Dependencies
Review:
npm outdatedand package-specific audit tooling appropriate to your environment.
Do not respond to every dependency warning by blindly running a major-version update in production.
Determine:
Is the package used?
Can it be removed?
If needed, can it be updated safely?
Are there incompatible dependencies?
Often the cleanest security update is deleting a dependency that serves no purpose.
PART XIV — SEODoes Removing Unused Code Improve SEO?
Potentially, indirectly.
Removing excessive code can support:
Faster loading
Better mobile experience
Lower main-thread work
Improved Core Web Vitals
Cleaner technical architecture
But Google does not rank a page based on:
How many lines of CSS you deleted.
A stylesheet shrinking from 80 KB to 70 KB does not guarantee a ranking improvement.
Performance is one part of a much larger search-quality system.
JavaScript Cleanup Can Harm SEO If Done Badly
Do not remove JavaScript that controls:
Navigation
Internal links
Important content
metadata generation
structured data
routing
server/client rendering coordination
A performance optimization that removes discoverable content is not a successful optimization.
Test the rendered page after cleanup.
PART XV — WORDPRESS PRACTICAL EXAMPLEExample: WordPress Service Website
Imagine a service-business homepage loading:
theme.css elementor.css woocommerce.css contact-form.css slider.css icons.css theme.js woocommerce.js forms.js slider.js popup.jsBut the homepage contains:
No store
No contact form
No slider
No popup
That is a strong optimization opportunity.
A safe process:
Record the Network requests.
Run Coverage.
Identify each file’s owner.
Determine which pages need it.
Review dependencies.
Change the enqueue logic.
Test the homepage.
Test the feature page.
Test mobile.
Compare performance.
Do not invent an expected “40% improvement.”
Measure what your actual site achieves.
PART XVI — CUSTOM PHP EXAMPLEExample: Legacy PHP Website
Suppose the project contains:
css/ style.css old.css bootstrap.css custom2.css js/ jquery.js jquery-ui.js old-slider.js app.jsStart by checking which files are loaded.
Then:
old.css
Search its selectors.
old-slider.js
Search:
HTML
PHP
JavaScript
DOM classes
initialization code
jquery-ui.js
Determine which widgets depend on it.
Bootstrap
Map Bootstrap components still in use.
Then remove one dependency at a time.
This is much safer than rewriting the entire frontend because the files look old.
PART XVII — HOW TO PRIORITIZE CLEANUPHighest Priority
Start with:
Large unused JavaScript libraries
Duplicate frameworks
Global scripts used on one page
Deprecated third-party tools
Large render-blocking CSS
Large dependency duplication
These can create meaningful network or processing cost.
Medium Priority
Examples:
Old component styles
Duplicate CSS selectors
Small page-specific JavaScript
Obsolete utility libraries
Worth fixing as part of broader maintenance.
Lower Priority
Examples:
Five unused CSS utilities
One tiny obsolete selector
600 bytes that require two days to prove safe
A useful principle:
Do not spend five hours deleting 800 bytes while a 400 KB third-party script loads on every page.
Common Dead CSS and JavaScript Problems and How to Fix Them
Problem 1: Lighthouse Says Most CSS Is Unused
Cause: A global stylesheet serves several page types.
Fix: Do not delete everything reported from one page. Test other templates and states, then split or remove code where appropriate.
Problem 2: CSS Cleanup Breaks the Mobile Menu
Cause: Classes are added dynamically only when the menu opens.
Fix: Add the necessary selectors to the cleanup tool’s source/safelist strategy and include menu interactions in testing.
Problem 3: WordPress Plugin CSS Loads Everywhere
Cause: Plugin enqueues globally.
Fix: Conditionally load or dequeue only after verifying the plugin’s dependencies and every page that uses the feature.
Problem 4: Bundle Contains a Large Library Used Once
Fix: Move the feature into a page-specific or lazy-loaded chunk.
Problem 5: Removing jQuery Breaks the Website
Cause: Other scripts depend on it.
Fix: Inventory dependencies first. Remove or rewrite dependent plugins before removing jQuery.
Problem 6: Two Library Versions Are Loaded
Impact: Extra bytes and possible incompatibility.
Fix: Inspect the dependency tree and standardize on a compatible version.
Problem 7: CSS Selector Appears Nowhere in HTML
Cause: It may be added by JavaScript or CMS-generated content.
Fix: Search templates, scripts, database content, and rendered markup.
Problem 8: PurgeCSS Removes Dynamic Classes
Fix: Use documented safelist rules or restructure the application so class names are statically discoverable. PurgeCSS currently supports explicit safelist strings and patterns.
Problem 9: Page Looks Fine but Modal Is Broken
Cause: Cleanup testing covered initial load only.
Fix: Test hidden states and interactions.
Problem 10: Desktop Works but Mobile Breaks
Cause: Responsive-only styles were removed.
Fix: Test important breakpoints before deployment.
Problem 11: Cleanup Creates a Flash of Unstyled Content
Cause: Critical styles are loading too late.
Fix: Review stylesheet ordering and critical/non-critical CSS strategy.
Problem 12: Deferred Script Executes Too Late
Cause: Another script assumed earlier execution.
Fix: Review dependencies before changing loading strategy.
Problem 13: async Breaks Dependent Code
Cause: Async scripts do not guarantee normal execution order.
Fix: Use defer or explicit dependency management when order matters.
Problem 14: Removing Script Breaks Analytics
Fix: Audit tags with marketing stakeholders before deletion.
Problem 15: Site Uses Old Bootstrap and Custom CSS
Fix: Identify active Bootstrap components first. Migrate component by component.
Problem 16: Deleted CSS Returns After Every Build
Cause: You edited generated output.
Fix: Find and modify the real source file or build configuration.
Problem 17: CSS Is Generated by a Page Builder
Fix: Use the builder’s supported regeneration/optimization workflow instead of deleting generated files manually.
Problem 18: Source Maps Are Missing
Impact: Bundled/minified code becomes difficult to trace.
Fix: Use appropriate development or staging source-map configuration where security and deployment policy allow it.
Problem 19: Bundle Analyzer Shows Duplicate Dependencies
Fix: Inspect package versions, dependency ownership, and bundler chunk configuration.
Problem 20: Lighthouse Improves but Conversions Fall
Cause: A delayed or removed script was part of the customer journey.
Fix: Restore the feature and optimize differently.
Business outcomes matter more than the score.
Problem 21: Marketing Still Needs an Old Campaign
Fix: Confirm ownership and future use before deletion. Consider isolating it from the normal site bundle.
Problem 22: Code Is Unused but Cannot Be Deleted Yet
Fix: Isolate and document it. Create a removal issue with ownership and conditions.
Problem 23: Admin JavaScript Loads Publicly
Fix: Separate frontend and admin assets.
Problem 24: WooCommerce Assets Load on Non-Commerce Pages
Fix: Investigate which WooCommerce features and dependencies are active before conditionally removing assets.
Problem 25: Website Has Hundreds of Tiny JavaScript Files
Fix: Do not blindly concatenate everything. Review caching, HTTP behavior, dependency boundaries, and actual loading patterns before deciding on bundling.
PART XVIII — PROFESSIONAL CLEANUP WORKFLOWStep-by-Step Dead Code Cleanup Process
Back up the project.
Commit current code to Git.
Create a cleanup branch.
Record a performance baseline.
Inventory loaded stylesheets.
Inventory loaded JavaScript.
Review the Network panel.
Run Lighthouse.
Run Chrome Coverage.
Interact with the full page.
Test responsive states.
Test other templates.
Search source files.
Search dynamic class creation.
Review CMS content.
Identify third-party assets.
Identify duplicate dependencies.
Remove obviously obsolete files.
Split page-specific code.
Lazy-load interaction-only features.
Run functional tests.
Perform visual regression testing.
Test Chrome.
Test Firefox.
Test Safari.
Test Edge.
Test real mobile devices.
Compare performance.
Deploy to staging.
Monitor JavaScript and server errors.
Test conversion paths.
Deploy to production.
Watch analytics and conversion behavior.
PART XIX — BEFORE AND AFTER EXAMPLE
Imagine a hypothetical local service website.
Before
CSS ├── bootstrap.css ├── theme.css ├── slider.css └── old-custom.css JavaScript ├── jquery.js ├── jquery-ui.js ├── slider.js ├── popup.js ├── form.js └── app.jsThe homepage loads everything.
After investigation:
Slider exists only on /gallery/.
Popup campaign was retired.
old-custom.css belongs to an older template.
Form JavaScript is required only where the form appears.
jQuery UI is used nowhere.
After
Global ├── core.css └── core.js Gallery ├── gallery.css └── gallery.js Forms └── form.jsThe exact performance benefit depends on the real file sizes, caching, devices, and implementation.
The architectural improvement is easier to see:
Each page receives code closer to what it actually needs.
PART XX — HOW OFTEN SHOULD CODE BE CLEANED?
Do not wait five years.
Review frontend assets:
After redesigns
After removing plugins
After major feature removal
After framework migration
Before major performance work
After marketing-platform changes
During planned technical-debt cycles
Small continuous cleanup is usually safer than a giant rewrite.
Conclusion
Dead CSS and JavaScript are normal consequences of a long-lived website.
What matters is how the team handles them.
A professional cleanup process should:
Measure first
Distinguish dead code from conditional code
Understand dynamic behavior
Remove code in small batches
Split code when deletion is inappropriate
Test every important state
Use Git
Use staging
Monitor production behavior
Chrome Coverage, Lighthouse, network analysis, bundle tooling, PurgeCSS, Tailwind source detection, tree shaking, code splitting, and WordPress conditional asset loading can all help.
None of them replaces engineering judgment.
The central rule is simple:
The goal is not to delete the largest number of lines. The goal is to deliver only the code users actually need while keeping the website reliable, accessible, maintainable, and fast.
Navasartov helps businesses audit and refactor legacy websites, optimize WordPress assets, clean CSS and JavaScript architecture, improve Core Web Vitals, reduce frontend bloat, and modernize websites without sacrificing important functionality.
Dead CSS and JavaScript Cleanup Checklist
Before Cleanup
Git repository is current
Backup exists
Cleanup branch created
Staging environment available
Baseline Lighthouse report saved
Core Web Vitals reviewed
Screenshots recorded
Conversion flows documented
CSS Inventory
All loaded CSS files listed
Global stylesheet reviewed
Framework CSS reviewed
Theme CSS reviewed
Plugin CSS reviewed
Page-builder CSS reviewed
Duplicate selectors reviewed
Legacy browser rules reviewed
Dynamic classes identified
Responsive states checked
JavaScript Inventory
All JS requests listed
Main bundle reviewed
Duplicate libraries checked
Old feature scripts identified
Third-party tools reviewed
Analytics tags reviewed
Admin/public bundles separated
Dynamic imports considered
Tree shaking verified
Code splitting evaluated
DevTools
Network panel reviewed
Chrome Coverage run
Initial load recorded
Menu interactions recorded
Modal interactions recorded
Form states recorded
Mobile navigation recorded
Responsive layouts checked
CMS / WordPress
Theme assets reviewed
Child theme reviewed
Plugin assets reviewed
WooCommerce dependencies reviewed
Page-builder output reviewed
Database-generated markup considered
Conditional enqueue opportunities identified
Build Tools
Production bundle analyzed
Source maps available where appropriate
Tree shaking configured correctly
Side effects reviewed
Route-level splitting considered
Lazy loading considered
PurgeCSS/similar tooling reviewed
Safelists documented
Tailwind source detection verified
States
Hover
Focus
Active
Error
Success
Loading
Disabled
Open/closed
Logged in
Logged out
Templates
Homepage
Service pages
Blog
Contact
Landing pages
Products
Cart
Checkout
Login
Account
Admin where applicable
Browser Testing
Chrome
Firefox
Safari
Edge
iOS Safari
Android Chrome
Final Validation
Functional tests pass
Visual regression reviewed
Accessibility checked
Lighthouse rerun
Network transfer compared
JavaScript errors checked
Staging tested
Production monitored
Conversion behavior monitored
Chrome DevTools
Chrome DevTools Coverage — Find Unused JavaScript and CSS
Primary reference for recording and interpreting current CSS/JS coverage.
Chrome DevTools CSS Reference
Useful for CSS inspection and Coverage workflows.
Chrome Performance Insights
Useful for current performance diagnostics, including duplicated JavaScript and CSS-related costs.
Lighthouse
What’s New in Lighthouse 13
Important for understanding the migration from older standalone audits toward current performance insights.
Lighthouse Performance Guidance
Useful for understanding historical unused-code guidance and render-blocking resources.
web.dev
Optimize Resource Loading
Explains CSS rendering cost, JavaScript parser blocking, defer, async, and unused resources.
Remove Unused Code
Connects excessive CSS/JS delivery with performance and Core Web Vitals.
Firefox
Firefox Developer Tools Documentation
Reference for Inspector, Network Monitor, JavaScript debugging, performance, responsive testing, and accessibility tools.
Safari
Safari Web Inspector
Reference for Elements, Sources, Network, Timelines, storage, and Safari-specific debugging.
WordPress
wp_enqueue_script()
Official script-enqueueing API and modern defer/async strategy support.
wp_enqueue_style()
Official stylesheet enqueueing API.
PurgeCSS
PurgeCSS Configuration and Safelisting
Current documentation for removing selectors and preserving dynamically used classes.
Tailwind CSS
Detecting Classes in Source Files
Current guidance on source scanning, dynamic class names, explicit sources, and safelisting.
webpack
Tree Shaking
Current explanation of unused export elimination and side effects.
Code Splitting
Current reference for separate and dynamically loaded chunks.
Rollup
Rollup
Official documentation describing tree shaking and code splitting.
Vite
Vite Features
Current reference for asynchronous chunks and CSS code splitting.
Recommended Schema Types
For the article page:
BlogPosting
WebPage
BreadcrumbList
Organization
Person
ImageObject
WebSite
Useful BlogPosting properties include:
headline
description
image
author
publisher
datePublished
dateModified
mainEntityOfPage
about
mentions
isPartOf
Potential about / mentions concepts:
CSS
JavaScript
Web performance
Chrome DevTools
Core Web Vitals
WordPress
Tree shaking
Code splitting
Frontend development
Website maintenance
FAQPage can describe genuine visible FAQ content, but it should not be added merely because a visible Google FAQ rich result is expected.
Frequently Asked Questions
1. What Is Dead CSS?
Dead CSS is styling that no longer contributes to any active website component or state.
2. What Is Dead JavaScript?
Dead JavaScript is code that can no longer be reached or serves no current application purpose.
3. Does Unused CSS Slow Down a Website?
Large amounts can increase transfer, parsing, render-blocking work, and maintenance complexity. Tiny amounts are usually not worth obsessing over.
4. Does Unused JavaScript Affect Core Web Vitals?
It can contribute by increasing download and processing work and delaying important browser activity. The effect depends on the bundle and application.
5. How Do I Find Unused CSS in Chrome?
Open Chrome DevTools, open Coverage, record a reload, interact with the page, and review unused stylesheet ranges.
6. How Do I Find Unused JavaScript in Chrome?
Use the same Coverage panel and inspect JavaScript resources showing large unused byte ranges.
7. What Is Chrome DevTools Coverage?
Coverage records how much loaded CSS and JavaScript was used during a specific browser session.
8. Can I Delete Everything Chrome Marks as Unused?
No. The code may be required on another page, breakpoint, user state, or after an interaction.
9. Why Does Lighthouse Report Unused CSS?
A page may load a global stylesheet containing rules for components or pages that are not needed during that particular audit.
10. How Can I Safely Remove Unused CSS?
Combine Coverage with source search, dynamic-state testing, multiple page templates, browser testing, Git, and staging.
11. How Can I Safely Remove Unused JavaScript?
Determine dependency ownership, test production builds, remove one dependency at a time, and use code splitting or lazy loading when the code is still needed elsewhere.
12. What Is Tree Shaking?
Tree shaking is build-time dead-code elimination in which a bundler analyzes modules and excludes code it can prove is unnecessary.
13. What Is Code Splitting?
Code splitting divides an application into smaller bundles that can load separately or on demand.
14. What Is PurgeCSS?
PurgeCSS analyzes source/content and CSS to remove selectors that appear unused.
15. Can PurgeCSS Break a Website?
Yes. Dynamic classes, CMS-generated markup, and runtime states can be missed unless the configuration accounts for them.
16. How Do Dynamic Class Names Affect CSS Cleanup?
Static analyzers may not recognize class names assembled at runtime. Preserve them through explicit source patterns, complete class names, or safelists.
17. How Do I Remove Unused CSS From WordPress?
Identify which theme or plugin loads each stylesheet, then conditionally enqueue/dequeue assets only after testing every relevant page and dependency.
18. Why Do WordPress Plugins Load Scripts on Every Page?
Some plugins use global enqueue logic because it is simpler and ensures their functionality is available anywhere.
19. Should I Remove jQuery From My Website?
Only when nothing still depends on it. Removing jQuery from a legacy application without checking plugins and custom code can break functionality.
20. Is Unused CSS Bad for SEO?
Not directly because of the number of lines. Excessive CSS can contribute to performance problems that affect user experience and technical quality.
21. Does Removing JavaScript Improve INP?
It can when the removed or restructured JavaScript reduces main-thread work around user interactions, but the result should be measured.
22. Should I Use defer Instead of Deleting JavaScript?
Only if the script is still needed. Dead code should usually be removed; required but non-critical code may be deferred.
23. What Is the Difference Between Dead Code and Lazy-Loaded Code?
Dead code serves no purpose. Lazy-loaded code is valid code intentionally loaded only when needed.
24. How Often Should a Website Codebase Be Cleaned?
Review it after redesigns, plugin removals, major feature changes, framework migrations, and periodically during technical-debt maintenance.
25. How Do I Test Whether Removing CSS Broke Something?
Test multiple templates, breakpoints, interactive states, browsers, accessibility states, and visual regression screenshots.
26. Can a Lighthouse Score Improve After Removing Unused Code?
Yes, depending on what was removed and whether the change reduces rendering or JavaScript cost. A score improvement is not guaranteed.
27. Should Admin JavaScript Load on Public Pages?
Usually not unless a public feature genuinely depends on it.
28. How Can I Find Duplicate JavaScript Libraries?
Review the Network panel, package dependency tree, generated bundle, and bundle-analysis tools.
29. How Do I Clean Up a Legacy PHP Website?
Inventory loaded files, run Coverage, search PHP/templates/JavaScript, identify dependencies, remove one component at a time, and test on staging.
30. Is Deleting Unused Code Always Better Than Code Splitting?
No. Code required somewhere else should normally be split or loaded conditionally rather than deleted.
Latest Articles