feat(explorer): collapsible mobile explorer (#1471)
Co-authored-by: Aaron Pham <Aaronpham0103@gmail.com>
This commit is contained in:
		@@ -27,7 +27,7 @@ export const defaultContentPageLayout: PageLayout = {
 | 
			
		||||
    Component.MobileOnly(Component.Spacer()),
 | 
			
		||||
    Component.Search(),
 | 
			
		||||
    Component.Darkmode(),
 | 
			
		||||
    Component.DesktopOnly(Component.Explorer()),
 | 
			
		||||
    Component.Explorer(),
 | 
			
		||||
  ],
 | 
			
		||||
  right: [
 | 
			
		||||
    Component.Graph(),
 | 
			
		||||
@@ -44,7 +44,7 @@ export const defaultListPageLayout: PageLayout = {
 | 
			
		||||
    Component.MobileOnly(Component.Spacer()),
 | 
			
		||||
    Component.Search(),
 | 
			
		||||
    Component.Darkmode(),
 | 
			
		||||
    Component.DesktopOnly(Component.Explorer()),
 | 
			
		||||
    Component.Explorer(),
 | 
			
		||||
  ],
 | 
			
		||||
  right: [],
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
 | 
			
		||||
import explorerStyle from "./styles/explorer.scss"
 | 
			
		||||
import style from "./styles/explorer.scss"
 | 
			
		||||
 | 
			
		||||
// @ts-ignore
 | 
			
		||||
import script from "./scripts/explorer.inline"
 | 
			
		||||
@@ -83,18 +83,46 @@ export default ((userOpts?: Partial<Options>) => {
 | 
			
		||||
      lastBuildId = ctx.buildId
 | 
			
		||||
      constructFileTree(allFiles)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <div class={classNames(displayClass, "explorer")}>
 | 
			
		||||
        <button
 | 
			
		||||
          type="button"
 | 
			
		||||
          id="explorer"
 | 
			
		||||
          id="mobile-explorer"
 | 
			
		||||
          class="collapsed hide-until-loaded"
 | 
			
		||||
          data-behavior={opts.folderClickBehavior}
 | 
			
		||||
          data-collapsed={opts.folderDefaultState}
 | 
			
		||||
          data-savestate={opts.useSavedState}
 | 
			
		||||
          data-tree={jsonTree}
 | 
			
		||||
          data-mobile={true}
 | 
			
		||||
          aria-controls="explorer-content"
 | 
			
		||||
          aria-expanded={opts.folderDefaultState === "open"}
 | 
			
		||||
          aria-expanded={false}
 | 
			
		||||
        >
 | 
			
		||||
          <svg
 | 
			
		||||
            xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
            width="24"
 | 
			
		||||
            height="24"
 | 
			
		||||
            viewBox="0 0 24 24"
 | 
			
		||||
            stroke-width="2"
 | 
			
		||||
            stroke-linecap="round"
 | 
			
		||||
            stroke-linejoin="round"
 | 
			
		||||
            class="lucide lucide-menu"
 | 
			
		||||
          >
 | 
			
		||||
            <line x1="4" x2="20" y1="12" y2="12" />
 | 
			
		||||
            <line x1="4" x2="20" y1="6" y2="6" />
 | 
			
		||||
            <line x1="4" x2="20" y1="18" y2="18" />
 | 
			
		||||
          </svg>
 | 
			
		||||
        </button>
 | 
			
		||||
        <button
 | 
			
		||||
          type="button"
 | 
			
		||||
          id="desktop-explorer"
 | 
			
		||||
          class="title-button"
 | 
			
		||||
          data-behavior={opts.folderClickBehavior}
 | 
			
		||||
          data-collapsed={opts.folderDefaultState}
 | 
			
		||||
          data-savestate={opts.useSavedState}
 | 
			
		||||
          data-tree={jsonTree}
 | 
			
		||||
          data-mobile={false}
 | 
			
		||||
          aria-controls="explorer-content"
 | 
			
		||||
          aria-expanded={true}
 | 
			
		||||
        >
 | 
			
		||||
          <h2>{opts.title ?? i18n(cfg.locale).components.explorer.title}</h2>
 | 
			
		||||
          <svg
 | 
			
		||||
@@ -122,7 +150,7 @@ export default ((userOpts?: Partial<Options>) => {
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Explorer.css = explorerStyle
 | 
			
		||||
  Explorer.css = style
 | 
			
		||||
  Explorer.afterDOMLoaded = script
 | 
			
		||||
  return Explorer
 | 
			
		||||
}) satisfies QuartzComponentConstructor
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,9 @@
 | 
			
		||||
import { FolderState } from "../ExplorerNode"
 | 
			
		||||
 | 
			
		||||
// Current state of folders
 | 
			
		||||
type MaybeHTMLElement = HTMLElement | undefined
 | 
			
		||||
let currentExplorerState: FolderState[]
 | 
			
		||||
 | 
			
		||||
const observer = new IntersectionObserver((entries) => {
 | 
			
		||||
  // If last element is observed, remove gradient of "overflow" class so element is visible
 | 
			
		||||
  const explorerUl = document.getElementById("explorer-ul")
 | 
			
		||||
@@ -16,23 +18,43 @@ const observer = new IntersectionObserver((entries) => {
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
function toggleExplorer(this: HTMLElement) {
 | 
			
		||||
  // Toggle collapsed state of entire explorer
 | 
			
		||||
  this.classList.toggle("collapsed")
 | 
			
		||||
 | 
			
		||||
  // Toggle collapsed aria state of entire explorer
 | 
			
		||||
  this.setAttribute(
 | 
			
		||||
    "aria-expanded",
 | 
			
		||||
    this.getAttribute("aria-expanded") === "true" ? "false" : "true",
 | 
			
		||||
  )
 | 
			
		||||
  const content = this.nextElementSibling as MaybeHTMLElement
 | 
			
		||||
  if (!content) return
 | 
			
		||||
 | 
			
		||||
  const content = (
 | 
			
		||||
    this.nextElementSibling?.nextElementSibling
 | 
			
		||||
      ? this.nextElementSibling.nextElementSibling
 | 
			
		||||
      : this.nextElementSibling
 | 
			
		||||
  ) as MaybeHTMLElement
 | 
			
		||||
  if (!content) return
 | 
			
		||||
  content.classList.toggle("collapsed")
 | 
			
		||||
  content.classList.toggle("explorer-viewmode")
 | 
			
		||||
 | 
			
		||||
  // Prevent scroll under
 | 
			
		||||
  if (document.querySelector("#mobile-explorer")) {
 | 
			
		||||
    // Disable scrolling on the page when the explorer is opened on mobile
 | 
			
		||||
    const bodySelector = document.querySelector("#quartz-body")
 | 
			
		||||
    if (bodySelector) bodySelector.classList.toggle("lock-scroll")
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function toggleFolder(evt: MouseEvent) {
 | 
			
		||||
  evt.stopPropagation()
 | 
			
		||||
 | 
			
		||||
  // Element that was clicked
 | 
			
		||||
  const target = evt.target as MaybeHTMLElement
 | 
			
		||||
  if (!target) return
 | 
			
		||||
 | 
			
		||||
  // Check if target was svg icon or button
 | 
			
		||||
  const isSvg = target.nodeName === "svg"
 | 
			
		||||
 | 
			
		||||
  // corresponding <ul> element relative to clicked button/folder
 | 
			
		||||
  const childFolderContainer = (
 | 
			
		||||
    isSvg
 | 
			
		||||
      ? target.parentElement?.nextSibling
 | 
			
		||||
@@ -42,10 +64,14 @@ function toggleFolder(evt: MouseEvent) {
 | 
			
		||||
    isSvg ? target.nextElementSibling : target.parentElement
 | 
			
		||||
  ) as MaybeHTMLElement
 | 
			
		||||
  if (!(childFolderContainer && currentFolderParent)) return
 | 
			
		||||
 | 
			
		||||
  // <li> element of folder (stores folder-path dataset)
 | 
			
		||||
  childFolderContainer.classList.toggle("open")
 | 
			
		||||
 | 
			
		||||
  // Collapse folder container
 | 
			
		||||
  const isCollapsed = childFolderContainer.classList.contains("open")
 | 
			
		||||
  setFolderState(childFolderContainer, !isCollapsed)
 | 
			
		||||
 | 
			
		||||
  // Save folder state to localStorage
 | 
			
		||||
  const fullFolderPath = currentFolderParent.dataset.folderpath as string
 | 
			
		||||
  toggleCollapsedByPath(currentExplorerState, fullFolderPath)
 | 
			
		||||
  const stringifiedFileTree = JSON.stringify(currentExplorerState)
 | 
			
		||||
@@ -53,20 +79,34 @@ function toggleFolder(evt: MouseEvent) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function setupExplorer() {
 | 
			
		||||
  const explorer = document.getElementById("explorer")
 | 
			
		||||
  if (!explorer) return
 | 
			
		||||
  // Set click handler for collapsing entire explorer
 | 
			
		||||
  const allExplorers = document.querySelectorAll(".explorer > button") as NodeListOf<HTMLElement>
 | 
			
		||||
 | 
			
		||||
  if (explorer.dataset.behavior === "collapse") {
 | 
			
		||||
  for (const explorer of allExplorers) {
 | 
			
		||||
    // Get folder state from local storage
 | 
			
		||||
    const storageTree = localStorage.getItem("fileTree")
 | 
			
		||||
 | 
			
		||||
    // Convert to bool
 | 
			
		||||
    const useSavedFolderState = explorer?.dataset.savestate === "true"
 | 
			
		||||
 | 
			
		||||
    if (explorer) {
 | 
			
		||||
      // Get config
 | 
			
		||||
      const collapseBehavior = explorer.dataset.behavior
 | 
			
		||||
 | 
			
		||||
      // Add click handlers for all folders (click handler on folder "label")
 | 
			
		||||
      if (collapseBehavior === "collapse") {
 | 
			
		||||
        for (const item of document.getElementsByClassName(
 | 
			
		||||
          "folder-button",
 | 
			
		||||
        ) as HTMLCollectionOf<HTMLElement>) {
 | 
			
		||||
          window.addCleanup(() => explorer.removeEventListener("click", toggleExplorer))
 | 
			
		||||
          item.addEventListener("click", toggleFolder)
 | 
			
		||||
      window.addCleanup(() => item.removeEventListener("click", toggleFolder))
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
  explorer.addEventListener("click", toggleExplorer)
 | 
			
		||||
      // Add click handler to main explorer
 | 
			
		||||
      window.addCleanup(() => explorer.removeEventListener("click", toggleExplorer))
 | 
			
		||||
      explorer.addEventListener("click", toggleExplorer)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set up click handlers for each folder (click handler on folder "icon")
 | 
			
		||||
    for (const item of document.getElementsByClassName(
 | 
			
		||||
@@ -77,8 +117,6 @@ function setupExplorer() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get folder state from local storage
 | 
			
		||||
  const storageTree = localStorage.getItem("fileTree")
 | 
			
		||||
  const useSavedFolderState = explorer?.dataset.savestate === "true"
 | 
			
		||||
    const oldExplorerState: FolderState[] =
 | 
			
		||||
      storageTree && useSavedFolderState ? JSON.parse(storageTree) : []
 | 
			
		||||
    const oldIndex = new Map(oldExplorerState.map((entry) => [entry.path, entry.collapsed]))
 | 
			
		||||
@@ -86,24 +124,61 @@ function setupExplorer() {
 | 
			
		||||
      ? JSON.parse(explorer.dataset.tree)
 | 
			
		||||
      : []
 | 
			
		||||
    currentExplorerState = []
 | 
			
		||||
 | 
			
		||||
    for (const { path, collapsed } of newExplorerState) {
 | 
			
		||||
    currentExplorerState.push({ path, collapsed: oldIndex.get(path) ?? collapsed })
 | 
			
		||||
      currentExplorerState.push({
 | 
			
		||||
        path,
 | 
			
		||||
        collapsed: oldIndex.get(path) ?? collapsed,
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    currentExplorerState.map((folderState) => {
 | 
			
		||||
      const folderLi = document.querySelector(
 | 
			
		||||
      `[data-folderpath='${folderState.path}']`,
 | 
			
		||||
        `[data-folderpath='${folderState.path.replace("'", "-")}']`,
 | 
			
		||||
      ) as MaybeHTMLElement
 | 
			
		||||
      const folderUl = folderLi?.parentElement?.nextElementSibling as MaybeHTMLElement
 | 
			
		||||
      if (folderUl) {
 | 
			
		||||
        setFolderState(folderUl, folderState.collapsed)
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function toggleExplorerFolders() {
 | 
			
		||||
  const currentFile = (document.querySelector("body")?.getAttribute("data-slug") ?? "").replace(
 | 
			
		||||
    /\/index$/g,
 | 
			
		||||
    "",
 | 
			
		||||
  )
 | 
			
		||||
  const allFolders = document.querySelectorAll(".folder-outer")
 | 
			
		||||
 | 
			
		||||
  allFolders.forEach((element) => {
 | 
			
		||||
    const folderUl = Array.from(element.children).find((child) =>
 | 
			
		||||
      child.matches("ul[data-folderul]"),
 | 
			
		||||
    )
 | 
			
		||||
    if (folderUl) {
 | 
			
		||||
      if (currentFile.includes(folderUl.getAttribute("data-folderul") ?? "")) {
 | 
			
		||||
        if (!element.classList.contains("open")) {
 | 
			
		||||
          element.classList.add("open")
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
window.addEventListener("resize", setupExplorer)
 | 
			
		||||
 | 
			
		||||
document.addEventListener("nav", () => {
 | 
			
		||||
  const explorer = document.querySelector("#mobile-explorer")
 | 
			
		||||
  if (explorer) {
 | 
			
		||||
    explorer.classList.add("collapsed")
 | 
			
		||||
    const content = explorer.nextElementSibling?.nextElementSibling as HTMLElement
 | 
			
		||||
    if (content) {
 | 
			
		||||
      content.classList.add("collapsed")
 | 
			
		||||
      content.classList.toggle("explorer-viewmode")
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  setupExplorer()
 | 
			
		||||
 | 
			
		||||
  observer.disconnect()
 | 
			
		||||
 | 
			
		||||
  // select pseudo element at end of list
 | 
			
		||||
@@ -111,6 +186,12 @@ document.addEventListener("nav", () => {
 | 
			
		||||
  if (lastItem) {
 | 
			
		||||
    observer.observe(lastItem)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Hide explorer on mobile until it is requested
 | 
			
		||||
  const hiddenUntilDoneLoading = document.querySelector("#mobile-explorer")
 | 
			
		||||
  hiddenUntilDoneLoading?.classList.remove("hide-until-loaded")
 | 
			
		||||
 | 
			
		||||
  toggleExplorerFolders()
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,70 @@
 | 
			
		||||
@use "../../styles/variables.scss" as *;
 | 
			
		||||
 | 
			
		||||
@media all and ($mobile) {
 | 
			
		||||
  .page > #quartz-body {
 | 
			
		||||
    // Shift page position when toggling Explorer on mobile.
 | 
			
		||||
    & > :not(.sidebar.left:has(.explorer)) {
 | 
			
		||||
      transform: translateX(0);
 | 
			
		||||
      transition: transform 300ms ease-in-out;
 | 
			
		||||
    }
 | 
			
		||||
    &.lock-scroll > :not(.sidebar.left:has(.explorer)) {
 | 
			
		||||
      transform: translateX(100dvw);
 | 
			
		||||
      transition: transform 300ms ease-in-out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Sticky top bar (stays in place when scrolling down on mobile).
 | 
			
		||||
    .sidebar.left:has(.explorer) {
 | 
			
		||||
      box-sizing: border-box;
 | 
			
		||||
      position: sticky;
 | 
			
		||||
      background-color: var(--light);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Hide Explorer on mobile until done loading.
 | 
			
		||||
    // Prevents ugly animation on page load.
 | 
			
		||||
    .hide-until-loaded ~ #explorer-content {
 | 
			
		||||
      display: none;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.explorer {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  overflow-y: hidden;
 | 
			
		||||
 | 
			
		||||
  @media all and ($mobile) {
 | 
			
		||||
    order: -1;
 | 
			
		||||
    height: initial;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
    flex-shrink: 0;
 | 
			
		||||
    align-self: flex-start;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  button#mobile-explorer {
 | 
			
		||||
    display: none;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  button#desktop-explorer {
 | 
			
		||||
    display: flex;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @media all and ($mobile) {
 | 
			
		||||
    button#mobile-explorer {
 | 
			
		||||
      display: flex;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    button#desktop-explorer {
 | 
			
		||||
      display: none;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &.desktop-only {
 | 
			
		||||
    @media all and not ($mobile) {
 | 
			
		||||
      display: flex;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /*&:after {
 | 
			
		||||
    pointer-events: none;
 | 
			
		||||
    content: "";
 | 
			
		||||
@@ -23,7 +79,8 @@
 | 
			
		||||
  }*/
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
button#explorer {
 | 
			
		||||
button#mobile-explorer,
 | 
			
		||||
button#desktop-explorer {
 | 
			
		||||
  background-color: transparent;
 | 
			
		||||
  border: none;
 | 
			
		||||
  text-align: left;
 | 
			
		||||
@@ -68,19 +125,19 @@ button#explorer {
 | 
			
		||||
  list-style: none;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  overflow-y: auto;
 | 
			
		||||
  max-height: 0px;
 | 
			
		||||
  transition:
 | 
			
		||||
    max-height 0.35s ease,
 | 
			
		||||
    visibility 0s linear 0.35s;
 | 
			
		||||
  margin-top: 0.5rem;
 | 
			
		||||
  visibility: hidden;
 | 
			
		||||
 | 
			
		||||
  &.collapsed {
 | 
			
		||||
    max-height: 100%;
 | 
			
		||||
    transition:
 | 
			
		||||
      max-height 0.35s ease,
 | 
			
		||||
      visibility 0s linear 0s;
 | 
			
		||||
  margin-top: 0.5rem;
 | 
			
		||||
    visibility: visible;
 | 
			
		||||
 | 
			
		||||
  &.collapsed {
 | 
			
		||||
    max-height: 0;
 | 
			
		||||
    transition:
 | 
			
		||||
      max-height 0.35s ease,
 | 
			
		||||
      visibility 0s linear 0.35s;
 | 
			
		||||
    visibility: hidden;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  & ul {
 | 
			
		||||
@@ -91,12 +148,14 @@ button#explorer {
 | 
			
		||||
      max-height 0.35s ease,
 | 
			
		||||
      transform 0.35s ease,
 | 
			
		||||
      opacity 0.2s ease;
 | 
			
		||||
 | 
			
		||||
    & li > a {
 | 
			
		||||
      color: var(--dark);
 | 
			
		||||
      opacity: 0.75;
 | 
			
		||||
      pointer-events: all;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  > #explorer-ul {
 | 
			
		||||
    max-height: none;
 | 
			
		||||
  }
 | 
			
		||||
@@ -179,3 +238,80 @@ li:has(> .folder-outer:not(.open)) > .folder-container > svg {
 | 
			
		||||
  // remove default margin from li
 | 
			
		||||
  margin: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.explorer {
 | 
			
		||||
  @media all and ($mobile) {
 | 
			
		||||
    #explorer-content {
 | 
			
		||||
      box-sizing: border-box;
 | 
			
		||||
      overscroll-behavior: none;
 | 
			
		||||
      z-index: 100;
 | 
			
		||||
      position: absolute;
 | 
			
		||||
      top: 0;
 | 
			
		||||
      background-color: var(--light);
 | 
			
		||||
      max-width: 100dvw;
 | 
			
		||||
      left: -100dvw;
 | 
			
		||||
      width: 100%;
 | 
			
		||||
      transition: transform 300ms ease-in-out;
 | 
			
		||||
      overflow: hidden;
 | 
			
		||||
      padding: $topSpacing 2rem 2rem;
 | 
			
		||||
      height: 100dvh;
 | 
			
		||||
      max-height: 100dvh;
 | 
			
		||||
      margin-top: 0;
 | 
			
		||||
      visibility: hidden;
 | 
			
		||||
 | 
			
		||||
      &:not(.collapsed) {
 | 
			
		||||
        transform: translateX(100dvw);
 | 
			
		||||
        visibility: visible;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      ul.overflow {
 | 
			
		||||
        max-height: 100%;
 | 
			
		||||
        width: 100%;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      &.collapsed {
 | 
			
		||||
        transform: translateX(0);
 | 
			
		||||
        visibility: visible;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #mobile-explorer {
 | 
			
		||||
      margin: 5px;
 | 
			
		||||
      z-index: 101;
 | 
			
		||||
 | 
			
		||||
      &:not(.collapsed) .lucide-menu {
 | 
			
		||||
        transform: rotate(-90deg);
 | 
			
		||||
        transition: transform 200ms ease-in-out;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .lucide-menu {
 | 
			
		||||
        stroke: var(--darkgray);
 | 
			
		||||
        transition: transform 200ms ease;
 | 
			
		||||
 | 
			
		||||
        &:hover {
 | 
			
		||||
          stroke: var(--dark);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.no-scroll {
 | 
			
		||||
  opacity: 0;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
html:has(.no-scroll) {
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media all and not ($mobile) {
 | 
			
		||||
  .no-scroll {
 | 
			
		||||
    opacity: 1 !important;
 | 
			
		||||
    overflow: auto !important;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  html:has(.no-scroll) {
 | 
			
		||||
    overflow: auto !important;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user