Jacky Zhao
2023-07-04 38cff2d670ecf7fd325aaaf776a4c250a72cc661
more visual polish, adjust colours and spacing
22 files modified
293 ■■■■■ changed files
quartz.config.ts 10 ●●●●● patch | view | raw | blame | history
quartz/components/Backlinks.tsx 2 ●●● patch | view | raw | blame | history
quartz/components/DesktopOnly.tsx 4 ●●● patch | view | raw | blame | history
quartz/components/Footer.tsx 4 ●●● patch | view | raw | blame | history
quartz/components/Graph.tsx 2 ●●● patch | view | raw | blame | history
quartz/components/MobileOnly.tsx 4 ●●● patch | view | raw | blame | history
quartz/components/ReadingTime.tsx 2 ●●● patch | view | raw | blame | history
quartz/components/Spacer.tsx 7 ●●●●● patch | view | raw | blame | history
quartz/components/TableOfContents.tsx 2 ●●● patch | view | raw | blame | history
quartz/components/pages/FolderContent.tsx 1 ●●●● patch | view | raw | blame | history
quartz/components/pages/TagContent.tsx 1 ●●●● patch | view | raw | blame | history
quartz/components/renderPage.tsx 8 ●●●● patch | view | raw | blame | history
quartz/components/scripts/graph.inline.ts 13 ●●●●● patch | view | raw | blame | history
quartz/components/scripts/search.inline.ts 62 ●●●● patch | view | raw | blame | history
quartz/components/styles/darkmode.scss 2 ●●● patch | view | raw | blame | history
quartz/components/styles/footer.scss 1 ●●●● patch | view | raw | blame | history
quartz/components/styles/popover.scss 4 ●●●● patch | view | raw | blame | history
quartz/components/styles/search.scss 6 ●●●●● patch | view | raw | blame | history
quartz/components/styles/toc.scss 2 ●●● patch | view | raw | blame | history
quartz/components/types.ts 3 ●●●●● patch | view | raw | blame | history
quartz/styles/base.scss 150 ●●●●● patch | view | raw | blame | history
quartz/styles/variables.scss 3 ●●●● patch | view | raw | blame | history
quartz.config.ts
@@ -22,6 +22,7 @@
  ],
  left: [
    Component.PageTitle(),
    Component.MobileOnly(Component.Spacer()),
    Component.Search(),
    Component.Darkmode(),
    Component.DesktopOnly(Component.TableOfContents()),
@@ -38,6 +39,7 @@
  ],
  left: [
    Component.PageTitle(),
    Component.MobileOnly(Component.Spacer()),
    Component.Search(),
    Component.Darkmode()
  ],
@@ -63,8 +65,8 @@
      colors: {
        lightMode: {
          light: '#faf8f8',
          lightgray: '#e8e8e8',
          gray: '#dadada',
          lightgray: '#e5e5e5',
          gray: '#b8b8b8',
          darkgray: '#4e4e4e',
          dark: '#141021',
          secondary: '#284b63',
@@ -73,8 +75,8 @@
        },
        darkMode: {
          light: '#161618',
          lightgray: '#292629',
          gray: '#343434',
          lightgray: '#393639',
          gray: '#646464',
          darkgray: '#d4d4d4',
          dark: '#fbfffe',
          secondary: '#7b97aa',
quartz/components/Backlinks.tsx
@@ -8,7 +8,7 @@
  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>}
quartz/components/DesktopOnly.tsx
@@ -4,9 +4,7 @@
  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
quartz/components/Footer.tsx
@@ -11,15 +11,13 @@
    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>
    </>
  }
  Footer.css = style
quartz/components/Graph.tsx
@@ -50,7 +50,7 @@
    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"
quartz/components/MobileOnly.tsx
@@ -4,9 +4,7 @@
  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
quartz/components/ReadingTime.tsx
@@ -14,7 +14,7 @@
ReadingTime.css = `
.reading-time {
  margin-top: 0;
  opacity: 0.5;
  color: var(--gray);
}
`
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
quartz/components/TableOfContents.tsx
@@ -26,7 +26,7 @@
      </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>)}
quartz/components/pages/FolderContent.tsx
@@ -27,7 +27,6 @@
  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} /> 
quartz/components/pages/TagContent.tsx
@@ -21,7 +21,6 @@
    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} />
quartz/components/renderPage.tsx
@@ -55,6 +55,9 @@
    <Head {...componentData} />
    <body data-slug={slug}>
      <div id="quartz-root" class="page">
        <Body {...componentData}>
          {LeftComponent}
          <div class="center">
        <div class="page-header">
          <Header {...componentData} >
            {header.map(HeaderComponent => <HeaderComponent {...componentData} />)}
@@ -63,14 +66,11 @@
            {beforeBody.map(BodyComponent => <BodyComponent {...componentData} />)}
          </div>
        </div>
        <Body {...componentData}>
          {LeftComponent}
          <div class="center">
            <Content {...componentData} />
            <Footer {...componentData} />
          </div>
          {RightComponent}
        </Body>
        <Footer {...componentData} />
      </div>
    </body>
    {pageResources.js.filter(resource => resource.loadTime === "afterDOMReady").map(res => JSResourceToScriptElement(res))}
quartz/components/scripts/graph.inline.ts
@@ -225,7 +225,7 @@
  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 @@
  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)
})
quartz/components/scripts/search.inline.ts
@@ -58,38 +58,7 @@
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
@@ -177,6 +146,37 @@
  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)
})
quartz/components/styles/darkmode.scss
@@ -2,7 +2,7 @@
  position: relative;
  width: 20px;
  height: 20px;
  margin: 1rem;
  margin: 0 10px;
  & > .toggle {
    display: none;
quartz/components/styles/footer.scss
@@ -1,6 +1,5 @@
footer {
  text-align: left;
  opacity: 0.8;
  margin-bottom: 4rem;
  & ul {
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 {
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%;
      }
quartz/components/styles/toc.scss
@@ -29,7 +29,7 @@
  list-style: none;
  overflow: hidden;
  max-height: none;
  transition: max-height 0.3s ease;
  transition: max-height 0.5s ease;
  & ul {
    list-style: none;
quartz/components/types.ts
@@ -11,6 +11,9 @@
  children: (QuartzComponent | JSX.Element)[]
  tree: Node<QuartzPluginData>
  allFiles: QuartzPluginData[]
  displayClass?: 'mobile-only' | 'desktop-only'
} & JSX.IntrinsicAttributes & {
  [key: string]: any
}
export type QuartzComponent = ComponentType<QuartzComponentProps> & {
quartz/styles/base.scss
@@ -43,71 +43,23 @@
  }
}
.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 @@
      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 @@
  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 @@
.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));
  }
}
quartz/styles/variables.scss
@@ -1,5 +1,6 @@
$pageWidth: 800px;
$pageWidth: 750px;
$mobileBreakpoint: 600px;
$tabletBreakpoint: 1200px;
$sidePanelWidth: 400px;
$topSpacing: 6rem;
$fullPageWidth: $pageWidth + 2 * $sidePanelWidth