Want to implement a maintenance page for your studio during current outages?

So… fed up with my studios being out all day and suffering from a reputational hit whilst raising money for my business, I have decided to implement a maintenance page for my Studio.

Looks like this and is fully responsive:

This is the code if you want to implement your own - change the variables for your details… then pop the code into the header box of your Studio Settings - remove when you don’t need it any more! Before you tell me this should already be available from the platform (I agree) - I have nothing to do with PA - I’m a user trying to smooth the problems I am experiencing and have been on this today a bit longer than some others as I’m in the UK!

  Maintenance Overlay Script v2.0
  --------------------------------
  Displays a full-screen, responsive “maintenance” message with up to two buttons.
  Everything is driven by the `config` object below—no hard-coded URLs or colours.
  
  Usage:
    1. Paste this entire <script> into your page (ideally in the <head>).
    2. Edit the values in the `config` section.
-->

<script>
(function(){
  // === CONFIGURATION ===
  var config = {
    // Logo shown above the message
    // Example: 'https://yourdomain.com/assets/logo.svg'
    logoUrl: 'https://example.com/path/to/logo.png',

    // Overlay styling
    backgroundColor: '#1b1b1b',     // page background
    textColor:       '#d3d3d3',     // main text colour
    accentColor:     '#cf000f',     // highlight (e.g. punctuation, buttons)
    fontFamily:      '"Changa One", cursive', // fallback to sans-serif if missing

    // The three lines of your message.
    // The last line will be forced uppercase and colour-cycled.
    messageLines: [
      'We’re sorry,',
      'this site is temporarily unavailable',
      'MAINTENANCE'
    ],

    // Colour-cycle animation duration for the last line
    cycleDuration: '8s',

    // Buttons to display. Up to two.
    // `action` must be a string of JS to execute.
    buttons: [
      {
        text:   'RETRY', 
        action: 'window.location.reload();'
      },
      {
        text:   'CONTACT',
        action: 'window.location.href="mailto:support@example.com";'
      }
    ]
  };

  // === CORE SCRIPT ===
  function showOverlay() {
    // 1) stop loading
    if (window.stop) window.stop();
    else document.execCommand && document.execCommand('Stop');

    // 2) disable scroll
    document.documentElement.style.cssText = 'margin:0;padding:0;overflow:hidden;height:100vh;width:100vw;';
    document.body.style.cssText = 'margin:0;padding:0;overflow:hidden;height:100vh;width:100vw;position:relative;';

    // 3) clear existing content
    while (document.body.firstChild) {
      document.body.removeChild(document.body.firstChild);
    }

    // 4) main container
    var container = document.createElement('div');
    container.style.cssText = [
      'position:absolute;top:0;left:0;width:100%;height:100%;',
      'display:flex;flex-direction:column;justify-content:center;align-items:center;',
      'background:' + config.backgroundColor + ';',
      'color:' + config.textColor + ';',
      'text-align:center;',
      'box-sizing:border-box;padding:1rem;',
      'font-family:' + config.fontFamily + ';'
    ].join('');

    // 5) logo
    if (config.logoUrl) {
      var logo = document.createElement('img');
      logo.src = config.logoUrl;
      logo.alt = 'Logo';
      logo.style.cssText = [
        'width:clamp(300px,80vw,900px);',
        'height:auto;',
        'margin-bottom:2rem;',
        'pointer-events:none;user-select:none;'
      ].join('');
      container.appendChild(logo);
    }

    // 6) message
    var msg = document.createElement('div');
    msg.style.cssText = [
      'max-width:min(90vw,36ch);',
      'padding:1.5rem;',
      'font-size:clamp(1.5rem,4vw,2.5rem);',
      'margin-bottom:1.5rem;',
      'line-height:1.3;'
    ].join('');

    // build lines
    config.messageLines.forEach(function(line, idx) {
      var div = document.createElement('div');
      div.textContent = (idx === config.messageLines.length - 1)
        ? line.toUpperCase()
        : line;
      // last line gets the colour-cycle class
      if (idx === config.messageLines.length - 1) {
        div.className = 'color-cycle';
      }
      // inject accent for any literal "." in earlier lines
      if (idx < config.messageLines.length - 1) {
        div.innerHTML = div.innerHTML.replace(
          '.', '<span style="color:' + config.accentColor + '">.</span>'
        );
      }
      msg.appendChild(div);
    });
    container.appendChild(msg);

    // 7) buttons
    if (config.buttons && config.buttons.length) {
      var btnGroup = document.createElement('div');
      btnGroup.style.cssText = 'display:flex;gap:1rem;';

      config.buttons.forEach(function(btnCfg) {
        var btn = document.createElement('button');
        btn.textContent = btnCfg.text.toUpperCase();
        btn.style.cssText = [
          'display:inline-flex;align-items:center;justify-content:center;',
          'width:clamp(140px,20vw,200px);padding:0.75rem 1.5rem;',
          'font-size:clamp(1rem,2vw,1.25rem);',
          'font-family:' + config.fontFamily + ';font-weight:400;',
          'text-transform:uppercase;',
          'border:none;border-radius:4px;',
          'background:' + config.accentColor + ';color:#fff;cursor:pointer;'
        ].join('');
        btn.addEventListener('click', function(){
          /* eslint-disable no-eval */
          eval(btnCfg.action);
          /* eslint-enable no-eval */
        });
        btnGroup.appendChild(btn);
      });

      container.appendChild(btnGroup);
    }

    document.body.appendChild(container);
  }

  // 8) inject Google Font
  function loadFontAndShow() {
    ['preconnect:https://fonts.googleapis.com',
     'preconnect:https://fonts.gstatic.com',
     'stylesheet:https://fonts.googleapis.com/css2?family=Changa+One&display=swap'
    ].forEach(function(linkDef) {
      var parts = linkDef.split(':');
      var l = document.createElement('link');
      l.rel = parts[0];
      l.href = parts[1];
      if (parts[0] === 'preconnect' && parts[1].includes('gstatic')) {
        l.crossOrigin = '';
      }
      document.head.appendChild(l);
    });

    // add keyframes & color-cycle rule
    var style = document.createElement('style');
    style.textContent = `
      @keyframes colorCycle {
        0%   { color: ${config.accentColor}; }
        25%  { color: #ffdd00; }
        50%  { color: #00ff99; }
        75%  { color: #0080ff; }
        100% { color: ${config.accentColor}; }
      }
      .color-cycle {
        display: inline-block;
        animation: colorCycle ${config.cycleDuration} infinite linear;
      }
      @media (max-width:600px) {
        #maintenance-container {
          justify-content: flex-start !important;
          padding-top: 5rem !important;
        }
      }
    `;
    document.head.appendChild(style);

    // finally, wait for font then show
    document.fonts.load('1rem ' + config.fontFamily).then(showOverlay);
  }

  // init
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', loadFontAndShow);
  } else {
    loadFontAndShow();
  }
})();
</script>

How to customise

  1. config.logoUrl URL to your own logo (or leave empty to omit the logo).
  2. Colours & font
  • backgroundColor: page behind overlay
  • textColor: primary text
  • accentColor: highlights and button background
  • fontFamily: any web-safe or Google Font you prefer
  1. messageLines Array of three strings.
  • Lines 0–1 render normally (with any . turned into an accent-coloured dot).
  • Line 2 renders uppercase and receives the slow colour-cycle effect.
  1. cycleDuration How long (e.g. “8s”) each colour-cycle loop takes.
5 Likes

I am definitely using this

1 Like