Customize status page with CSS and JavaScript

You can add custom CSS and JavaScript to your status page to match your brand, add tracking scripts, or customize texts.

Requires a paid plan

Custom CSS requires a paid plan with the Custom CSS & JavaScript feature enabled. Custom JavaScript also requires a custom domain configured on your status page.

Get started

Go to Better Stack → Status pages → select your status page → Settings → expand Advanced settings. Custom CSS and Custom JavaScript are under Look & feel.

Page background

The simplest way to start customizing is changing the background color:

Custom CSS
body {
  background: #f0f4f8;
}

.dark body {
  background: #141821;
}

The navigation bar is translucent by default. It uses a semi-transparent white background with a backdrop blur.

Status page navigation bar

Here's how you can change the navigation bar background tint to match a colored page background. Update the backdrop, the link hover states, and the subscribe button together:

Custom CSS: tinted navigation bar
nav::before {
  background: #f0f4f8 !important;
  border-bottom-color: transparent !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}

/* Navigation links container (has its own white background) */
nav [data-target="status-pages-v2--header.menu"] {
  background: transparent !important;
  box-shadow: none !important;
  border-color: transparent !important;
}

/* Active navigation link (current page) */
nav .bg-statuspage-neutral-60 {
  background: #fff !important;
}

/* Navigation link hover */
nav [data-target="status-pages-v2--header.menu"] a:hover {
  background: #fff !important;
}

/* Subscribe button */
[data-status-pages-v2--subscribe-target="toggle"] {
  background: #fff !important;
  border-color: transparent !important;
  box-shadow: none !important;
}

/* Dark mode */
.dark nav::before {
  background: #141821 !important;
  border-bottom-color: transparent !important;
}

.dark nav [data-target="status-pages-v2--header.menu"] {
  background: transparent !important;
  box-shadow: none !important;
  border-color: transparent !important;
}

.dark nav .bg-statuspage-neutral-60 {
  background: #000 !important;
}

.dark nav [data-target="status-pages-v2--header.menu"] a:hover {
  background: #000 !important;
}

.dark [data-status-pages-v2--subscribe-target="toggle"] {
  background: #000 !important;
  border-color: transparent !important;
  box-shadow: none !important;
}

Subscribe button

If you'd like to change the subscribe button shape or color, you can target it independently. These small tweaks work on their own without changing the rest of the navigation bar:

Custom CSS: pill-shaped button Custom CSS: brand-colored button
[data-status-pages-v2--subscribe-target="toggle"] {
  border-radius: 999px;
}
[data-status-pages-v2--subscribe-target="toggle"] {
  background: #2563eb !important;
  color: #fff !important;
  border-color: #2563eb !important;
  box-shadow: none !important;
}

.dark [data-status-pages-v2--subscribe-target="toggle"] {
  background: #3b82f6 !important;
  color: #fff !important;
  border-color: #3b82f6 !important;
  box-shadow: none !important;
}

Sections

Each service group is wrapped in a .section element.

Status page sections

You can adjust its background, spacing, or remove the default top border that separates sections:

Custom CSS
.section {
  background: #f8fafc;
  border-top: none !important;
  padding: 4px 12px;
  margin-top: 10px;
}

.dark .section {
  background: rgba(200, 180, 255, 0.03);
  border-top: none !important;
}

Uptime history bars

Status page uptime history bars

If you'd like to change the size of the history bars, adjust the height and border-radius on the .tick class:

Custom CSS
.tick {
  height: 24px !important;
  border-radius: 3px !important;
}

Theme examples

Here are two examples of cohesive themes. They combine the techniques above, so pick one and paste it into Custom CSS:

Slate (light mode) Navy (dark mode)
body {
  background: #f0f4f8;
}

nav::before {
  background: rgba(240, 244, 248, 0.9) !important;
  border-bottom-color: #dde4ed !important;
}

.tick {
  height: 20px !important;
  border-radius: 2px !important;
}

/* Dark mode */
.dark body {
  background: #141821;
}

.dark nav::before {
  background: rgba(20, 24, 33, 0.9) !important;
  border-bottom-color: #252b3d !important;
}
html, body {
  background: #0b1120 !important;
  color: #c8d2e0 !important;
}

nav::before {
  background: rgba(11, 17, 32, 0.95) !important;
  border-bottom-color: #1a2540 !important;
}

h1 {
  color: #e4eaf4 !important;
}

.section-toggle:hover {
  background: #162036 !important;
}

/* Navigation links (Status, Maintenance, Previous incidents) */
nav [data-target="status-pages-v2--header.menu"] a {
  color: #8896b0;
}

