Reading time : about 7 minutes Level : Intermediate

In this detailed case study, we will dissect the most common and sometimes most frustrating browser compatibility issues encountered when creating a UI mockup with Bootstrap 5.2. From handling Flexbox to the subtlety of font rendering, discover our debugging journey to turn a shaky design under Firefox into a pixel-perfect experience across all browsers.


The battlefield: a mockup, three browsers, multiple renderings

Every front-end developer knows this feeling. After hours of work, the mockup is finally perfect. Alignments are crisp, typography is elegant, animations are smooth. You let out a sigh of satisfaction, you admire your work on Chrome (or Brave, or Edge), then, just to be safe, you open the project on Firefox. And there, it’s drama. Elements overflowing, broken alignments, fonts with chaotic sizes… the beautiful harmony has flown away.

It’s precisely the scenario we faced when developing a new portfolio interface. The brief was simple: a modern, responsive home page, with a theme selector (light, dark, and high contrast), using vanilla JavaScript and the latest version of Bootstrap 5.2.

This article is not a list of miracle solutions. It is the logbook of our debugging session, a deep dive into the "why" of rendering differences between the Blink (Chromium, Brave) and Gecko (Firefox) engines, and the demonstration that robust, modern solutions are always better than hasty patches.

Use Case Diagram: The debugging process
@startuml
left to right direction
actor "Front-End Developer" as Dev

rectangle "Bootstrap 5 UI mockup" {
  usecase "Fix Flexbox alignment" as UC1
  usecase "Adjust padding dynamically (JS)" as UC2
  usecase "Control typography (clamp())" as UC3
  usecase "Fix the menu overflow" as UC4
  usecase "Make the menu icon visible" as UC5
}

actor "Firefox browser" as Firefox

Dev -- (UC1)
Dev -- (UC2)
Dev -- (UC3)
Dev -- (UC4)
Dev -- (UC5)

(UC1) ..> Firefox : <<extend>>
(UC2) ..> Firefox : <<extend>>
(UC3) ..> Firefox : <<extend>>
(UC4) ..> Firefox : <<extend>>
(UC5) ..> Firefox : <<extend>>

note right of Firefox : Révèle les problèmes de compatibilité
@enduml
Component Diagram: Architecture of the front-end solution
@startuml
!theme plain

package "Front-end solution" {
  component "HTML (FTL Templates)" as HTML
  component "script.js" as JS
  component "styles.css" as CSS

  interface "DOM manipulation" as iDOM
  interface "CSS corrections" as iCSS
}

package "Framework" {
  component "Bootstrap 5.2" as BS
  interface "CSS Variables" as iVars
  interface "Utility Classes" as iUtils
}

HTML ..> iDOM : nécessite
JS ..> iDOM : réalise

HTML ..> iCSS : nécessite
CSS ..> iCSS : réalise

CSS ..> iVars : nécessite
BS ..> iVars : fournit

HTML ..> iUtils : utilise
BS ..> iUtils : fournit

note right of JS
  - Mesure la hauteur de la navbar
  - Applique le padding dynamiquement
end note

note right of CSS
  - Utilise clamp() pour la typographie
  - Réduit les marges du menu
  - Surcharge l'icône du menu via les variables
end note

@enduml

Problem 1: The floating card of the "Hero" section

The first bug was also the most visible. The welcome section ("hero") is composed of two columns: on the left, the main title; on the right, a presentation card.

  • The symptom:On Chrome and Brave, the two columns were perfectly vertically centered. On Firefox, the right card inexplicably fell below the left text.

  • The investigation:The HTML code, however, seemed simple and correct, using Bootstrap’s standard classes.

<section id="home" class="hero-section">
    <div class="container">
        <!-- Cette ligne est la clé du problème -->
        <div class="row align-items-center min-vh-100">
            <div class="col-lg-6">
                <!-- Contenu de gauche -->
            </div>
            <div class="col-lg-6">
                <!-- Carte de droite -->
            </div>
        </div>
    </div>
</section>

Our custom CSS for`.hero-section`was defining`display: flex` et align-items: center, and the class`min-vh-100`gave it a minimum height to the line (div.row). So why was Firefox refusing to center the columns?

  • The root cause :It’s a textbook case of how rendering engines interpret implied heights. The`<section>`had a`min-height`, but no`height`explicit. To apply`align-items-center`inside`div.row`, Firefox needs to know the reference height of this container. Like its parents (div.container et la `<section>`herself) had no heightstrict, Firefox was lost. The Blink engine, more permissive, manages to "guess" the intention and to center the elements. Gecko, more faithful to the specification, does not do that.

  • The solution:Make the height explicit. We forced the`.container` et le `.row`to take up 100 % of the height of their respective parent using the utility class`h-100`of Bootstrap.

