Emile Bangma
2024-09-23 7d7e3349763b0c1ade94b8a6d13986e171861bc1
feat: responsive design grid (#1354)

* Responsive design grid

* Addressed PR feedback

* Bump Quartz version 4.3.1 => 4.4.0

* Moved page-header into center

* Updated docs with new layouts

* Sync updated version number with package-lock

* Table of Content scrollbar auto

* Reset node_modules

* Updated layout images

* Fixed tablet layout

* Finilazed layout images
1 files deleted
3 files added
13 files modified
339 ■■■■ changed files
docs/images/quartz layout.png patch | view | raw | blame | history
docs/images/quartz-layout-desktop.png patch | view | raw | blame | history
docs/images/quartz-layout-mobile.png patch | view | raw | blame | history
docs/images/quartz-layout-tablet.png patch | view | raw | blame | history
docs/layout.md 27 ●●●● patch | view | raw | blame | history
package-lock.json 4 ●●●● patch | view | raw | blame | history
package.json 2 ●●● patch | view | raw | blame | history
quartz/components/renderPage.tsx 2 ●●● patch | view | raw | blame | history
quartz/components/styles/backlinks.scss 55 ●●●● patch | view | raw | blame | history
quartz/components/styles/explorer.scss 25 ●●●● patch | view | raw | blame | history
quartz/components/styles/graph.scss 2 ●●● patch | view | raw | blame | history
quartz/components/styles/listPage.scss 2 ●●● patch | view | raw | blame | history
quartz/components/styles/popover.scss 2 ●●● patch | view | raw | blame | history
quartz/components/styles/search.scss 10 ●●●●● patch | view | raw | blame | history
quartz/components/styles/toc.scss 4 ●●●● patch | view | raw | blame | history
quartz/styles/base.scss 147 ●●●●● patch | view | raw | blame | history
quartz/styles/variables.scss 57 ●●●●● patch | view | raw | blame | history
docs/images/quartz layout.png
Binary files differ
docs/images/quartz-layout-desktop.png
docs/images/quartz-layout-mobile.png
docs/images/quartz-layout-tablet.png
docs/layout.md
@@ -13,15 +13,19 @@
  beforeBody: QuartzComponent[] // laid out vertically
  pageBody: QuartzComponent // single component
  afterBody: QuartzComponent[] // laid out vertically
  left: QuartzComponent[] // vertical on desktop, horizontal on mobile
  right: QuartzComponent[] // vertical on desktop, horizontal on mobile
  left: QuartzComponent[] // vertical on desktop and tablet, horizontal on mobile
  right: QuartzComponent[] // vertical on desktop, horizontal on tablet and mobile
  footer: QuartzComponent // single component
}
```
These correspond to following parts of the page:
![[quartz layout.png|800]]
| Layout                          | Preview                             |
| ------------------------------- | ----------------------------------- |
| Desktop (width > 1200px)        | ![[quartz-layout-desktop.png\|800]] |
| Tablet (800px < width < 1200px) | ![[quartz-layout-tablet.png\|800]]  |
| Mobile (width < 800px)          | ![[quartz-layout-mobile.png\|800]]  |
> [!note]
> There are two additional layout fields that are _not_ shown in the above diagram.
@@ -33,6 +37,23 @@
See [a list of all the components](component.md) for all available components along with their configuration options. You can also checkout the guide on [[creating components]] if you're interested in further customizing the behaviour of Quartz.
### Layout breakpoints
Quartz has different layouts depending on the width the screen viewing the website.
The breakpoints for layouts can be configured in `variables.scss`.
- `mobile`: screen width below this size will use mobile layout.
- `desktop`: screen width above this size will use desktop layout.
- Screen width between `mobile` and `desktop` width will use the tablet layout.
```scss
$breakpoints: (
  mobile: 800px,
  desktop: 1200px,
);
```
### Style
Most meaningful style changes like colour scheme and font can be done simply through the [[configuration#General Configuration|general configuration]] options. However, if you'd like to make more involved style changes, you can do this by writing your own styles. Quartz 4, like Quartz 3, uses [Sass](https://sass-lang.com/guide/) for styling.
package-lock.json
@@ -1,12 +1,12 @@
{
  "name": "@jackyzha0/quartz",
  "version": "4.3.1",
  "version": "4.4.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "@jackyzha0/quartz",
      "version": "4.3.1",
      "version": "4.4.0",
      "license": "MIT",
      "dependencies": {
        "@clack/prompts": "^0.7.0",
package.json
@@ -2,7 +2,7 @@
  "name": "@jackyzha0/quartz",
  "description": "🌱 publish your digital garden and notes as a website",
  "private": true,
  "version": "4.3.1",
  "version": "4.4.0",
  "type": "module",
  "author": "jackyzha0 <j.zhao2k19@gmail.com>",
  "license": "MIT",
quartz/components/renderPage.tsx
@@ -242,8 +242,8 @@
              </div>
            </div>
            {RightComponent}
            <Footer {...componentData} />
          </Body>
          <Footer {...componentData} />
        </div>
      </body>
      {pageResources.js
quartz/components/styles/backlinks.scss
@@ -1,19 +1,50 @@
@use "../../styles/variables.scss" as *;
.backlinks {
  position: relative;
  @media all and not ($desktop) {
    overflow-y: auto;
    display: initial;
    &:after {
      pointer-events: none;
      content: "";
      width: 100%;
      height: 50px;
      position: absolute;
      left: 0;
      bottom: 0;
      opacity: 1;
      transition: opacity 0.3s ease;
      background: linear-gradient(transparent 0px, var(--light));
    }
  & > h3 {
    font-size: 1rem;
    margin: 0;
  }
    &:has(> .overflow) {
      position: unset;
    }
  & > ul {
    list-style: none;
    padding: 0;
    margin: 0.5rem 0;
    & > h3 {
      font-size: 1rem;
      margin: 0;
    }
    & > li {
      & > a {
        background-color: transparent;
    & > ul {
      list-style: none;
      padding: 0;
      margin: 0.5rem 0;
      & > li {
        & > a {
          background-color: transparent;
        }
      }
    }
    & > .overflow {
      max-height: unset;
      & > li:last-of-type {
        margin-bottom: 0;
      }
      &:after {
        display: none;
      }
    }
  }
quartz/components/styles/explorer.scss
@@ -1,5 +1,23 @@
@use "../../styles/variables.scss" as *;
.explorer {
  &.desktop-only {
    overflow-y: auto;
  }
  &:after {
    pointer-events: none;
    content: "";
    width: 100%;
    height: 50px;
    position: absolute;
    left: 0;
    bottom: 0;
    opacity: 1;
    transition: opacity 0.3s ease;
    background: linear-gradient(transparent 0px, var(--light));
  }
}
button#explorer {
  background-color: transparent;
  border: none;
@@ -58,10 +76,6 @@
    visibility: hidden;
  }
  &.collapsed > .overflow::after {
    opacity: 0;
  }
  & ul {
    list-style: none;
    margin: 0.08rem 0;
@@ -76,6 +90,9 @@
      pointer-events: all;
    }
  }
  > #explorer-ul {
    max-height: none;
  }
}
svg {
quartz/components/styles/graph.scss
@@ -65,7 +65,7 @@
      height: 80vh;
      width: 80vw;
      @media all and (max-width: $fullPageWidth) {
      @media all and ($desktop) {
        width: 90%;
      }
    }
quartz/components/styles/listPage.scss
@@ -13,7 +13,7 @@
    display: grid;
    grid-template-columns: fit-content(8em) 3fr 1fr;
    @media all and (max-width: $mobileBreakpoint) {
    @media all and ($mobile) {
      & > .tags {
        display: none;
      }
quartz/components/styles/popover.scss
@@ -70,7 +70,7 @@
    opacity 0.3s ease,
    visibility 0.3s ease;
  @media all and (max-width: $mobileBreakpoint) {
  @media all and ($mobile) {
    display: none !important;
  }
}
quartz/components/styles/search.scss
@@ -3,7 +3,9 @@
.search {
  min-width: fit-content;
  max-width: 14rem;
  flex-grow: 0.3;
  @media all and ($mobile) {
    flex-grow: 0.3;
  }
  & > .search-button {
    background-color: var(--lightgray);
@@ -62,7 +64,7 @@
      margin-left: auto;
      margin-right: auto;
      @media all and (max-width: $fullPageWidth) {
      @media all and ($desktop) {
        width: 90%;
      }
@@ -104,7 +106,7 @@
          flex: 0 0 min(30%, 450px);
        }
        @media all and (min-width: $tabletBreakpoint) {
        @media all and not ($tablet) {
          &[data-preview] {
            & .result-card > p.preview {
              display: none;
@@ -130,7 +132,7 @@
          border-radius: 5px;
        }
        @media all and (max-width: $tabletBreakpoint) {
        @media all and ($tablet) {
          & > #preview-container {
            display: none !important;
          }
quartz/components/styles/toc.scss
@@ -1,3 +1,7 @@
.toc {
  overflow-y: auto;
}
button#toc {
  background-color: transparent;
  border: none;
quartz/styles/base.scss
@@ -12,7 +12,6 @@
body,
section {
  margin: 0;
  max-width: 100%;
  box-sizing: border-box;
  background-color: var(--light);
  font-family: var(--bodyFont);
@@ -109,25 +108,21 @@
.desktop-only {
  display: initial;
  @media all and (max-width: $fullPageWidth) {
  @media all and ($mobile) {
    display: none;
  }
}
.mobile-only {
  display: none;
  @media all and (max-width: $fullPageWidth) {
  @media all and ($mobile) {
    display: initial;
  }
}
.page {
  @media all and (max-width: $fullPageWidth) {
    margin: 0 auto;
    padding: 0 1rem;
    max-width: $pageWidth;
  }
  max-width: calc(#{map-get($breakpoints, desktop)} + 300px);
  margin: 0 auto;
  & article {
    & > h1 {
      font-size: 2rem;
@@ -155,79 +150,117 @@
  }
  & > #quartz-body {
    width: 100%;
    display: flex;
    @media all and (max-width: $fullPageWidth) {
      flex-direction: column;
    display: grid;
    grid-template-columns: #{map-get($desktopGrid, templateColumns)};
    grid-template-rows: #{map-get($desktopGrid, templateRows)};
    column-gap: #{map-get($desktopGrid, columnGap)};
    row-gap: #{map-get($desktopGrid, rowGap)};
    grid-template-areas: #{map-get($desktopGrid, templateAreas)};
    @media all and ($desktop) {
      grid-template-columns: #{map-get($tabletGrid, templateColumns)};
      grid-template-rows: #{map-get($tabletGrid, templateRows)};
      column-gap: #{map-get($tabletGrid, columnGap)};
      row-gap: #{map-get($tabletGrid, rowGap)};
      grid-template-areas: #{map-get($tabletGrid, templateAreas)};
    }
    @media all and ($mobile) {
      grid-template-columns: #{map-get($mobileGrid, templateColumns)};
      grid-template-rows: #{map-get($mobileGrid, templateRows)};
      column-gap: #{map-get($mobileGrid, columnGap)};
      row-gap: #{map-get($mobileGrid, rowGap)};
      grid-template-areas: #{map-get($mobileGrid, templateAreas)};
    }
    @media all and ($desktop) {
      padding: 0 1rem;
    }
    @media all and ($mobile) {
      margin: 0 auto;
    }
    & .sidebar {
      flex: 1;
      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: 2rem;
      }
      padding: $topSpacing 2rem 2rem 2rem;
      display: flex;
      height: 100vh;
      position: sticky;
    }
    & .sidebar.left {
      z-index: 1;
      left: calc(calc(100vw - $pageWidth) / 2 - $sidePanelWidth);
      @media all and (max-width: $fullPageWidth) {
      grid-area: sidebar-left;
      flex-direction: column;
      @media all and ($mobile) {
        gap: 0;
        align-items: center;
        position: initial;
        display: flex;
        height: unset;
        flex-direction: row;
        padding: 0;
        padding-top: 2rem;
      }
    }
    & .sidebar.right {
      right: calc(calc(100vw - $pageWidth) / 2 - $sidePanelWidth);
      flex-wrap: wrap;
      & > * {
        @media all and (max-width: $fullPageWidth) {
      grid-area: sidebar-right;
      margin-right: 0;
      flex-direction: column;
      @media all and ($mobile) {
        margin-left: inherit;
        margin-right: inherit;
      }
      @media all and ($desktop) {
        position: initial;
        height: unset;
        width: 100%;
        flex-direction: row;
        padding: 0;
        & > * {
          flex: 1;
          min-width: 140px;
        }
        & > .toc {
          display: none;
        }
      }
    }
  }
  & .page-header,
  & .page-footer {
    width: $pageWidth;
    margin-top: 1rem;
    @media all and (max-width: $fullPageWidth) {
      width: initial;
    & .page-header,
    & .page-footer {
      margin-top: 1rem;
    }
  }
  & .page-header {
    margin: $topSpacing auto 0 auto;
    @media all and (max-width: $fullPageWidth) {
      margin-top: 2rem;
    & .page-header {
      grid-area: page-header;
      margin: $topSpacing 0 0 0;
      @media all and ($mobile) {
        margin-top: 0;
        padding: 0;
      }
    }
  }
  & .center,
  & footer {
    margin-left: auto;
    margin-right: auto;
    width: $pageWidth;
    @media all and (max-width: $fullPageWidth) {
      width: initial;
    & .center > article {
      grid-area: page-center;
    }
    & .page-footer {
      grid-area: page-footer;
    }
    & .center,
    & footer {
      margin-left: auto;
      margin-right: auto;
      @media all and ($desktop) {
        margin-right: 0;
      }
      @media all and ($mobile) {
        margin-left: 0;
      }
    }
    & footer {
      margin-left: 0;
      margin-right: 0;
    }
  }
}
quartz/styles/variables.scss
@@ -1,9 +1,56 @@
$pageWidth: 750px;
$mobileBreakpoint: 600px;
$tabletBreakpoint: 1000px;
$sidePanelWidth: 380px;
/**
 * Layout breakpoints
 * $mobile: screen width below this value will use mobile styles
 * $desktop: screen width above this value will use desktop styles
 * Screen width between $mobile and $desktop width will use the tablet layout.
 * assuming mobile < desktop
 */
$breakpoints: (
  mobile: 800px,
  desktop: 1200px,
);
$mobile: "(max-width: #{map-get($breakpoints, mobile)})";
$tablet: "(min-width: #{map-get($breakpoints, mobile)}) and (max-width: #{map-get($breakpoints, desktop)})";
$desktop: "(max-width: #{map-get($breakpoints, desktop)})";
$pageWidth: #{map-get($breakpoints, mobile)};
$sidePanelWidth: 320px; //380px;
$topSpacing: 6rem;
$fullPageWidth: $pageWidth + 2 * $sidePanelWidth;
$boldWeight: 700;
$semiBoldWeight: 600;
$normalWeight: 400;
$mobileGrid: (
  templateRows: "auto auto auto auto auto",
  templateColumns: "auto",
  rowGap: "5px",
  columnGap: "5px",
  templateAreas:
    '"sidebar-left"\
      "page-header"\
      "page-center"\
      "sidebar-right"\
      "page-footer"',
);
$tabletGrid: (
  templateRows: "auto auto auto auto",
  templateColumns: "#{$sidePanelWidth} auto",
  rowGap: "5px",
  columnGap: "5px",
  templateAreas:
    '"sidebar-left page-header"\
      "sidebar-left page-center"\
      "sidebar-left sidebar-right"\
      "sidebar-left page-footer"',
);
$desktopGrid: (
  templateRows: "auto auto auto",
  templateColumns: "#{$sidePanelWidth} auto #{$sidePanelWidth}",
  rowGap: "5px",
  columnGap: "5px",
  templateAreas:
    '"sidebar-left page-header sidebar-right"\
      "sidebar-left page-center sidebar-right"\
      "sidebar-left page-footer sidebar-right"',
);