nav [data-target="status-pages-v2--header.menu"] a:hover {
  color: #e4eaf4 !important;
  background: #162036 !important;
}

/* Subscribe button */
[data-status-pages-v2--subscribe-target="toggle"] {
  background: #162036 !important;
  border-color: #1a2540 !important;
  color: #c8d2e0 !important;
}

.tick {
  height: 20px !important;
  border-radius: 2px !important;
}

footer {
  opacity: 0.4;
}

Selector reference

Here are the most commonly targeted elements organized by area:

Page layout Header and navigation Status overview Sections and resources Incidents and maintenance Subscribe popup
/* Full page background */
body { }

/* Main content container */
main { }
/* Navigation bar background (translucent pseudo-element) */
nav::before { }

/* Navigation bar layout and spacing */
nav { }

/* Company logo or name */
nav > div:first-child a { }

/* Navigation links container */
nav [data-target="status-pages-v2--header.menu"] { }

/* Individual navigation link pill */
nav [data-target="status-pages-v2--header.menu"] a { }

/* Subscribe button */
[data-status-pages-v2--subscribe-target="toggle"] { }
/* Status heading ("All services are online", etc.) */
h1 { }

/* "Updated" timestamp below the heading */
h1 + p { }

/* Status icon (SVG above the heading) */
svg:has(~ h1) { }

/* Announcement banner */
.prose-status-page { }
/* Section container (collapsible service group) */
.section { }

/* Section toggle button (expand/collapse) */
.section-toggle { }

/* Section status badge ("Operational", "Degraded", etc.) */
.section-status-badge { }

/* Individual resource row */
.section-content > div { }

/* Uptime history bar (colored tick) */
.tick { }

/* History time range ("90 days ago" / "Today") */
.section-content .leading-none { }
/* "Previous incidents" heading */
h3 { }
/* Email/URL input field */
.subscribe-input { }

Styling dark mode?