<section id="home" class="hero-section">
    <div class="container h-100">
        <div class="row align-items-center min-vh-100 h-100">
            <!-- ... -->
        </div>
    </div>
</section>

When a`align-items`Flexbox does not work as expected on the vertical axis, always check that the container has a height (height ou min-height) defined and, if the problem persists (especially on Firefox), make sure that the intermediate parent containers properly propagate this height.

Problem 2: The cannibal navigation bar

Once the "Hero" section is aligned, another problem appeared: the navigation bar, with its class`fixed-top`, overlapped the title. A`padding-top: 80px`had been applied to the "Hero" section as a solution.
  • The symptom: Le `padding`was sufficient on Chrome, but not on Firefox, where the title was always partially hidden.

  • The root cause:Using a "magic number" (80px) is a very bad practice. The height of an element such as a navigation bar can vary by a few pixels from one browser to another due to subtle differences in font rendering, spacing, or even user settings. Relying on a fixed value is a recipe for a fragile design.

  • The solution:A dynamic and foolproof solution in JavaScript. We wrote a small script to:

  • Measure the actual height of the navigation bar after rendering the page.

  • Apply this measured height as`padding-top`to the Hero section.

  • Re-execute this function on every window resize to adapt to changes (such as switching to the hamburger menu).

document.addEventListener('DOMContentLoaded', function() {
    function adjustHeroPadding() {
        const navbar = document.querySelector('.navbar.fixed-top');
        const heroSection = document.querySelector('.hero-section');

        if (navbar && heroSection) {
            const navbarHeight = navbar.offsetHeight;
            heroSection.style.paddingTop = navbarHeight + 'px';
        }
    }

    // Ajuster au chargement et au redimensionnement
    adjustHeroPadding();
    window.addEventListener('resize', adjustHeroPadding);
});
Sequence Diagram: Dynamic padding adjustment
@startuml
title Solution to the problem of the cannibal navigation bar
actor Utilisateur
participant "Browser" as Browser
participant "script.js" as JS
participant "DOM" as DOM

Utilisateur -> Browser: Charge la page
activate Browser

Browser -> JS: Déclenche l'événement 'DOMContentLoaded'
activate JS

JS -> DOM: querySelector('.navbar.fixed-top')
activate DOM
DOM --> JS: Retourne l'élément Navbar
deactivate DOM

JS -> DOM: querySelector('.hero-section')
activate DOM
DOM --> JS: Retourne l'élément Hero
deactivate DOM

JS -> DOM: navbar.offsetHeight
activate DOM
DOM --> JS: Retourne la hauteur calculée (ex: 83px)
deactivate DOM

JS -> DOM: heroSection.style.paddingTop = "83px"
activate DOM
deactivate DOM

deactivate JS
deactivate Browser

... Plus tard ...

Utilisateur -> Browser: Redimensionne la fenêtre
activate Browser
Browser -> JS: Déclenche l'événement 'resize'
activate JS
JS -> JS: Exécute à nouveau adjustHeroPadding()
note right of JS: Le processus de mesure et\nd'application du padding se répète.
deactivate JS
deactivate Browser

@enduml

In parallel, of course we have removed the rule`padding-top: 80px;`of our file`styles.css`.

Problem 3 : Anarchic typography

The following concern was the most damaging to the brand image: the size of the main title.
  • The symptom:On Firefox, the font of the title "Développeur Formateur…​" was gigantic, almost shocking, whereas it was harmonious on the other browsers.

  • Root cause:The title used a class`display-4`of Bootstrap. These classes use responsive font sizes, often based on the unit`rem`and media queries. Once again, Firefox’s rendering algorithm, combined with the "Inter" font, resulted in a much larger size calculation on our 1920x1080 resolution.

  • The solution:Take back control with the CSS function`clamp(). This function is a game-changer for fluid typography. It allows you to define a font size with three values: a minimum size, a "preferred" size (which adapts to the viewport width,`vw), and a maximum size.

.hero-content h1 {
    color: var(--text-primary);
    line-height: 1.2;
    /*
       Min: 2rem
       Préférée: s'adapte à la vue
       Max: 2.5rem (40px)
    */
    font-size: clamp(2rem, 1.2rem + 1.5vw, 2.5rem);
}

With`clamp(), we were able to tell the browser: "Make the font grow with the screen, but do not exceedneverthe limit of`2.5rem. This instantly harmonized the rendering across all browsers, giving us absolute control over the final aesthetic.

