From 38cff2d670ecf7fd325aaaf776a4c250a72cc661 Mon Sep 17 00:00:00 2001
From: Jacky Zhao <j.zhao2k19@gmail.com>
Date: Tue, 4 Jul 2023 16:48:36 -0700
Subject: [PATCH] more visual polish, adjust colours and spacing

---
 quartz.config.ts                           |  10 +-
 quartz/components/Backlinks.tsx            |   2 +-
 quartz/components/DesktopOnly.tsx          |   4 +-
 quartz/components/Footer.tsx               |  14 +-
 quartz/components/Graph.tsx                |   2 +-
 quartz/components/MobileOnly.tsx           |   4 +-
 quartz/components/ReadingTime.tsx          |   2 +-
 quartz/components/Spacer.tsx               |   7 +-
 quartz/components/TableOfContents.tsx      |   2 +-
 quartz/components/pages/FolderContent.tsx  |   1 -
 quartz/components/pages/TagContent.tsx     |   1 -
 quartz/components/renderPage.tsx           |  18 +--
 quartz/components/scripts/graph.inline.ts  |  13 +-
 quartz/components/scripts/search.inline.ts |  62 ++++-----
 quartz/components/styles/darkmode.scss     |   2 +-
 quartz/components/styles/footer.scss       |   1 -
 quartz/components/styles/popover.scss      |   4 +-
 quartz/components/styles/search.scss       |   6 +-
 quartz/components/styles/toc.scss          |   2 +-
 quartz/components/types.ts                 |   3 +
 quartz/styles/base.scss                    | 150 +++++++++++++--------
 quartz/styles/variables.scss               |   3 +-
 22 files changed, 173 insertions(+), 140 deletions(-)

