# 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.

## Prerequisites

- Custom CSS requires a paid plan with the **Custom CSS & JavaScript** feature enabled.
- Custom JavaScript requires a paid plan **and** a [custom domain](https://betterstack.com/docs/uptime/status-pages/custom-domain/) configured on your status page.

## Settings location

Go to [Better Stack → Status pages](https://uptime.betterstack.com/team/0/status-pages) → select your status page → **Settings** → scroll to **Custom CSS** and **Custom JavaScript**.

## Page background

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

```css
[label Custom CSS]
body {
  background: #f0f4f8;
}

.dark body {
  background: #141821;
}
```

## Navigation bar

The nav bar is translucent by default — it uses a semi-transparent white background with a backdrop blur. Here are some ways to adjust it.

If you'd like to change the nav background tint to match a colored page background, update the backdrop, the link hover states, and the subscribe button together:

```css
[label Custom CSS — tinted nav]
nav::before {
  background: #f0f4f8 !important;
  border-bottom-color: transparent !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}

/* Nav links container (has its own white background) */
nav a:first-child + div {
  background: transparent !important;
  box-shadow: none !important;
  border-color: transparent !important;
}

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

/* Nav link hover */
nav a:first-child + div a > div:hover {
  background: #fff !important;
}

/* Subscribe button */
nav > div:last-child a {
  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 a:first-child + div {
  background: transparent !important;
  box-shadow: none !important;
  border-color: transparent !important;
}

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

.dark nav a:first-child + div a > div:hover {
  background: #000 !important;
}

.dark nav > div:last-child a {
  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 nav:

```css
[label Custom CSS — pill-shaped button]
nav > div:last-child a {
  border-radius: 999px;
}
```

```css
[label Custom CSS — brand-colored button]
nav > div:last-child a {
  background: #2563eb !important;
  color: #fff !important;
  border-color: #2563eb !important;
  box-shadow: none !important;
}

.dark nav > div:last-child a {
  background: #3b82f6 !important;
  color: #fff !important;
  border-color: #3b82f6 !important;
  box-shadow: none !important;
}
```

## Sections

Each service group is wrapped in a `.section` element. You can adjust its background, spacing, or remove the default top border that separates sections:

```css
[label 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

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

```css
[label Custom CSS]
.tick {
  height: 24px !important;
  border-radius: 3px !important;
}
```

## Theme examples

These combine the techniques above into cohesive themes. Pick one and paste it into **Custom CSS**.

[code-tabs]
```css
[label Slate (light 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;
}
```

```css
[label Navy (dark mode)]
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;
}

/* Nav links (Status, Maintenance, Previous incidents) */
nav a:first-child ~ div div {
  color: #8896b0;
}

nav a:first-child ~ div div:hover {
  color: #e4eaf4 !important;
  background: #162036 !important;
}

/* Subscribe button */
nav > div:last-child a {
  background: #162036 !important;
  border-color: #1a2540 !important;
  color: #c8d2e0 !important;
}

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

footer {
  opacity: 0.4;
}
```
[/code-tabs]

## Selector reference

Here are the most commonly targeted elements organized by area.

[code-tabs]
```css
[label Page layout]
/* Full page background */
body { }

/* Main content container */
main { }
```

```css
[label Header and navigation]
/* Navigation bar background (translucent pseudo-element) */
nav::before { }

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

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

/* Navigation links container */
nav a:first-child ~ div { }

/* Individual navigation link pill */
nav a:first-child ~ div div { }

/* Subscribe button */
nav > div:last-child a { }
```

```css
[label Status overview]
/* Status heading ("All services are online", etc.) */
h1 { }

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

/* Status icon (SVG next to heading) */
h1 ~ svg { }

/* Announcement banner */
.prose-status-page { }
```

```css
[label Sections and resources]
/* 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 { }
```

```css
[label Incidents and maintenance]
/* "Previous incidents" heading */
h3 { }

/* Maintenance banner link */
main > div:first-child a { }
```

```css
[label Subscribe popup]
/* Email/URL input field */
.subscribe-input { }
```
[/code-tabs]

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

## Custom fonts

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

```css
[label 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.

[code-tabs]
```
[label All sections expanded (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");
});
```

```
[label All sections collapsed (CSS + JS)]
/* 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");
});
```
[/code-tabs]

## Hide elements

Use `display: none` to remove any element from view:

```css
[label Custom CSS]
/* Hide the footer */
footer { display: none; }

/* Hide the subscribe button */
nav > div:last-child a { 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

[info]
Google Analytics has a dedicated field — you do not need custom JavaScript for it. Go to **Status page** → **Advanced settings** → **Analytics** and paste your tag ID into the **Google tag ID** field.
[/info]

For other analytics providers, use the **Custom JavaScript** field. Paste the full `<script>` tag:

```html
[label Custom JavaScript — Plausible Analytics]
<script defer data-domain="status.example.com" src="https://plausible.io/js/script.js"></script>
```

```html
[label Custom JavaScript — Fathom Analytics]
<script src="https://cdn.usefathom.com/script.js" data-site="ABCDEFGH" defer></script>
```

[warning]
Custom JavaScript only runs on status pages with a custom domain configured. It will not run on the default `*.betterstack.com` subdomain.
[/warning]

## Subscribe component picker

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

```javascript
[label 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"));
      }

      var components = document.querySelector(
        '[data-target="status-pages-v2--subscribe.specificComponents"]'
      );
      if (components) {
        components.style.display = "block";
      }
    });
  }
});
```

## 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.

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**:

```css
[label 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**:

```html
[label 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 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:

```html
[label Custom JavaScript — Intercom]
<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>
```

```html
[label Custom JavaScript — Crisp]
<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**:

```css
[label 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:

```css
[label 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:

```javascript
[label 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 });
```

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

## API access

You can set `custom_css` and `custom_javascript` through the [Status Page API](https://betterstack.com/docs/uptime/api/status-pages/):

```bash
[label Update custom CSS via the API]
curl -X PATCH "https://uptime.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](mailto:hello@betterstack.com).