Problem 4: The navigation bar that overflows

It was the problem that gave us the most trouble and ultimately revealed the deep nature of the rendering micro-differences.

  • The symptom:On a 1920×1080 screen, the last links in the menu ("Blog", "Contact") were pushed off‑screen to the right, but only in Firefox.

  • Profound cause :After several unsuccessful attempts (changing Bootstrap breakpoints of`lg` à xl`then`xxl), we understood. The cause was not the Bootstrap logic, but a simple width calculation. On Firefox, the sum of the widths of all the links, including their`padding` et `margin`To the nearest sub-pixel, it was slightly greater than 1920 pixels. On Chrome, this same sum was slightly less. We were missing a few pixels.

  • The solution:A surgical reduction of spacing. Since it was out of the question to specifically target Firefox with outdated CSS "hacks", the only clean solution was to find a common style that works everywhere. So we slightly reduced the horizontal margins and padding of each navigation link.

/* La version finale, légèrement plus compacte */
.navbar-nav .nav-link {
    font-size: 0.9rem; /* 90% de la taille de base */
    color: var(--text-primary) !important;
    font-weight: 500;
    margin: 0 0.2rem;
    padding: 0.5rem 0.5rem !important;
    border-radius: 0.375rem;
    transition: all 0.3s ease;
}

This reduction, virtually invisible to the naked eye on a single element, collectively gave us the necessary space for all links to fit within the frame in Firefox, while preserving an almost identical appearance on other browsers.

Problem 5: The invisible hamburger menu

The last problem, and by no means the least, concerned mobile accessibility.

  • The symptom:In responsive mode (hamburger menu), the menu icon was invisible on the 'dark' and 'high-contrast' themes.

  • The root cause:The icon of the Bootstrap 5 menu is a`background-image`(a URL-encoded SVG). By default, the color of its stroke is dark, optimized for a light background. It does not automatically adapt to theme changes.

  • The solution:Use Bootstrap CSS variables to override the icon. We have defined a new SVG icon, but this time with a white stroke.#ffffff), and we applied it specifically when the themes`dark` ou `high-contrast`are active.

/* ======================
   Fix pour l'icône du menu Burger (Blanc)
   ====================== */
[data-bs-theme="dark"] .navbar-toggler-icon,
[data-bs-theme="high-contrast"] .navbar-toggler-icon {
    --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='%23ffffff' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
}

Customizing Bootstrap components like this one is increasingly done via overriding CSS variables (--bs-component-property), which is the cleanest and most sustainable method.

Conclusion: the lessons of relentless debugging

This journey, although sometimes frustrating, is rich in lessons. Each solved problem reinforces a fundamental truth of web development:Nothing replaces a rigorous cross-browser test..

Mindmap : Summary of debugging and lessons learned
@startmindmap
+ Débogage Cross-Browser : Bootstrap 5
++ **Conflit :** Chrome (Blink) vs Firefox (Gecko)
++ **Problème 1 : Alignement Hero**
+++ Cause : Hauteur implicite
+++ Solution : Classe `.h-100`
++ **Problème 2 : Navbar Cannibale**
+++ Cause : "Magic Number" (padding fixe)
+++ Solution : JS `offsetHeight`
++ **Problème 3 : Typographie Géante**
+++ Cause : Rendu des `rem`
+++ Solution : CSS `clamp()`
++ **Problème 4 : Menu qui déborde**
+++ Cause : Calcul de largeur sub-pixel
+++ Solution : Réduction des marges/paddings
++ **Problème 5 : Icône Invisible**
+++ Cause : SVG non adaptable au thème
+++ Solution : Surcharge de variable CSS
++ **Leçons Clés**
+++ Firefox force un code robuste
+++ Fuir les "magic numbers"
+++ Maîtriser la typographie fluide
+++ Personnaliser via les variables CSS
@endmindmap
These are the lessons we retain:
  1. Firefox is an ally:Its greater fidelity to CSS standards forces us to write code that is more robust and less ambiguous.

  2. Avoid the "magic numbers" :Fixed values (such as`padding-top: 80px`) are ticking time bombs. Always prefer dynamic solutions (JavaScript) or relative ones (Flexbox, Grid).

  3. Master your typography:Use`clamp()`for full control over the size of responsive fonts.

  4. Think in "components":To customize Bootstrap, override its CSS variables instead of adding many rules that override the framework.

  5. Do not target a browser:The solution is almost never to make a \"hack\" for a specific browser, but to find a common style that works everywhere.

Finally, the mockup is now solid, predictable, and ready to be integrated into a templating system. Every bug fixed was not a failure, but a step towards better quality code.

Related articles