Your status page supports dark mode. Use the .dark selector to target dark mode, for example .dark body { background: #0d1117; }.

Custom fonts

Import a font from Google Fonts and apply it to the page:

Custom CSS
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');

body {
  font-family: 'Inter', sans-serif;
}

Section expand and collapse

Override the default collapse behavior so all sections start expanded or stay permanently collapsed:

All sections expanded (CSS + JS) All sections collapsed (CSS + JS)
/* Custom CSS */
html:not(.sections-ready) .section-collapsed .section-content {
  height: auto !important;
  overflow: visible !important;
}

html:not(.sections-ready) .section-collapsed .section-chevron {
  display: none;
}

/* Custom JavaScript */
document.addEventListener("DOMContentLoaded", function () {
  document.querySelectorAll(".section-collapsed").forEach(function (el) {
    el.classList.remove("section-collapsed");
  });
  document.documentElement.classList.add("sections-ready");
});
/* Custom CSS */
html:not(.sections-ready) .section .section-content {
  height: 0;
  overflow: hidden;
}

/* Custom JavaScript */
document.addEventListener("DOMContentLoaded", function () {
  document.querySelectorAll(".section:not(.section-collapsed)").forEach(function (el) {
    el.classList.add("section-collapsed");
  });
  document.documentElement.classList.add("sections-ready");
});

Hide elements

Use display: none to remove any element from view:

Custom CSS
/* Hide the footer */
footer { display: none; }

/* Hide the subscribe button */
[data-status-pages-v2--subscribe-target="toggle"] { display: none; }

/* Hide uptime history bars and time axis — shows only the SLA percentage */
.tick { display: none; }
.section-content .leading-none { display: none; }

External tracking scripts

Want to add your Google Analytics?

Google Analytics has a dedicated field, so you don't need custom JavaScript for it. Go to your status page's Settings → Advanced settings → Analytics and paste your tag ID into the Google tag ID field.

Integrate Plausible, Fathom, or other analytics providers by pasting the full <script> tag into Custom JavaScript:

Custom JavaScript: Plausible Analytics Custom JavaScript: Fathom Analytics
<script defer data-domain="status.example.com" src="https://plausible.io/js/script.js"></script>
<script src="https://cdn.usefathom.com/script.js" data-site="ABCDEFGH" defer></script>

Custom domain is required

Custom JavaScript only runs on status pages with a custom domain configured. It will not run on the default *.betteruptime.com subdomain.

Subscribe component picker

When a visitor clicks Get updates, the subscribe popup opens with a general e-mail field. If you'd like the Subscribe to specific components checkbox to be pre-selected so the component list is visible right away, add this to Custom JavaScript:

Custom JavaScript
document.addEventListener("DOMContentLoaded", function () {
  var button = document.querySelector(
    'a[data-action*="status-pages-v2--subscribe#handleToggleClick"]'
  );

  if (button) {
    button.addEventListener("click", function () {
      var checkbox = document.getElementById("specific_resources");
      if (checkbox && !checkbox.checked) {
        checkbox.checked = true;
        checkbox.dispatchEvent(new Event("change"));
      }
    });
  }
});

Subscribe popup with the component list shown

Intro card

You can inject a description card that appears between the status heading and your service sections. This is useful for telling visitors what the page monitors, how to get help, or where to subscribe.

Intro card below the status heading

The example uses both Custom CSS for the card styling and Custom JavaScript to inject it into the page. It also includes a MutationObserver so the card persists through auto-refreshes.

Paste this into Custom CSS:

Custom CSS
.company-description {
  max-width: 700px;
  margin: 1.5rem auto 0;
  padding: 1.5rem 2rem;
  background: rgba(249, 250, 251, 0.6);
  border: 1px solid rgba(209, 213, 219, 0.4);
  border-radius: 12px;
  font-size: 0.9375rem;
  line-height: 1.7;
  color: #4b5563;
  text-align: center;
  backdrop-filter: blur(8px);
  transition: all 0.3s ease;
}

.dark .company-description {
  background: rgba(31, 41, 55, 0.5);
  border-color: rgba(75, 85, 99, 0.4);
  color: #d1d5db;
}

.company-description:hover {
  border-color: rgba(156, 163, 175, 0.5);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
}

.dark .company-description:hover {
  border-color: rgba(107, 114, 128, 0.5);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
}

.company-description strong {
  color: #1f2937;
  font-weight: 600;
}

.dark .company-description strong {
  color: #f9fafb;
}

.company-description a {
  color: #2563eb;
  text-decoration: none;
  font-weight: 500;
  transition: color 0.2s ease;
}

.company-description a:hover {
  color: #1d4ed8;
  text-decoration: underline;
}

.dark .company-description a {
  color: #60a5fa;
}

.dark .company-description a:hover {
  color: #93c5fd;
}

.company-description-title {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-weight: 600;
  font-size: 1rem;
  color: #374151;
  margin-bottom: 0.75rem;
}

.dark .company-description-title {
  color: #e5e7eb;
}

.company-icon {
  width: 18px;
  height: 18px;
  fill: currentColor;
  opacity: 0.7;
}

.company-description-text p {
  margin: 0;
}

.company-description-text p + p {
  margin-top: 0.75rem;
}

@media (max-width: 640px) {
  .company-description {
    margin: 1rem 1rem 0;
    padding: 1rem;
    font-size: 0.875rem;
  }
}

Paste this into Custom JavaScript:

Custom JavaScript
<script>
  document.addEventListener("DOMContentLoaded", function () {
    function injectDescription() {
      var anchor = document.querySelector(".mt-16.mb-12.sm\\:mt-20 p.mt-3.font-medium");
      if (!anchor || document.querySelector(".company-description")) return;

      var card = document.createElement("div");
      card.className = "company-description";
      card.innerHTML =
        '<div class="company-description-title">' +
          '<svg class="company-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">' +
            '<path fill-rule="evenodd" d="M10 2C5.58 2 2 5.58 2 10s3.58 8 8 8 8-3.58 8-8-3.58-8-8-8zm3.71 6.71a1 1 0 0 1-1.42 0L9 10.59 7.71 9.29a1 1 0 0 0-1.42 1.42l2 2a1 1 0 0 0 1.42 0l4-4a1 1 0 0 0 0-1.42z"/>' +
          "</svg>" +
          "About this status page" +
        "</div>" +
        '<div class="company-description-text">' +
          "<p>Real-time monitoring for <strong>Acme Corp</strong> production infrastructure with automatic incident detection.</p>" +
          '<p><strong>Subscribe above</strong> for instant alerts, or visit our <a href="https://example.com/support" target="_blank" rel="noopener">Support Center</a> for assistance.</p>' +
        "</div>";

      anchor.parentNode.insertBefore(card, anchor.nextSibling);
    }

    injectDescription();

    var main = document.querySelector("main.container");
    if (main) {
      new MutationObserver(function () {
        if (!document.querySelector(".company-description")) injectDescription();
      }).observe(main, { childList: true, subtree: true });
    }
  });
</script>

Replace "Acme Corp", the title text, and the support link with your own company details.

Live chat widget

If you'd like to embed a support chat on your status page, paste the provider's snippet into Custom JavaScript. Here are examples for common providers:

Custom JavaScript: Intercom Custom JavaScript: Crisp
<script>
  window.intercomSettings = {
    api_base: "https://api-iam.intercom.io",
    app_id: "YOUR_APP_ID"
  };

  (function () {
    var w = window;
    var ic = w.Intercom;
    if (typeof ic === "function") {
      ic("reattach_activator");
      ic("update", w.intercomSettings);
    } else {
      var d = document;
      var i = function () { i.c(arguments); };
      i.q = [];
      i.c = function (args) { i.q.push(args); };
      w.Intercom = i;
      var l = function () {
        var s = d.createElement("script");
        s.type = "text/javascript";
        s.async = true;
        s.src = "https://widget.intercom.io/widget/YOUR_APP_ID";
        var x = d.getElementsByTagName("script")[0];
        x.parentNode.insertBefore(s, x);
      };
      if (document.readyState === "complete") {
        l();
      } else {
        w.addEventListener("load", l, false);
      }
    }
  })();
</script>
<script>
  window.$crisp = [];
  window.CRISP_WEBSITE_ID = "YOUR_WEBSITE_ID";
  (function () {
    var s = document.createElement("script");
    s.src = "https://client.crisp.chat/l.js";
    s.async = true;
    document.head.appendChild(s);
  })();
</script>

Replace YOUR_APP_ID or YOUR_WEBSITE_ID with the value from your chat provider's dashboard.

Status indicator colors

If you'd like to change the colors used for operational, degraded, downtime, or maintenance states, override the status page color variables in Custom CSS:

Custom CSS
.bg-statuspage-green {
  background-color: #059669 !important;
}

.bg-statuspage-yellow {
  background-color: #d97706 !important;
}

.bg-statuspage-red {
  background-color: #dc2626 !important;
}

.bg-statuspage-blue {
  background-color: #2563eb !important;
}

These classes apply to uptime history bars, status icons, and status badges throughout the page. The hover variants follow automatically since they use opacity.

To change the text color of status labels like "Operational" or "Degraded", target the corresponding text classes:

Custom CSS
.text-statuspage-green {
  color: #059669 !important;
}

.text-statuspage-yellow {
  color: #d97706 !important;
}

.text-statuspage-red {
  color: #dc2626 !important;
}

.text-statuspage-blue {
  color: #2563eb !important;
}

Auto-refreshing content

The status page refreshes dynamic sections every 30 seconds. Any DOM changes made by your JavaScript will be overwritten after each refresh.

To make changes persist, use a MutationObserver that re-applies modifications whenever the content updates:

Custom JavaScript
var observer = new MutationObserver(function () {
  var heading = document.querySelector("h1");
  if (heading && heading.textContent === "All services are online") {
    heading.textContent = "Everything is running smoothly";
  }
});

observer.observe(document.body, { childList: true, subtree: true });

Using only CSS?

CSS changes are not affected by auto-refresh. Only use a MutationObserver if you are modifying the page with JavaScript.

API access

You can set custom_css and custom_javascript through the Status Page API:

Update custom CSS via the API
curl -X PATCH "https://incidents.betterstack.com/api/v2/status-pages/{status_page_id}" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"custom_css": "body { background: #f0f4f8; }"}'


Before using custom CSS

Many common customizations are available in the status page settings without writing any code:

  • Branding — upload separate light and dark mode logos, a custom favicon, and set a company URL for the logo link.
  • White-labeling — remove the "Powered by Better Stack" footer.
  • Navigation links — replace the default "Status", "Maintenance", and "Previous incidents" tabs with up to 4 custom links.
  • Translations — adjust any text on the page, including headings, status labels, and timestamp formats.
  • History length — show 7, 14, 30, 90, 180, or 365 days of uptime history.
  • Minimum incident length — filter out short incidents from the history bars.
  • Header layout — choose between vertical and horizontal layouts.
  • Theme — choose light, dark, or system (follows the visitor's OS preference).
  • Password protection and IP allowlisting — restrict access to the page.
  • Hide from search engines — prevent indexing.
  • Announcements — display a banner with optional subscriber notifications.

Tips

  • Use browser developer tools (right-click → Inspect) to find selectors and test changes in the Styles panel before saving.
  • Add !important to declarations that do not take effect — the status page uses Tailwind utility classes.
  • Changes take effect within seconds. No deploy or restart is needed.
  • Test on a non-production status page first, and check on mobile — the navigation collapses into a dropdown on smaller screens.

Need help?

Let us know at hello@betterstack.com.