Quartz customisation

Quartz (GitHub) is the tool used to create this website.
I have done a number of modifications to different components of Quartz, in order to better suit my use case and my preferences. The repo history was a huge mess and it made it very hard to track all the changes, so I’ve decided to add these code snippets here.
In addition to this, I ended up recreating a new git repo for my Quartz page, allowing me to properly document all the changes to the tool, and the content, separately, using the git commit messages to document the changes.

Full code

The full code for this Quartz page is available in the GitHub repo.


Snippets

Added folder emoji and removed dates from folders listed on folder pages

16-04-2025 ▪ In the pages listing the content of folders, subfolders where displayed with a date and look that made them look like actual notes. To make them more distinct, I used css to make the difference a bit more obvious.
This was kindly provided by saberzero1 on Quartz’s Discord.

The below code was added to custom.scss.

// hide date for subfolders on folderpage
.page-listing {
    li.section-li:has(.section .desc a.internal[href$="/"]) {
        .meta {
            // hides date, keep indent
            visibility: hidden;
        }
    }
}
 
// indicate folder more clearly on folderpage
.page-listing {
    li.section-li:has(.section .desc a.internal[href$="/"]) {
        .desc a.internal {
            // prepend folder emoji
            &:before {
                content: "📁";
            }
        }
    }
}

Added better spacing to explorer menu items to improve readability for long titles

02-02-2025 ▪ Some notes with longer titles were difficult to read in the explorer menu, as the spacing between titles and the lines was the same. I increased the spacing between entries. Taken and modified from this PR.

This was added to custom.scss

// Adding some spacing between entries in the explorer
div.explorer {
    .overflow {
        li {
            li {
                margin-bottom: 0.3rem;
            }
            &:last-child {
                margin-bottom: 0;
            }
        }
    }
}

Display both modified and created date

11-01-2025 ▪ Each post/note now displays the last update date in addition to the created date, whereas the original code only allowed me to display one of them.

/quartz/components/ContentMeta.tsx

@@ -29,8 +29,10 @@ export default ((opts?: Partial<ContentMetaOptions>) => {
     if (text) {
       const segments: (string | JSX.Element)[] = []
 
+      // Display modified and created date
       if (fileData.dates) {
-        segments.push(<Date date={getDate(cfg, fileData)!} locale={cfg.locale} />)
+        segments.push("Last Update: " + fileData.dates.modified.toDateString() + " ▪ ")
+        segments.push("Created: " + fileData.dates.created.toDateString() + " ▪ ")
       }

Display alt-text as caption for images

08-01-2025 ▪ Modified the Obsidian flavoured markdown component to automatically add a caption to any image, using the content of the alt-text.

ofm.ts

@@ -235,23 +235,20 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>>
 
                 // embed cases
                 if (value.startsWith("!")) {
-                  const ext: string = path.extname(fp).toLowerCase()
-                  const url = slugifyFilePath(fp as FilePath)
+                  const baseExt = path.extname(fp).toLowerCase();
+                  const ext = baseExt.split("?")[0]; // Remove query string from the extension
+                  const url = slugifyFilePath(fp as FilePath);
                   if ([".png", ".jpg", ".jpeg", ".gif", ".bmp", ".svg", ".webp"].includes(ext)) {
                     const match = wikilinkImageEmbedRegex.exec(alias ?? "")
                     const alt = match?.groups?.alt ?? ""
                     const width = match?.groups?.width ?? "auto"
                     const height = match?.groups?.height ?? "auto"
-                    return {
-                      type: "image",
-                      url,
-                      data: {
-                        hProperties: {
-                          width,
-                          height,
-                          alt,
-                        },
-                      },
+                  return {
+                    type: "html",
+                    value: `<figure>
+                              <img src="${url}" alt="${alt}" width="${width}" height="${height}">
+                              <figcaption>${alt}</figcaption>
+                            </figure>`,
                     }
                   } else if ([".mp4", ".webm", ".ogv", ".mov", ".mkv"].includes(ext)) {
                     return {
 

Make content use up more of the screen for wide-screens

06-01-2025 ▪ While the design of the page is responsive by default, I felt that it didn’t use enough of the screen when using large screens (ie, wide-screen and 4K monitors). Made a few changes to make sure it uses more of the screen, where possible.

variables.scss

@@ -46,7 +46,7 @@ $tabletGrid: (
 );
 $desktopGrid: (
   templateRows: "auto auto auto",
-  templateColumns: "#{$sidePanelWidth} auto #{$sidePanelWidth}",
+  templateColumns: "#{$sidePanelWidth} auto 380",
   rowGap: "5px",
   columnGap: "5px",
   templateAreas:
 

base.scss

@@ -121,7 +121,7 @@ a {
 }
 
 .page {
-  max-width: calc(#{map-get($breakpoints, desktop)} + 300px);
+  max-width: calc(#{map-get($breakpoints, desktop)} + 750px);
   margin: 0 auto;
   & article {
     & > h1 {
 

20-12-2024 ▪ Modified the Explorer component in order to show the root/index as “Home” on the navigation bar and link to Home

quartz/components/Explorer.tsx

           aria-controls="explorer-content"
           aria-expanded={opts.folderDefaultState === "open"}
         >
-          <h2>{opts.title ?? i18n(cfg.locale).components.explorer.title}</h2>
+         <a href="/"> <h2>{opts.title ?? i18n(cfg.locale).components.explorer.title}</h2></a>
           <svg
             xmlns="http://www.w3.org/2000/svg"
             width="14"
 

Default to dark-mode

20-12-2024 ▪ Changed the default to dark-mode, keeping the option to switch between light/dark.

quartz/components/scripts/darkmode.inline.ts

@@ -1,5 +1,5 @@
 const userPref = window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark"
-const currentTheme = localStorage.getItem("theme") ?? userPref
+const currentTheme = localStorage.getItem("theme") ?? "dark"
 document.documentElement.setAttribute("saved-theme", currentTheme)
 
 const emitThemeChangeEvent = (theme: "light" | "dark") => {

20-12-2024 ▪ Removed the link to Quartz (it’s mentioned and linked to in About this site).
Added title, CC-BY license, link and logos.

quartz/components/Footer.tsx

@@ -14,9 +14,27 @@ export default ((opts?: Options) => {
     return (
       <footer class={`${displayClass ?? ""}`}>
         <p>
-          {i18n(cfg.locale).components.footer.createdWith}{" "}
-          <a href="https://quartz.jzhao.xyz/">Quartz v{version}</a> © {year}
-        </p>
+          Dan's Garden © {year}, licensed under <a href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a> <img
+          src="/static/cc.svg"
+          alt="Creative Commons CC Icon"
+          width="20"
+          height="20"
+          style={{
+            verticalAlign: "middle", // Aligns with text baseline
+            display: "inline-block",     // Keeps the icon inline
+          }}
+          />
+          <img
+          src="/static/by.svg"
+          alt="Creative Commons BY Icon"
+          width="20"
+          height="20"
+          style={{
+            verticalAlign: "middle", // Aligns with text baseline
+            display: "inline-block",     // Keeps the icon inline
+          }}
+          />
+         </p>
         <ul>
           {Object.entries(links).map(([text, link]) => (
             <li>
 

Added Reply by Email Component

30-12-2024 ▪ Added a “Reply by Email” button under each note, inspired by Kev Quirk’s blog.
06-05-2025 ▪ Updated the “Reply by Email”, using base64 encoding for the email, and adding the ability to specify it via the Quartz layout file. 13-05-2025 ▪ Further updated the component with the ability to specific include and exclude lists, plus the text label for the button. This has been submitted as a PR to Quartz, the code can be seen here.


20-12-2024 ▪ Added links to About, Contact and RSS.

/rss is a redirect from /index.xmldone via Cloudflare.

quartz.layout.ts

@@ -8,8 +8,9 @@ export const sharedPageComponents: SharedLayout = {
   afterBody: [],
   footer: Component.Footer({
     links: {
-      GitHub: "https://github.com/jackyzha0/quartz",
-      "Discord Community": "https://discord.gg/cRFFHYye7t",
+      "About": "https://dansgarden.eu/about",
+      "Contact": "https://dansgarden.eu/contact",
+      "RSS": "https://dansgarden.eu/rss",
     },
   }),
 }

Misc custom css adjustments

▪ All the different items are commented within the code.

custom.scss

@use "./base.scss";
 
// put your custom CSS here!
// Adding Inter with ss02
@font-face {
    font-family: Inter;
    font-style: normal;
    font-weight: 100 400 900;
    font-display: swap;
    font-feature-settings: 'liga' 1, 'calt' 1, 'ss02' 1; /* fix for Chrome */
    src: url("/static/fonts/InterVariable.woff2") format("woff2");
}
@font-face {
    font-family: Inter;
    font-style: italic;
    font-weight: 100 400 900;
    font-display: swap;
    font-feature-settings: 'liga' 1, 'calt' 1, 'ss02' 1; /* fix for Chrome */
    src: url("/static/fonts/InterVariable-Italic.woff2") format("woff2");
}
 
//  Change style for footnotes
section[data-footnotes] {
    & > #footnote-label.sr-only {
        font-size: 1.25rem;
        color: var(--dark);
        margin-bottom: -0.75rem;
    }
 
    & > ol {
        margin-bottom: 2rem;
    }
}
 
 
// hide Date if frontmatter contains cssclasses hideDate
body:has(.page-header ~ article.hideDate) .content-meta {
    display: none;
}
 
// hide Tags if frontmatter contains cssclasses hideTags
body:has(.page-header ~ article.hideTags) .tags {
    display: none;
}
 
// Change style for external links
a {
    &.external {
        text-decoration: underline;
        text-decoration-color: var(--secondary);
        color: var(--darkgray);
        background-color: var(--light);
    }
}
 
// typography improvements
h1 {
    font-size: 1.75rem;
    margin-top: 2.25rem;
    margin-bottom: 1rem;
    text-align: center;
}
 
h2 {
    font-size: 1.4rem;
    margin-top: 1.9rem;
    margin-bottom: 1rem;
    color: var(--secondary)
 
}
 
h3 {
    font-size: 1.12rem;
    margin-top: 1.62rem;
    margin-bottom: 1rem;
}
 
h4 {
    font-size: 1rem;
    margin-top: 1.5rem;
    margin-bottom: -0.5rem;
    color: var(--tertiary)
}
 
h5 {
    text-indent: 2em;
    font-size: 1rem;
    margin-top: 1.5rem;
    margin-bottom: -0.9rem;
}
 
h6 {
    font-size: 1rem;
    margin-top: 1.5rem;
    margin-bottom: -0.9rem;
}
 
// Apply justification to only paragraphs or text elements if needed
p {
    text-align: justify;
    text-justify: inter-word;
    hyphens: auto;
}
 
// center fig caption
figure:has(img) {
    margin-top: -1rem;
    text-align: center;
}
 
figcaption {
    text-align: center;
    margin-top: -1rem;
    font-style: italic;
    font-size: 0.9rem;
}
 
// Adding some spacing between entries in the explorer
div.explorer {
    .overflow {
        li {
            li {
                margin-bottom: 0.3rem;
            }
            &:last-child {
                margin-bottom: 0;
            }
        }
    }
}
 
// Changing the colour of the Home page button link
.explorer .title-button.explorer-toggle.desktop-explorer a h2 {
    color: var(--dark);
}
 
// hide date for subfolders on folderpage
.page-listing {
    li.section-li:has(.section .desc a.internal[href$="/"]) {
        .meta {
            // hides date, keep indent
            visibility: hidden;
        }
    }
}
 
// indicate folder more clearly on folderpage
.page-listing {
    li.section-li:has(.section .desc a.internal[href$="/"]) {
        .desc a.internal {
            // prepend folder emoji
            &:before {
                content: "📁";
            }
        }
    }
}
 
// re-work space on folderpage to allow more tags inline on desktop
@media all and (min-width: 1024px) {
    li.section-li > .section {
        grid-template-columns: fit-content(8em) 3fr 2fr;
    }
}
 
// reduce font size on recent notes title
.recent-ul .recent-li h3 {
    font-size: 0.9em;
}
 
// reduce font size on recent notes dates
.recent-ul .recent-li .meta {
    font-size: 0.9em;
}
 
// add a code callout
.callout[data-callout^="code"] {
    --callout-icon: var(--callout-icon-example);
}