diff --git a/quartz.config.ts b/quartz.config.ts
index 18f2533..4f17bd9 100644
--- a/quartz.config.ts
+++ b/quartz.config.ts
@@ -22,6 +22,7 @@ const contentPageLayout: PageLayout = {
   ],
   left: [
     Component.PageTitle(),
+    Component.MobileOnly(Component.Spacer()),
     Component.Search(),
     Component.Darkmode(),
     Component.DesktopOnly(Component.TableOfContents()),
@@ -38,6 +39,7 @@ const listPageLayout: PageLayout = {
   ],
   left: [
     Component.PageTitle(),
+    Component.MobileOnly(Component.Spacer()),
     Component.Search(),
     Component.Darkmode()
   ],
@@ -63,8 +65,8 @@ const config: QuartzConfig = {
       colors: {
         lightMode: {
           light: '#faf8f8',
-          lightgray: '#e8e8e8',
-          gray: '#dadada',
+          lightgray: '#e5e5e5',
+          gray: '#b8b8b8',
           darkgray: '#4e4e4e',
           dark: '#141021',
           secondary: '#284b63',
@@ -73,8 +75,8 @@ const config: QuartzConfig = {
         },
         darkMode: {
           light: '#161618',
-          lightgray: '#292629',
-          gray: '#343434',
+          lightgray: '#393639',
+          gray: '#646464',
           darkgray: '#d4d4d4',
           dark: '#fbfffe',
           secondary: '#7b97aa',
diff --git a/quartz/components/Backlinks.tsx b/quartz/components/Backlinks.tsx
index 5096977..0784e9a 100644
--- a/quartz/components/Backlinks.tsx
+++ b/quartz/components/Backlinks.tsx
@@ -8,7 +8,7 @@ function Backlinks({ fileData, allFiles }: QuartzComponentProps) {
   const backlinkFiles = allFiles.filter(file => file.links?.includes(slug))
   return <div class="backlinks">
     <h3>Backlinks</h3>
-    <ul>
+    <ul class="overflow">
       {backlinkFiles.length > 0 ?
         backlinkFiles.map(f => <li><a href={stripIndex(relativeToRoot(slug, f.slug!))} class="internal">{f.frontmatter?.title}</a></li>)
         : <li>No backlinks found</li>}
diff --git a/quartz/components/DesktopOnly.tsx b/quartz/components/DesktopOnly.tsx
index a1c5dae..a11f23f 100644
--- a/quartz/components/DesktopOnly.tsx
+++ b/quartz/components/DesktopOnly.tsx
@@ -4,9 +4,7 @@ export default ((component?: QuartzComponent) => {
   if (component) {
     const Component = component
     function DesktopOnly(props: QuartzComponentProps) {
-      return <div class="desktop-only">
-        <Component {...props} />
-      </div>
+      return <Component displayClass="desktop-only" {...props} />
     }
 
     DesktopOnly.displayName = component.displayName
diff --git a/quartz/components/Footer.tsx b/quartz/components/Footer.tsx
index 5fc6d64..a4c895b 100644
--- a/quartz/components/Footer.tsx
+++ b/quartz/components/Footer.tsx
@@ -11,15 +11,13 @@ export default ((opts?: Options) => {
     const year = new Date().getFullYear()
     const name = opts?.authorName ?? "someone"
     const links = opts?.links ?? []
-    return <>
+    return <footer>
       <hr />
-      <footer>
-        <p>Made by {name} using <a href="https://quartz.jzhao.xyz/">Quartz</a>, © {year}</p>
-        <ul>{Object.entries(links).map(([text, link]) => <li>
-          <a href={link}>{text}</a>
-        </li>)}</ul>
-      </footer>
-    </>
+      <p>Made by {name} using <a href="https://quartz.jzhao.xyz/">Quartz</a>, © {year}</p>
+      <ul>{Object.entries(links).map(([text, link]) => <li>
+        <a href={link}>{text}</a>
+      </li>)}</ul>
+    </footer>
   }
 
   Footer.css = style
diff --git a/quartz/components/Graph.tsx b/quartz/components/Graph.tsx
index e7f1df2..6d492f2 100644
--- a/quartz/components/Graph.tsx
+++ b/quartz/components/Graph.tsx
@@ -50,7 +50,7 @@ export default ((opts?: GraphOptions) => {
     const localGraph = { ...opts?.localGraph, ...defaultOptions.localGraph }
     const globalGraph = { ...opts?.globalGraph, ...defaultOptions.globalGraph }
     return <div class="graph">
-      <h3>Interactive Graph</h3>
+      <h3>Site Graph</h3>
       <div class="graph-outer">
         <div id="graph-container" data-cfg={JSON.stringify(localGraph)}></div>
         <svg version="1.1" id="global-graph-icon" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
diff --git a/quartz/components/MobileOnly.tsx b/quartz/components/MobileOnly.tsx
index b75fd76..5a19095 100644
--- a/quartz/components/MobileOnly.tsx
+++ b/quartz/components/MobileOnly.tsx
@@ -4,9 +4,7 @@ export default ((component?: QuartzComponent) => {
   if (component) {
     const Component = component
     function MobileOnly(props: QuartzComponentProps) {
-      return <div class="mobile-only">
-        <Component {...props} />
-      </div>
+      return <Component displayClass="mobile-only" {...props} />
     }
 
     MobileOnly.displayName = component.displayName
diff --git a/quartz/components/ReadingTime.tsx b/quartz/components/ReadingTime.tsx
index 9c64289..3e3d4d7 100644
--- a/quartz/components/ReadingTime.tsx
+++ b/quartz/components/ReadingTime.tsx
@@ -14,7 +14,7 @@ function ReadingTime({ fileData }: QuartzComponentProps) {
 ReadingTime.css = `
 .reading-time {
   margin-top: 0;
-  opacity: 0.5;
+  color: var(--gray);
 }
 `
 
diff --git a/quartz/components/Spacer.tsx b/quartz/components/Spacer.tsx
index 34ea084..b9d5bb6 100644
--- a/quartz/components/Spacer.tsx
+++ b/quartz/components/Spacer.tsx
@@ -1,7 +1,8 @@
-import { QuartzComponentConstructor } from "./types"
+import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
 
-function Spacer() {
-  return <div class="spacer"></div>
+function Spacer({ displayClass }: QuartzComponentProps) {
+  const className = displayClass ? `spacer ${displayClass}` : "spacer"
+  return <div class={className}></div>
 }
 
 export default (() => Spacer) satisfies QuartzComponentConstructor
diff --git a/quartz/components/TableOfContents.tsx b/quartz/components/TableOfContents.tsx
index a9d7b78..ddd5195 100644
--- a/quartz/components/TableOfContents.tsx
+++ b/quartz/components/TableOfContents.tsx
@@ -26,7 +26,7 @@ function TableOfContents({ fileData }: QuartzComponentProps) {
       </svg>
     </button>
     <div id="toc-content">
-      <ul>
+      <ul class="overflow">
         {fileData.toc.map(tocEntry => <li key={tocEntry.slug} class={`depth-${tocEntry.depth}`}>
           <a href={`#${tocEntry.slug}`} data-for={tocEntry.slug}>{tocEntry.text}</a>
         </li>)}
diff --git a/quartz/components/pages/FolderContent.tsx b/quartz/components/pages/FolderContent.tsx
index 445074c..b00ea94 100644
--- a/quartz/components/pages/FolderContent.tsx
+++ b/quartz/components/pages/FolderContent.tsx
@@ -27,7 +27,6 @@ function FolderContent(props: QuartzComponentProps) {
   const content = toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: 'html' })
   return <div class="popover-hint">
     <article>{content}</article>
-    <hr/>
     <p>{allPagesInFolder.length} items under this folder.</p>
     <div>
       <PageList {...listProps} /> 
diff --git a/quartz/components/pages/TagContent.tsx b/quartz/components/pages/TagContent.tsx
index b84ce29..81e8de2 100644
--- a/quartz/components/pages/TagContent.tsx
+++ b/quartz/components/pages/TagContent.tsx
@@ -21,7 +21,6 @@ function TagContent(props: QuartzComponentProps) {
     const content = toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: 'html' })
     return <div class="popover-hint">
       <article>{content}</article>
-      <hr/>
       <p>{allPagesWithTag.length} items with this tag.</p>
       <div>
         <PageList {...listProps} />
diff --git a/quartz/components/renderPage.tsx b/quartz/components/renderPage.tsx
index 4da6370..8de60bf 100644
--- a/quartz/components/renderPage.tsx
+++ b/quartz/components/renderPage.tsx
@@ -55,22 +55,22 @@ export function renderPage(slug: string, componentData: QuartzComponentProps, co
     <Head {...componentData} />
     <body data-slug={slug}>
       <div id="quartz-root" class="page">
-        <div class="page-header">
-          <Header {...componentData} >
-            {header.map(HeaderComponent => <HeaderComponent {...componentData} />)}
-          </Header>
-          <div class="popover-hint">
-            {beforeBody.map(BodyComponent => <BodyComponent {...componentData} />)}
-          </div>
-        </div>
         <Body {...componentData}>
           {LeftComponent}
           <div class="center">
+            <div class="page-header">
+              <Header {...componentData} >
+                {header.map(HeaderComponent => <HeaderComponent {...componentData} />)}
+              </Header>
+              <div class="popover-hint">
+                {beforeBody.map(BodyComponent => <BodyComponent {...componentData} />)}
+              </div>
+            </div>
             <Content {...componentData} />
-            <Footer {...componentData} />
           </div>
           {RightComponent}
         </Body>
+        <Footer {...componentData} />
       </div>
     </body>
     {pageResources.js.filter(resource => resource.loadTime === "afterDOMReady").map(res => JSResourceToScriptElement(res))}
diff --git a/quartz/components/scripts/graph.inline.ts b/quartz/components/scripts/graph.inline.ts
index 194a23b..33e90c7 100644
--- a/quartz/components/scripts/graph.inline.ts
+++ b/quartz/components/scripts/graph.inline.ts
@@ -225,7 +225,7 @@ async function renderGraph(container: string, slug: string) {
   const labels = graphNode
     .append("text")
     .attr("dx", 0)
-    .attr("dy", (d) => nodeRadius(d) - 8 + "px")
+    .attr("dy", (d) => -nodeRadius(d) + "px")
     .attr("text-anchor", "middle")
     .text((d) => data[d.id]?.title || (d.id.charAt(1).toUpperCase() + d.id.slice(2)).replace("-", " "))
     .style('opacity', (opacityScale - 1) / 3.75)
@@ -297,14 +297,3 @@ document.addEventListener("nav", async (e: unknown) => {
   containerIcon?.addEventListener("click", renderGlobalGraph)
 })
 
-let resizeEventDebounce: number | undefined = undefined
-window.addEventListener('resize', () => {
-  if (resizeEventDebounce) {
-    clearTimeout(resizeEventDebounce)
-  }
-
-  resizeEventDebounce = window.setTimeout(async () => {
-    const slug = document.body.dataset["slug"]!
-    await renderGraph("graph-container", slug)
-  }, 50)
-})
diff --git a/quartz/components/scripts/search.inline.ts b/quartz/components/scripts/search.inline.ts
index f69d77d..054d352 100644
--- a/quartz/components/scripts/search.inline.ts
+++ b/quartz/components/scripts/search.inline.ts
@@ -58,38 +58,7 @@ const encoder = (str: string) => str.toLowerCase().split(/([^a-z]|[^\x00-\x7F])/
 document.addEventListener("nav", async (e: unknown) => {
   const currentSlug = (e as CustomEventMap["nav"]).detail.url
 
-  // setup index if it hasn't been already
   const data = await fetchData
-  if (!index) {
-    index = new Document({
-      cache: true,
-      charset: 'latin:extra',
-      optimize: true,
-      encode: encoder,
-      document: {
-        id: "slug",
-        index: [
-          {
-            field: "title",
-            tokenize: "forward",
-          },
-          {
-            field: "content",
-            tokenize: "reverse",
-          },
-        ]
-      },
-    })
-
-    for (const [slug, fileData] of Object.entries<ContentDetails>(data)) {
-      await index.addAsync(slug, {
-        slug,
-        title: fileData.title,
-        content: fileData.content
-      })
-    }
-  }
-
   const container = document.getElementById("search-container")
   const searchIcon = document.getElementById("search-icon")
   const searchBar = document.getElementById("search-bar") as HTMLInputElement | null
@@ -176,6 +145,37 @@ document.addEventListener("nav", async (e: unknown) => {
   searchIcon?.addEventListener("click", showSearch)
   searchBar?.removeEventListener("input", onType)
   searchBar?.addEventListener("input", onType)
+  
+  // setup index if it hasn't been already
+  if (!index) {
+    index = new Document({
+      cache: true,
+      charset: 'latin:extra',
+      optimize: true,
+      encode: encoder,
+      document: {
+        id: "slug",
+        index: [
+          {
+            field: "title",
+            tokenize: "forward",
+          },
+          {
+            field: "content",
+            tokenize: "reverse",
+          },
+        ]
+      },
+    })
+
+    for (const [slug, fileData] of Object.entries<ContentDetails>(data)) {
+      await index.addAsync(slug, {
+        slug,
+        title: fileData.title,
+        content: fileData.content
+      })
+    }
+  }
 
   // register handlers
   registerEscapeHandler(container, hideSearch)
diff --git a/quartz/components/styles/darkmode.scss b/quartz/components/styles/darkmode.scss
index 0edfebd..10cbc72 100644
--- a/quartz/components/styles/darkmode.scss
+++ b/quartz/components/styles/darkmode.scss
@@ -2,7 +2,7 @@
   position: relative;
   width: 20px;
   height: 20px;
-  margin: 1rem;
+  margin: 0 10px;
 
   & > .toggle {
     display: none;
diff --git a/quartz/components/styles/footer.scss b/quartz/components/styles/footer.scss
index 16df545..565f9e3 100644
--- a/quartz/components/styles/footer.scss
+++ b/quartz/components/styles/footer.scss
@@ -1,6 +1,5 @@
 footer {
   text-align: left;
-  opacity: 0.8;
   margin-bottom: 4rem;
 
   & ul {
diff --git a/quartz/components/styles/popover.scss b/quartz/components/styles/popover.scss
index 05c6dc7..a372299 100644
--- a/quartz/components/styles/popover.scss
+++ b/quartz/components/styles/popover.scss
@@ -29,11 +29,11 @@
     line-height: normal;
     font-size: initial;
     font-family: var(--bodyFont);
-    border: 1px solid var(--gray);
+    border: 1px solid var(--lightgray);
     background-color: var(--light);
     border-radius: 5px;
     box-shadow: 6px 6px 36px 0 rgba(0,0,0,0.25);
-    overflow: scroll;
+    overflow: auto;
   }
 
   h1 {
diff --git a/quartz/components/styles/search.scss b/quartz/components/styles/search.scss
index 1f0a8b5..fe94ec7 100644
--- a/quartz/components/styles/search.scss
+++ b/quartz/components/styles/search.scss
@@ -12,6 +12,7 @@
     display: flex;
     align-items: center;
     cursor: pointer;
+    white-space: nowrap;
 
     & > div {
       flex-grow: 1;
@@ -38,12 +39,13 @@
 
   & > #search-container {
     position: fixed;
+    contain: layout;
     z-index: 999;
     left: 0;
     top: 0;
     width: 100vw;
     height: 100vh;
-    overflow: scroll;
+    overflow-y: auto;
     display: none;
     backdrop-filter: blur(4px);
 
@@ -57,7 +59,7 @@
       margin-left: auto;
       margin-right: auto;
 
-      @media all and (max-width: $tabletBreakpoint) {
+      @media all and (max-width: $fullPageWidth) {
         width: 90%;
       }
 
diff --git a/quartz/components/styles/toc.scss b/quartz/components/styles/toc.scss
index eee3ee8..08e9548 100644
--- a/quartz/components/styles/toc.scss
+++ b/quartz/components/styles/toc.scss
@@ -29,7 +29,7 @@ button#toc {
   list-style: none;
   overflow: hidden;
   max-height: none;
-  transition: max-height 0.3s ease;
+  transition: max-height 0.5s ease;
 
   & ul {
     list-style: none;
diff --git a/quartz/components/types.ts b/quartz/components/types.ts
index d1c153d..b37ccee 100644
--- a/quartz/components/types.ts
+++ b/quartz/components/types.ts
@@ -11,6 +11,9 @@ export type QuartzComponentProps = {
   children: (QuartzComponent | JSX.Element)[]
   tree: Node<QuartzPluginData>
   allFiles: QuartzPluginData[]
+  displayClass?: 'mobile-only' | 'desktop-only'
+} & JSX.IntrinsicAttributes & {
+  [key: string]: any
 }
 
 export type QuartzComponent = ComponentType<QuartzComponentProps> & {
diff --git a/quartz/styles/base.scss b/quartz/styles/base.scss
index e741755..4d1145d 100644
--- a/quartz/styles/base.scss
+++ b/quartz/styles/base.scss
@@ -43,71 +43,23 @@ a {
   }
 }
 
-.page {
-  & > .page-header {
-    max-width: $pageWidth;
-    margin: $topSpacing auto 0 auto;
-  }
-
-  & > #quartz-body {
-    width: 100%;
-    display: flex;
-
-    & .left, & .right {
-      flex: 1;
-      width: calc(calc(100vw - $pageWidth) / 2);
-    }
-
-    & .left-inner, & .right-inner {
-      display: flex;
-      flex-direction: column;
-      gap: 2rem;
-      top: 0;
-      width: $sidePanelWidth;
-      margin-top: $topSpacing;
-      box-sizing: border-box;
-      padding: 0 4rem;
-      position: fixed;
-    }
-
-    & .left-inner {
-      left: calc(calc(100vw - $pageWidth) / 2 - $sidePanelWidth);
-    }
-
-    & .right-inner {
-      right: calc(calc(100vw - $pageWidth) / 2 - $sidePanelWidth);
-    }
-
-    & .center {
-      width: $pageWidth;
-      margin: 0 auto;
-    }
-  }
-}
-
 .desktop-only {
   display: initial;
-  @media all and (max-width: ($pageWidth + 2 * $sidePanelWidth)) {
+  @media all and (max-width: $fullPageWidth) {
     display: none;
   }
 }
 
 .mobile-only {
   display: none;
-  @media all and (max-width: ($pageWidth + 2 * $sidePanelWidth)) {
+  @media all and (max-width: $fullPageWidth) {
     display: initial;
   }
 }
 
 .page {
-  @media all and (max-width: $tabletBreakpoint) {
-    margin: 25px 5vw;
-    & .left, & .right {
-      padding: 0;
-      height: initial;
-      max-width: none;
-      position: initial;
-    }
+  @media all and (max-width: $fullPageWidth) {
+    margin: 0 5vw;
   }
 
   & p {
@@ -129,6 +81,78 @@ a {
       padding-left: 0;
     }
   }
+
+  & > #quartz-body {
+    width: 100%;
+    display: flex;
+    @media all and (max-width: $fullPageWidth) {
+      flex-direction: column;
+    }
+
+    & .left, & .right {
+      flex: 1;
+      width: calc(calc(100vw - $pageWidth) / 2);
+      @media all and (max-width: $fullPageWidth) {
+        width: initial;
+      }
+    }
+
+    & .left-inner, & .right-inner {
+      display: flex;
+      flex-direction: column;
+      gap: 2rem;
+      top: 0;
+      width: $sidePanelWidth;
+      margin-top: $topSpacing;
+      box-sizing: border-box;
+      padding: 0 4rem;
+      position: fixed;
+      @media all and (max-width: $fullPageWidth) {
+        position: initial;
+        flex-direction: row;
+        padding: 0;
+        width: initial;
+        margin-top: 4rem;
+      }
+    }
+
+    & .left-inner {
+      left: calc(calc(100vw - $pageWidth) / 2 - $sidePanelWidth);
+      @media all and (max-width: $fullPageWidth) {
+        gap: 1rem;
+        align-items: center;
+      }
+    }
+
+    & .right-inner {
+      right: calc(calc(100vw - $pageWidth) / 2 - $sidePanelWidth);
+      & > * {
+        @media all and (max-width: $fullPageWidth) {
+          flex: 1;
+        }
+      }
+    }
+  }
+
+  & .page-header {
+    width: $pageWidth;
+    margin: $topSpacing auto 0 auto;
+    @media all and (max-width: $fullPageWidth) {
+      width: initial;
+      margin-top: 2rem;
+    }
+  }
+
+  & .center, & footer {
+    width: $pageWidth;
+    margin-left: auto;
+    margin-right: auto;
+    @media all and (max-width: $fullPageWidth) {
+      width: initial;
+      margin-left: 0;
+      margin-right: 0;
+    }
+  }
 }
 
 input[type="checkbox"] {
@@ -200,7 +224,7 @@ pre {
   font-family: var(--codeFont);
   padding: 0.5rem;
   border-radius: 5px;
-  overflow-x: scroll;
+  overflow-x: auto;
   border: 1px solid var(--lightgray);
 
   & > code {
@@ -301,3 +325,23 @@ audio, video {
 .spacer {
   flex: 1 1 auto;
 }
+
+ul.overflow, ol.overflow {
+  height: 400px;
+  overflow-y: scroll;
+
+  & > li:last-of-type {
+    margin-bottom: 50px;
+  }
+
+  &:after {
+    pointer-events: none;
+    content: '';
+    width: 100%;
+    height: 50px;    
+    position: absolute;
+    left: 0;
+    bottom: 0;
+    background: linear-gradient(transparent 0px, var(--light));
+  }
+}
diff --git a/quartz/styles/variables.scss b/quartz/styles/variables.scss
index 792223b..6dd5614 100644
--- a/quartz/styles/variables.scss
+++ b/quartz/styles/variables.scss
@@ -1,5 +1,6 @@
-$pageWidth: 800px;
+$pageWidth: 750px;
 $mobileBreakpoint: 600px;
 $tabletBreakpoint: 1200px;
 $sidePanelWidth: 400px;
 $topSpacing: 6rem;
+$fullPageWidth: $pageWidth + 2 * $sidePanelWidth