Developer Workflow for Debugging UI Bugs
UI bugs are frustrating because they often involve multiple systems: CSS layout, JavaScript logic, framework state, and browser rendering. A systematic workflow helps you diagnose issues faster and build mental models that prevent similar bugs in the future.
Problem
Developers often approach UI bugs randomly:
- Change something
- Refresh
- It did not work
- Change something else
- Repeat
This trial-and-error approach is slow and does not build understanding. Even when it works, you may not know why it worked.
Why It Happens
UI bugs have many potential causes:
- CSS specificity or cascade issues
- Layout algorithm misunderstandings
- JavaScript timing or state problems
- Browser inconsistencies
- Framework rendering quirks
Without a systematic approach, you are essentially guessing which category the bug falls into.
Solution
Follow a structured debugging workflow that narrows down the problem space efficiently.
Implementation
Step 1: Reproduce Reliably
Before debugging, ensure you can reproduce the bug consistently:
Reproduction steps:
1. Navigate to /blog
2. Click first article
3. Scroll to "Implementation" section
4. TOC should highlight "Implementation"
5. BUG: TOC highlights "Problem" instead
If the bug is intermittent, find the conditions that trigger it.
Step 2: Isolate the Component
Render the component in isolation to determine if the bug is in the component or its context:
// pages/debug.tsx
import { TOC } from '../components/TOC';
const testContent = `
## Problem
First section.
## Implementation
Second section.
`;
export default function Debug() {
return (
<div style={{ padding: 100 }}>
<TOC content={testContent} />
</div>
);
}
If the bug disappears, the issue is in how the component is used, not the component itself.
Step 3: Check DevTools
Open DevTools and examine:
- Elements panel: Check computed styles, box model, applied rules
- Console: Look for errors or warnings
- Network: Verify resources loaded correctly
- Performance: Check for layout thrashing or long tasks
For CSS issues, right-click the element and select "Inspect" to see exactly which rules apply.
Step 4: Add Debug Logging
Add temporary logging to understand runtime behavior:
useEffect(() => {
console.log('Headings:', headings);
console.log('Active ID:', activeId);
const observer = new IntersectionObserver((entries) => {
console.log('Intersection entries:', entries.map(e => ({
id: e.target.id,
isIntersecting: e.isIntersecting,
ratio: e.intersectionRatio
})));
// ...
});
}, [headings]);
TIP: Use
console.table()for array data andconsole.dir()for DOM elements to see their properties clearly.
Step 5: Form a Hypothesis
Based on your observations, form a specific hypothesis:
"The IntersectionObserver rootMargin is configured for the wrong viewport height, causing headings to register as visible too early."
Step 6: Test the Hypothesis
Make a targeted change to test your hypothesis:
// Change rootMargin to test hypothesis
const observer = new IntersectionObserver(
callback,
{
rootMargin: '-120px 0px -60% 0px' // Adjusted for fixed header
}
);
If this fixes the bug, your hypothesis was correct. If not, return to Step 4 with new logging.
Example
Debugging a sidebar that overlaps content on mobile:
Step 1: Reproduce
- Viewport width: 768px
- Sidebar visually overlaps article content
Step 2: Isolate
- Rendered sidebar alone: displays correctly
- Issue is layout interaction
Step 3: DevTools
- Sidebar has
position: fixedbut no width constraint - On mobile, it takes
width: 280pxwhich extends beyond viewport
Step 4: Log
console.log('Viewport:', window.innerWidth);
console.log('Sidebar rect:', sidebarRef.current?.getBoundingClientRect());
Output: Sidebar extends 280px from left, viewport is 768px, but left padding pushes content under sidebar.
Step 5: Hypothesis "Sidebar should be hidden on viewports under 1024px."
Step 6: Test
@media (max-width: 1023px) {
.sidebar {
display: none;
}
}
Bug fixed.
Common Mistakes
Changing multiple things at once
/* Do NOT do this */
.element {
position: relative; /* Changed */
z-index: 10; /* Changed */
overflow: visible; /* Changed */
}
Change one property, test, then change the next if needed.
Not checking error console
Many CSS issues trigger console warnings:
DevTools: "position: sticky" has no effect because the parent element has "overflow: hidden"
Browser warnings often point directly to the cause.
Assuming the bug is where it appears
The visual symptom and root cause are often in different components:
Symptom: Button appears behind modal
Cause: Ancestor three levels up creates stacking context
WARNING: The element with the visual bug is not necessarily the element you need to fix. Trace up the ancestor chain.
Not testing on actual devices
Some bugs only appear on real mobile devices due to:
- Touch interactions
- Viewport units
- Safe area insets
- Browser rendering differences
Conclusion
Debug UI bugs systematically: reproduce, isolate, inspect, hypothesize, test. Make one change at a time and verify each step. Use browser DevTools extensively—the Elements panel and Console contain most of the information you need. Build mental models of CSS and JavaScript behavior so you can form hypotheses faster over time.