Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URLs added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Auto Save

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <svg class="inactivity-ring" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg">
  <circle cx="25" cy="25" r="20" stroke-width="10" stroke-linejoin="round" />
</svg>
<main>
  <header>
    <h1>Inactivity Pop-up</h1>
  </header>
  <article>
    <p>
      <strong>
        In this demo, leave the page inactive for 4 seconds, and a pop-up will appear. Inactivity pop-ups are often seen in apps that display secure information. They often tell the user they'll get logged out if the inactivity continues.
      </strong>
    </p>
    <p>Lorem ipsum, dolor, sit amet consectetur adipisicing elit. Repellat illo eum hic possimus, libero voluptates, facere nihil deserunt similique error sunt vero minima enim cupiditate quis velit rem ab repellendus.</p>
    <p>Neque hic veniam cum voluptates quae magnam quisquam saepe. Dolorum quasi amet officiis vero aliquid pariatur commodi repellendus. Quos iure nobis doloremque ipsum facere nostrum neque adipisci hic labore voluptates?</p>
    <p>Mollitia beatae, repellat est laboriosam officiis? Earum veritatis incidunt explicabo id tempora perspiciatis non doloribus natus nisi asperiores quia error autem laudantium, amet doloremque, vitae quos. Soluta unde numquam id.</p>
    <p>Recusandae facere esse quod dolorum maxime corrupti ex quam iure id quis eius cumque fugiat, consequatur autem, excepturi molestias libero velit illo a quo. Sed quae aut inventore quia, ipsa.</p>
    <p>Eius id laboriosam, quas iure laborum corrupti optio corporis tempore odit laudantium similique, accusamus, vel! Perspiciatis sed saepe rem. Nihil explicabo ea at vitae rerum eum, corrupti ipsam suscipit, id!</p>
    <p>Voluptate pariatur, cum praesentium, veniam tenetur distinctio totam voluptates ea beatae, dicta voluptatum illum sit! Earum harum odio natus perspiciatis. Magni eos temporibus harum quas laborum aspernatur, minus, sunt illum.</p>
    <p>Ducimus nobis, libero illum commodi aliquid. Molestias beatae ducimus ratione quod, quis itaque ipsum, illum amet labore, nostrum quisquam enim. Sint aperiam qui nihil eaque id quam odio? Repudiandae, sint!</p>
    <p>In rem tenetur accusamus accusantium id, officia quaerat atque, placeat architecto nihil unde, sit cupiditate minima tempore! Magni, atque labore. Dolor voluptatum eius incidunt, enim? Asperiores similique, ut amet atque?</p>
    <p>Illum, quam fugiat, veniam officia totam nihil aut dolore quo! Officiis ipsa harum vero voluptatibus eaque repudiandae amet eligendi ducimus laboriosam ab maiores labore, odit, voluptas quos illo sint nulla.</p>
    <p>Eius earum nihil officiis, inventore accusantium consequuntur dolor reprehenderit in esse quos eos iste nemo amet facere, mollitia, assumenda cum provident quam. Ipsam, vitae! Excepturi quos possimus enim, quia distinctio.</p>
  </article>
</main>
<div id="inactivity-pop-up" popover>
  <div class="card elevated">
    <div class="title">You've barely touched your HTML</div>
    <div class="subtitle">It'll self-destruct in <span class="seconds"></span> seconds unless you interact.</div>
    <div class="actions">
      <button class="button filled ripple" popoverhidetarget="inactivity-pop-up">Keep it</button>
      <button class="button destructive ripple">Destroy it</button>
    </div>
  </div>
</div>
              
            
!

CSS

              
                @layer demo {
  [popover] {
    transition: transform 0.2s;
    transform: scale(var(--open, 0));
    max-width: calc(100% - (2 * var(--size-4)));
    overflow: visible;
  }

  [popover]:open,
  [popover]:open::backdrop {
    --open: 1;
  }

  [popover]::backdrop {
    transition: opacity 0.2s;
    opacity: var(--open, 0);
    background: hsl(0 0% 10% / 0.5);
  }

  .seconds {
    font-weight: var(--font-weight-9);
  }

  .card {
    min-width: 0;
  }

  .card .actions {
    justify-content: flex-end;
    gap: 1ch;
  }

  .inactivity-ring {
    fill: none;
    position: fixed;
    top: var(--size-4);
    right: var(--size-4);
    color: var(--red-6);
    width: 40px;
    aspect-ratio: 1;
    stroke-dasharray: 130;
    stroke-dashoffset: 130;
    transform: rotate(-90deg);
    z-index: 20;
  }

  .inactivity-ring circle {
    stroke: currentColor;
  }

  .timing .inactivity-ring {
    animation: load calc(var(--threshold) * 1ms) linear;
  }

  .button.destructive {
    background: var(--md-sys-color-error);
    color: var(--md-sys-color-surface);
  }

  .button.destructive:is(:hover, :focus) {
    background: var(--red-6);
  }

  @keyframes load {
    to {
      stroke-dashoffset: 0;
    }
  }
}

@layer base {
  *,
  *:after,
  *:before {
    box-sizing: border-box;
    /*cursor: none;*/
    touch-action: none;
  }

  body {
    display: block;
    min-height: 100vh;
    font-family: "Google Sans", sans-serif, system-ui;
    overflow: auto;
  }

  :where([popover]) {
    margin: auto;
    border-width: 0;
    border-style: none;
    background: transparent;
  }

  h1 {
    margin: 0;
    color: var(--gray-0);
  }

  header {
    height: 25vmin;
    min-height: 200px;
    background: var(--gradient-2);
    display: grid;
    place-items: center;
    color: var(--gray-0);
    padding: var(--size-4);
    grid-template-columns: 1fr;
  }

  main {
    margin: 0 auto;
  }

  article {
    padding: var(--size-4);
    display: flex;
    flex-direction: column;
    align-items: center;
  }

  article > * + * {
    margin-top: var(--size-4);
  }
}

              
            
!

JS

              
                const POPUP = document.querySelector("#inactivity-pop-up");
const RING = document.querySelector("svg");
const BUTTON = document.querySelector("button.destructive");

const SCREENSAVER_THRESHOLD = 4000;
const DESTRUCTIVE_THRESHOLD = 9;

let checker;
let screensaverTimeout;
let secondsInterval;
const EVENT_TYPES = [
  "pointermove",
  "keypress",
  "keydown",
  "keyup",
  "scroll",
  "click"
];

const setSaverTimer = () => {
  if (screensaverTimeout) {
    clearTimeout(screensaverTimeout);
    document.body.classList.remove("timing");
  }

  if (!POPUP.matches(":open")) {
    screensaverTimeout = setTimeout(() => {
      document.body.classList.remove("timing");
      POPUP.showPopover();
    }, SCREENSAVER_THRESHOLD);
    requestAnimationFrame(() => {
      document.body.classList.add("timing");
    });
  }
};

const destroy = () => {
  if (secondsInterval) clearInterval(secondsInterval);

  document.body.style = `
    display: grid;
    place-items: center;
  `;
  document.body.innerHTML = "<p>it's gone...</p>";

  if (screensaverTimeout) {
    clearTimeout(screensaverTimeout);
    document.body.classList.remove("timing");
  }

  EVENT_TYPES.forEach((e) =>
    document.body.removeEventListener(e, setSaverTimer)
  );
};

document.body.classList.add("timing");
document.documentElement.style.setProperty(
  "--threshold",
  SCREENSAVER_THRESHOLD
);

const SECONDS = document.querySelector(".seconds");

POPUP.addEventListener("beforetoggle", ({ newState }) => {
  if (newState === "closed") {
    if (POPUP.parentNode) setSaverTimer();
    if (secondsInterval) clearInterval(secondsInterval);
  } else {
    SECONDS.innerText = DESTRUCTIVE_THRESHOLD;
    secondsInterval = setInterval(() => {
      SECONDS.innerText -= 1;
      if (parseInt(SECONDS.innerText, 10) === 0) destroy();
    }, 1000);
  }
});

BUTTON.addEventListener("click", destroy);

setSaverTimer();

EVENT_TYPES.forEach((e) => document.body.addEventListener(e, setSaverTimer));

              
            
!
999px

Console