Skip to content

Comments

Recalculate body padding after SPA navigation#987

Merged
barryvdh merged 4 commits intophp-debugbar:masterfrom
angus-mcritchie:fix/recalculate-body-padding-on-spa-navigation
Feb 14, 2026
Merged

Recalculate body padding after SPA navigation#987
barryvdh merged 4 commits intophp-debugbar:masterfrom
angus-mcritchie:fix/recalculate-body-padding-on-spa-navigation

Conversation

@angus-mcritchie
Copy link
Contributor

@angus-mcritchie angus-mcritchie commented Feb 10, 2026

Problem

When using SPA navigation frameworks (Livewire, Turbo, HTMX), the body's padding may change between pages. The debugbar captures body padding at initialization time (lines 702-704) and uses these cached values in recomputeBottomOffset().

This causes a visual bug when navigating between pages with different layouts. For example, navigating from a page with a sticky header (large padding-top like 177px) to a page without one (no padding), the debugbar incorrectly restores the old padding value after navigation completes.

Steps to reproduce (using Livewire 4)

  1. Create two pages with different body padding (e.g., one with a sticky header, one without)
  2. Enable debugbar with bodyBottomInset: true and toolbarPosition: 'top'
  3. Load the page with the sticky header (body has padding-top: 177px)
  4. Use wire:navigate to navigate to the page without the sticky header
  5. Observe: The debugbar restores padding-top: 177px even though the new page has no padding

Timeline of the bug

Page A loads (sticky header) → debugbar caches bodyPaddingTopHeight = 177
User navigates to Page B (no header) → Livewire clears body padding
livewire:navigated fires → debugbar calls recomputeBottomOffset()
recomputeBottomOffset() restores paddingTop = 177px (wrong!)

Solution

This PR adds listeners for common SPA navigation events and recalculates the cached body padding values after navigation completes:

  • livewire:navigated (Livewire 4)
  • turbo:load (Turbo/Hotwire)
  • htmx:afterSettle (HTMX)

The new recalculateBodyPadding() method:

  1. Clears any debugbar-applied inline padding
  2. Reads the page's actual CSS padding values
  3. Updates the cached values
  4. Calls recomputeBottomOffset() with the correct values

Testing

Tested with Livewire 4 wire:navigate between pages with different body padding values. The debugbar now correctly respects each page's actual padding.

Copilot AI review requested due to automatic review settings February 10, 2026 00:10
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses a visual/layout bug where the debugbar caches document.body padding on initialization and later reapplies stale values after SPA-style navigations (e.g., Livewire/Turbo/HTMX), causing incorrect padding restoration when navigating between pages with different layouts.

Changes:

  • Adds SPA navigation event listeners (livewire:navigated, turbo:load, htmx:afterSettle) to trigger a recalculation step.
  • Introduces recalculateBodyPadding() to refresh cached body padding values and re-run recomputeBottomOffset().

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 781 to 785
recalculateBodyPadding() {
// Clear inline styles to read the page's actual CSS values
document.body.style.paddingTop = '';
document.body.style.paddingBottom = '';

Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

recalculateBodyPadding() clears document.body.style.paddingTop/paddingBottom unconditionally. If options.bodyBottomInset is false, recomputeBottomOffset() is a no-op, so this listener would permanently wipe any existing inline body padding after SPA navigation. Even when bodyBottomInset is true, clearing these properties can remove application-defined inline padding (e.g. <body style="padding-top: …">) before you measure it, causing the cached values to be wrong. Consider early-returning when !this.options.bodyBottomInset, and/or preserving/restoring the previous inline padding values (or deriving the “original” padding by subtracting the debugbar offset from the current computed padding) instead of blanking the inline styles.

Copilot uses AI. Check for mistakes.
Comment on lines 788 to 789
this.bodyPaddingTopHeight = Number.parseInt(bodyStyles.paddingTop);
this.bodyPaddingBottomHeight = Number.parseInt(bodyStyles.paddingBottom);
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Number.parseInt(bodyStyles.paddingTop/paddingBottom) will truncate fractional pixel values from getComputedStyle() (e.g. 12.5px -> 12), which can introduce small layout drift when recomputing offsets. Using Number.parseFloat(...) (and updating the initial cached reads in initialize() for consistency) avoids precision loss.

Suggested change
this.bodyPaddingTopHeight = Number.parseInt(bodyStyles.paddingTop);
this.bodyPaddingBottomHeight = Number.parseInt(bodyStyles.paddingBottom);
this.bodyPaddingTopHeight = Number.parseFloat(bodyStyles.paddingTop);
this.bodyPaddingBottomHeight = Number.parseFloat(bodyStyles.paddingBottom);

Copilot uses AI. Check for mistakes.
@barryvdh
Copy link
Collaborator

I think this makes sense, but should we make this configurable? Maybe set a list of eventNames somewhere? Otherwise every framework needs to add their own event.

When using SPA navigation frameworks (Livewire, Turbo, HTMX), the body's
padding may change between pages. The debugbar captures body padding at
initialization time and uses these cached values in recomputeBottomOffset().

This causes issues when navigating between pages with different layouts -
for example, from a page with a sticky header (large padding-top) to a
page without one (no padding). The debugbar would incorrectly restore the
old padding value after navigation.

This fix adds listeners for configurable SPA navigation events and
recalculates the cached body padding values after navigation completes.
Default events: livewire:navigated, turbo:load, htmx:afterSettle

Changes:
- Add spaNavigationEvents option (array of event names, configurable)
- Skip recalculation when bodyBottomInset is disabled
- Use parseFloat instead of parseInt to avoid precision loss
@angus-mcritchie angus-mcritchie force-pushed the fix/recalculate-body-padding-on-spa-navigation branch from f47f94c to bf41e44 Compare February 10, 2026 21:54
@angus-mcritchie
Copy link
Contributor Author

I think this makes sense, but should we make this configurable? Maybe set a list of eventNames somewhere? Otherwise every framework needs to add their own event.

Good idea @barryvdh, I have made them configurable via PHP and move the defaults there.

@barryvdh
Copy link
Collaborator

Looks good :)
Can you fix the tests to add the new initialization code?

@angus-mcritchie
Copy link
Contributor Author

Looks good :) Can you fix the tests to add the new initialization code?

Sure, all done!

Big fan of your work BTW, the debugbar has saved so much time and it was actually one of the reasons I wanted to try Laravel back in the day.

@barryvdh barryvdh merged commit 957b5cc into php-debugbar:master Feb 14, 2026
21 of 22 checks passed
@barryvdh
Copy link
Collaborator

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants