LISN.js

Edit on StackBlitz

"use client";
import { useEffect, useRef } from "react";

import { LayoutWatcher, LayoutData, LayoutTrigger, Show } from "lisn.js";
import "lisn.js/base.css";

import styles from "./demo.module.css";

export default function Page() {
  const boxRef = useRef<HTMLDivElement>(null);
  const msgRef = useRef<HTMLHeadingElement>(null);
  const windowLayoutMsgRef = useRef<HTMLParagraphElement>(null);
  const boxLayoutMsgRef = useRef<HTMLParagraphElement>(null);

  useEffect(() => {
    // box layout
    const box = boxRef.current;
    const msg = boxLayoutMsgRef.current;
    const handler = (layout: LayoutData) => {
      const { device, aspectRatio } = layout;
      if (msg && device && aspectRatio) {
        msg.innerText =
          `The resizable box below has a ${formatLayout(device)} width ` +
          `and a ${formatLayout(aspectRatio)} aspect ratio.`;
      }
    };
    let watcher: LayoutWatcher;

    if (box) {
      watcher = LayoutWatcher.create({ root: box });
      watcher.onLayout(handler);
    }

    return () => {
      // cleanup
      watcher?.offLayout(handler);
    };
  }, []);

  useEffect(() => {
    // window layout
    const msg = windowLayoutMsgRef.current;
    const handler = (layout: LayoutData) => {
      const { device, aspectRatio } = layout;
      if (msg && device && aspectRatio) {
        msg.innerText =
          `This is a ${formatLayout(device)} device ` +
          `with a ${formatLayout(aspectRatio)} aspect ratio.`;
      }
    };
    const watcher = LayoutWatcher.reuse();
    watcher.onLayout(handler);

    return () => {
      // cleanup
      watcher.offLayout(handler);
    };
  }, []);

  useEffect(() => {
    // You could also use LayoutWatcher directly and show/hide the element in
    // your callback
    const msg = msgRef.current;
    let trigger: LayoutTrigger;
    if (msg) {
      trigger = new LayoutTrigger(msg, [new Show(msg)], {
        layout: "max tablet",
      });
    }

    return () => {
      // cleanup
      trigger?.destroy();
    };
  }, []);

  return (
    <>
      <div className={styles.wrapper}>
        <h4 ref={msgRef} className={[styles.msg, "lisn-hide"].join(" ")}>
          This demo is best viewed on a computer.
        </h4>

        <div className={styles.demo}>
          <p ref={windowLayoutMsgRef}></p>
          <p ref={boxLayoutMsgRef}></p>
          <div ref={boxRef} className={styles.box}></div>
        </div>
      </div>
    </>
  );
}

const layoutMapping: { [K in string]: string } = {
  "mobile-wide": "mobile (landscape)",
  square: "roughly square",
};

const formatLayout = (l: string) =>
  (l in layoutMapping ? layoutMapping[l] : l).replace(/-/g, " ");
/* Container */
.wrapper {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  width: 100dvw;
  height: 100vh;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

.demo {
  text-align: center;
  width: 100%;
  padding: 1em;
}

.box {
  margin: 0 auto;
  width: 400px;
  height: 400px;
  resize: both;
  background: var(--bg-color-lighter);
  box-shadow: var(--lisn-shadow);
  overflow: hidden;
}

Edit on CodePen

document.addEventListener("DOMContentLoaded", () => {
  const layoutMapping = {
    "mobile-wide": "mobile (landscape)",
    square: "roughly square",
  };

  const formatLayout = (l) => (layoutMapping[l] ?? l).replace(/-/g, " ");

  const main = document.getElementById("demo");
  const box = main.querySelector(".box");
  const windowLayout = main.querySelector(".window-layout");
  const boxLayout = main.querySelector(".box-layout");

  LISN.watchers.LayoutWatcher.reuse().onLayout((layout) => {
    windowLayout.innerText =
      `This is a ${formatLayout(layout.device)} device ` +
      `with a ${formatLayout(layout.aspectRatio)} aspect ratio.`;
  });

  LISN.watchers.LayoutWatcher.create({ root: box }).onLayout((layout) => {
    boxLayout.innerText =
      `The resizable box below has a ${formatLayout(layout.device)} width ` +
      `and a ${formatLayout(layout.aspectRatio)} aspect ratio.`;
  });

  const msg = document.getElementById("msg");
  // or you could use LayoutWatcher directly and show/hide the element in your callback
  new LISN.triggers.LayoutTrigger(msg, [new LISN.actions.Show(msg)], {
    layout: "max tablet",
  });
});
<h4 id="msg">This demo is best viewed on a computer.</h4>

<div id="demo">
  <p class="window-layout"></p>
  <p class="box-layout"></p>
  <div class="box"></div>
</div>
#demo {
  text-align: center;
  width: 100%;
  padding: 1em;
}

#demo .box {
  margin: 0 auto;
  width: 400px;
  height: 400px;
  resize: both;
  background: #28283f;
  box-shadow: var(--lisn-shadow);
  overflow: hidden;
}

Edit on CodePen

<h4 data-lisn-on-layout="max tablet @show">
  This demo is best viewed on a computer.
</h4>

<div id="demo">
  <p>
    This is a
    <span data-lisn-on-layout="mobile @display">mobile</span>
    <span data-lisn-on-layout="mobile-wide @display"
      >mobile (landscape)</span
    >
    <span data-lisn-on-layout="tablet @display">tablet</span>
    <span data-lisn-on-layout="desktop @display">desktop</span>
    device with a
    <span data-lisn-on-layout="very-wide @display">very-wide</span>
    <span data-lisn-on-layout="wide @display">wide</span>
    <span data-lisn-on-layout="square @display">roughly square</span>
    <span data-lisn-on-layout="tall @display">tall</span>
    <span data-lisn-on-layout="very-tall @display">very tall</span>
    aspect ratio.
  </p>

  <p>
    The resizable box below has a
    <span data-lisn-on-layout="mobile @display +root=next.box"
      >mobile</span
    >
    <span data-lisn-on-layout="mobile-wide @display +root=next.box"
      >mobile (landscape)</span
    >
    <span data-lisn-on-layout="tablet @display +root=next.box"
      >tablet</span
    >
    <span data-lisn-on-layout="desktop @display +root=next.box"
      >desktop</span
    >
    width and a
    <span data-lisn-on-layout="very-wide @display +root=next.box"
      >very-wide</span
    >
    <span data-lisn-on-layout="wide @display +root=next.box">wide</span>
    <span data-lisn-on-layout="square @display +root=next.box"
      >roughly square</span
    >
    <span data-lisn-on-layout="tall @display +root=next.box">tall</span>
    <span data-lisn-on-layout="very-tall @display +root=next.box"
      >very tall</span
    >
    aspect ratio.
  </p>

  <div class="box"></div>
</div>
#demo {
  text-align: center;
  width: 100%;
  padding: 1em;
}

#demo .box {
  margin: 0 auto;
  width: 400px;
  height: 400px;
  resize: both;
  background: #28283f;
  box-shadow: var(--lisn-shadow);
  overflow: hidden;
}

This demo is best viewed on a computer.