From 2a6b9a9ea01e8e6a80fe51d01dc865595b67a612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20Schr=C3=B6der?= <33390735+lnschroeder@users.noreply.github.com> Date: Tue, 7 Nov 2023 18:16:48 +0100 Subject: [PATCH 01/16] docs: fix property name for ToC toggle (#573) --- docs/features/table of contents.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/table of contents.md b/docs/features/table of contents.md index f169b22d2..0298ffaab 100644 --- a/docs/features/table of contents.md +++ b/docs/features/table of contents.md @@ -8,7 +8,7 @@ tags: Quartz can automatically generate a table of contents from a list of headings on each page. It will also show you your current scroll position on the site by marking headings you've scrolled through with a different colour. By default, it will show all headers from H1 (`# Title`) all the way to H3 (`### Title`) and will only show the table of contents if there is more than 1 header on the page. -You can also hide the table of contents on a page by adding `showToc: false` to the frontmatter for that page. +You can also hide the table of contents on a page by adding `enableToc: false` to the frontmatter for that page. > [!info] > This feature requires both `Plugin.TableOfContents` in your `quartz.config.ts` and `Component.TableOfContents` in your `quartz.layout.ts` to function correctly. From e9f4e28a2d3e0b529586ddb5a50680a6e66412f3 Mon Sep 17 00:00:00 2001 From: Yes365 Date: Fri, 10 Nov 2023 11:44:16 +0800 Subject: [PATCH 02/16] fix: adapt vercel cleanurls (#487) Co-authored-by: Harrison --- quartz/components/pages/TagContent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quartz/components/pages/TagContent.tsx b/quartz/components/pages/TagContent.tsx index 205ba8958..4a068c6bc 100644 --- a/quartz/components/pages/TagContent.tsx +++ b/quartz/components/pages/TagContent.tsx @@ -53,7 +53,7 @@ function TagContent(props: QuartzComponentProps) { return (

- + #{tag}

From 99e8f5944fdd83110fbac4c9edc37cdcaf2c25f7 Mon Sep 17 00:00:00 2001 From: Jacky Zhao Date: Sat, 11 Nov 2023 09:56:30 -0800 Subject: [PATCH 03/16] fix: trailing slash aliases (closes #577) --- quartz/plugins/emitters/aliases.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/quartz/plugins/emitters/aliases.ts b/quartz/plugins/emitters/aliases.ts index 942412e9d..210715eb4 100644 --- a/quartz/plugins/emitters/aliases.ts +++ b/quartz/plugins/emitters/aliases.ts @@ -1,4 +1,4 @@ -import { FilePath, FullSlug, resolveRelative, simplifySlug } from "../../util/path" +import { FilePath, FullSlug, joinSegments, resolveRelative, simplifySlug } from "../../util/path" import { QuartzEmitterPlugin } from "../types" import path from "path" @@ -25,7 +25,12 @@ export const AliasRedirects: QuartzEmitterPlugin = () => ({ slugs.push(permalink as FullSlug) } - for (const slug of slugs) { + for (let slug of slugs) { + // fix any slugs that have trailing slash + if (slug.endsWith("/")) { + slug = joinSegments(slug, "index") as FullSlug + } + const redirUrl = resolveRelative(slug, file.data.slug!) const fp = await emit({ content: ` From 134b6ed582d6ce9d8fb8f1e58d2bae89c07c2d5d Mon Sep 17 00:00:00 2001 From: Jacky Zhao Date: Sat, 11 Nov 2023 10:11:31 -0800 Subject: [PATCH 04/16] fix: anchors links shouldnt cause reload (closes #574) --- docs/features/callouts.md | 2 +- quartz/components/scripts/spa.inline.ts | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/features/callouts.md b/docs/features/callouts.md index 63051ad9d..27de687eb 100644 --- a/docs/features/callouts.md +++ b/docs/features/callouts.md @@ -33,7 +33,7 @@ See [documentation on supported types and syntax here](https://help.obsidian.md > [!question]+ Can callouts be nested? > -> > [!todo]- Yes!, they can. +> > [!todo]- Yes!, they can. And collapsed! > > > > > [!example] You can even use multiple layers of nesting. diff --git a/quartz/components/scripts/spa.inline.ts b/quartz/components/scripts/spa.inline.ts index 31ae14fcb..115bc968b 100644 --- a/quartz/components/scripts/spa.inline.ts +++ b/quartz/components/scripts/spa.inline.ts @@ -18,6 +18,12 @@ const isLocalUrl = (href: string) => { return false } +const isSamePage = (url: URL): boolean => { + const sameOrigin = url.origin === window.location.origin + const samePath = url.pathname === window.location.pathname + return sameOrigin && samePath +} + const getOpts = ({ target }: Event): { url: URL; scroll?: boolean } | undefined => { if (!isElement(target)) return if (target.attributes.getNamedItem("target")?.value === "_blank") return @@ -93,8 +99,16 @@ function createRouter() { if (typeof window !== "undefined") { window.addEventListener("click", async (event) => { const { url } = getOpts(event) ?? {} + // dont hijack behaviour, just let browser act normally if (!url || event.ctrlKey || event.metaKey) return event.preventDefault() + + if (isSamePage(url) && url.hash) { + const el = document.getElementById(decodeURIComponent(url.hash.substring(1))) + el?.scrollIntoView() + return + } + try { navigate(url, false) } catch (e) { From 50a87d0d8673dbce6ebafef83f71f197df9bc196 Mon Sep 17 00:00:00 2001 From: Jacky Zhao Date: Sat, 11 Nov 2023 11:39:56 -0800 Subject: [PATCH 05/16] style: scrollable tables --- quartz/styles/base.scss | 26 ++++++++++++++++++-------- quartz/util/{jsx.ts => jsx.tsx} | 18 ++++++++++++++++-- 2 files changed, 34 insertions(+), 10 deletions(-) rename quartz/util/{jsx.ts => jsx.tsx} (53%) diff --git a/quartz/styles/base.scss b/quartz/styles/base.scss index 51694cb4e..c72965659 100644 --- a/quartz/styles/base.scss +++ b/quartz/styles/base.scss @@ -390,23 +390,33 @@ p { line-height: 1.6rem; } -table { - margin: 1rem; - padding: 1.5rem; - border-collapse: collapse; - & > * { - line-height: 2rem; +.table-container { + overflow-x: auto; + + & > table { + margin: 1rem; + padding: 1.5rem; + border-collapse: collapse; + + th, + td { + min-width: 75px; + } + + & > * { + line-height: 2rem; + } } } th { text-align: left; - padding: 0.4rem 1rem; + padding: 0.4rem 0.7rem; border-bottom: 2px solid var(--gray); } td { - padding: 0.2rem 1rem; + padding: 0.2rem 0.7rem; } tr { diff --git a/quartz/util/jsx.ts b/quartz/util/jsx.tsx similarity index 53% rename from quartz/util/jsx.ts rename to quartz/util/jsx.tsx index 8cba485ab..e813240c6 100644 --- a/quartz/util/jsx.ts +++ b/quartz/util/jsx.tsx @@ -1,14 +1,28 @@ -import { toJsxRuntime } from "hast-util-to-jsx-runtime" +import { Components, toJsxRuntime } from "hast-util-to-jsx-runtime" import { QuartzPluginData } from "../plugins/vfile" import { Node, Root } from "hast" import { Fragment, jsx, jsxs } from "preact/jsx-runtime" import { trace } from "./trace" import { type FilePath } from "./path" +const customComponents: Components = { + table: (props) => ( +
+ + + ), +} + export function htmlToJsx(fp: FilePath, tree: Node) { try { // @ts-ignore (preact makes it angry) - return toJsxRuntime(tree as Root, { Fragment, jsx, jsxs, elementAttributeNameCase: "html" }) + return toJsxRuntime(tree as Root, { + Fragment, + jsx, + jsxs, + elementAttributeNameCase: "html", + components: customComponents, + }) } catch (e) { trace(`Failed to parse Markdown in \`${fp}\` into JSX`, e as Error) } From 22b7cf135e8b031550553947c48aa0dc00b3abbd Mon Sep 17 00:00:00 2001 From: Jacky Zhao Date: Sat, 11 Nov 2023 11:41:44 -0800 Subject: [PATCH 06/16] types: cast in jsx.tsx to avoid @ts-ignore --- quartz/util/jsx.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/quartz/util/jsx.tsx b/quartz/util/jsx.tsx index e813240c6..fc988a034 100644 --- a/quartz/util/jsx.tsx +++ b/quartz/util/jsx.tsx @@ -1,4 +1,4 @@ -import { Components, toJsxRuntime } from "hast-util-to-jsx-runtime" +import { Components, Jsx, toJsxRuntime } from "hast-util-to-jsx-runtime" import { QuartzPluginData } from "../plugins/vfile" import { Node, Root } from "hast" import { Fragment, jsx, jsxs } from "preact/jsx-runtime" @@ -15,11 +15,10 @@ const customComponents: Components = { export function htmlToJsx(fp: FilePath, tree: Node) { try { - // @ts-ignore (preact makes it angry) return toJsxRuntime(tree as Root, { Fragment, - jsx, - jsxs, + jsx: jsx as Jsx, + jsxs: jsxs as Jsx, elementAttributeNameCase: "html", components: customComponents, }) From 5d666d1860be721c573ebd24cb82d210a8567bbf Mon Sep 17 00:00:00 2001 From: Jacky Zhao Date: Sat, 11 Nov 2023 11:59:05 -0800 Subject: [PATCH 07/16] fix: normalize relative urls (closes #569) --- quartz/components/scripts/spa.inline.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/quartz/components/scripts/spa.inline.ts b/quartz/components/scripts/spa.inline.ts index 115bc968b..917671de5 100644 --- a/quartz/components/scripts/spa.inline.ts +++ b/quartz/components/scripts/spa.inline.ts @@ -1,5 +1,6 @@ import micromorph from "micromorph" import { FullSlug, RelativeURL, getFullSlug } from "../../util/path" +import { normalizeRelativeURLs } from "./popover.inline" // adapted from `micromorph` // https://github.com/natemoo-re/micromorph @@ -52,6 +53,8 @@ async function navigate(url: URL, isBack: boolean = false) { if (!contents) return const html = p.parseFromString(contents, "text/html") + normalizeRelativeURLs(html, url) + let title = html.querySelector("title")?.textContent if (title) { document.title = title @@ -154,6 +157,7 @@ if (!customElements.get("route-announcer")) { style: "position: absolute; left: 0; top: 0; clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; white-space: nowrap; width: 1px; height: 1px", } + customElements.define( "route-announcer", class RouteAnnouncer extends HTMLElement { From f67356c3d2d609a8a6b674ac650fc41d6dfb4cc7 Mon Sep 17 00:00:00 2001 From: Jacky Zhao Date: Sat, 11 Nov 2023 12:02:34 -0800 Subject: [PATCH 08/16] lint: format --- quartz/components/scripts/spa.inline.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quartz/components/scripts/spa.inline.ts b/quartz/components/scripts/spa.inline.ts index 917671de5..0318ec348 100644 --- a/quartz/components/scripts/spa.inline.ts +++ b/quartz/components/scripts/spa.inline.ts @@ -157,7 +157,7 @@ if (!customElements.get("route-announcer")) { style: "position: absolute; left: 0; top: 0; clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; white-space: nowrap; width: 1px; height: 1px", } - + customElements.define( "route-announcer", class RouteAnnouncer extends HTMLElement { From bf603c49c2edaa5fcbe42479421a336dcacf92b9 Mon Sep 17 00:00:00 2001 From: Jacky Zhao Date: Sat, 11 Nov 2023 12:08:54 -0800 Subject: [PATCH 09/16] fix: sort rss feed by date --- quartz/plugins/emitters/contentIndex.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/quartz/plugins/emitters/contentIndex.ts b/quartz/plugins/emitters/contentIndex.ts index 69d0d376f..2c703685d 100644 --- a/quartz/plugins/emitters/contentIndex.ts +++ b/quartz/plugins/emitters/contentIndex.ts @@ -6,6 +6,7 @@ import { FilePath, FullSlug, SimpleSlug, simplifySlug } from "../../util/path" import { QuartzEmitterPlugin } from "../types" import { toHtml } from "hast-util-to-html" import path from "path" +import { byDateAndAlphabetical } from "../../components/PageList" export type ContentIndex = Map export type ContentDetails = { @@ -59,6 +60,7 @@ function generateRSSFeed(cfg: GlobalConfiguration, idx: ContentIndex, limit?: nu ` const items = Array.from(idx) + .sort((a, b) => byDateAndAlphabetical(cfg)(a[1], b[1])) .map(([slug, content]) => createURLEntry(simplifySlug(slug), content)) .slice(0, limit ?? idx.size) .join("") From bc02791734aa969ff388c5f7068e402bd0e3862a Mon Sep 17 00:00:00 2001 From: Jacky Zhao Date: Sat, 11 Nov 2023 20:27:51 -0800 Subject: [PATCH 10/16] fix: .date.getTime() based sort --- quartz/plugins/emitters/contentIndex.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/quartz/plugins/emitters/contentIndex.ts b/quartz/plugins/emitters/contentIndex.ts index 2c703685d..c5170c64a 100644 --- a/quartz/plugins/emitters/contentIndex.ts +++ b/quartz/plugins/emitters/contentIndex.ts @@ -6,7 +6,6 @@ import { FilePath, FullSlug, SimpleSlug, simplifySlug } from "../../util/path" import { QuartzEmitterPlugin } from "../types" import { toHtml } from "hast-util-to-html" import path from "path" -import { byDateAndAlphabetical } from "../../components/PageList" export type ContentIndex = Map export type ContentDetails = { @@ -60,7 +59,17 @@ function generateRSSFeed(cfg: GlobalConfiguration, idx: ContentIndex, limit?: nu ` const items = Array.from(idx) - .sort((a, b) => byDateAndAlphabetical(cfg)(a[1], b[1])) + .sort(([_, f1], [__, f2]) => { + if (f1.date && f2.date) { + return f2.date.getTime() - f1.date.getTime() + } else if (f1.date && !f2.date) { + return -1 + } else if (!f1.date && f2.date) { + return 1 + } + + return f1.title.localeCompare(f2.title) + }) .map(([slug, content]) => createURLEntry(simplifySlug(slug), content)) .slice(0, limit ?? idx.size) .join("") From 275bea3051a6f2a7da65fef6dccc7cc52a0a15e3 Mon Sep 17 00:00:00 2001 From: Jacky Zhao Date: Sat, 11 Nov 2023 20:46:29 -0800 Subject: [PATCH 11/16] style + cfg: resolve breadcrumb titles by default and change arrow character --- quartz/components/Breadcrumbs.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/quartz/components/Breadcrumbs.tsx b/quartz/components/Breadcrumbs.tsx index 467b5a503..f35f8766f 100644 --- a/quartz/components/Breadcrumbs.tsx +++ b/quartz/components/Breadcrumbs.tsx @@ -28,9 +28,9 @@ interface BreadcrumbOptions { } const defaultOptions: BreadcrumbOptions = { - spacerSymbol: ">", + spacerSymbol: "❯", rootName: "Home", - resolveFrontmatterTitle: false, + resolveFrontmatterTitle: true, hideOnRoot: true, } From d1551872ffeb08c34f59dc2042b43562c499b620 Mon Sep 17 00:00:00 2001 From: Jacky Zhao Date: Sat, 11 Nov 2023 20:46:57 -0800 Subject: [PATCH 12/16] fix: check if popover exists after fetching and before inserting --- quartz/components/scripts/popover.inline.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/quartz/components/scripts/popover.inline.ts b/quartz/components/scripts/popover.inline.ts index ed3c925ce..9506ec412 100644 --- a/quartz/components/scripts/popover.inline.ts +++ b/quartz/components/scripts/popover.inline.ts @@ -28,8 +28,11 @@ async function mouseEnterHandler( }) } + const hasAlreadyBeenFetched = () => + [...link.children].some((child) => child.classList.contains("popover")) + // dont refetch if there's already a popover - if ([...link.children].some((child) => child.classList.contains("popover"))) { + if (hasAlreadyBeenFetched()) { return setPosition(link.lastChild as HTMLElement) } @@ -49,6 +52,11 @@ async function mouseEnterHandler( console.error(err) }) + // bailout if another popover exists + if (hasAlreadyBeenFetched()) { + return + } + if (!contents) return const html = p.parseFromString(contents, "text/html") normalizeRelativeURLs(html, targetUrl) From 43d638a6de5a8a11c1a719c596b07ea957c7329a Mon Sep 17 00:00:00 2001 From: Jacky Zhao Date: Sat, 11 Nov 2023 21:06:37 -0800 Subject: [PATCH 13/16] perf: compute mapping of folder name to file data for faster breadcrumbs --- docs/features/breadcrumbs.md | 6 ++-- quartz/components/Breadcrumbs.tsx | 47 +++++++++++++++---------------- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/docs/features/breadcrumbs.md b/docs/features/breadcrumbs.md index 94db66ac0..20c3b8d65 100644 --- a/docs/features/breadcrumbs.md +++ b/docs/features/breadcrumbs.md @@ -16,10 +16,10 @@ For example, here's what the default configuration looks like: ```typescript title="quartz.layout.ts" Component.Breadcrumbs({ - spacerSymbol: ">", // symbol between crumbs + spacerSymbol: "❯", // symbol between crumbs rootName: "Home", // name of first/root element - resolveFrontmatterTitle: false, // wether to resolve folder names through frontmatter titles (more computationally expensive) - hideOnRoot: true, // wether to hide breadcrumbs on root `index.md` page + resolveFrontmatterTitle: true, // whether to resolve folder names through frontmatter titles + hideOnRoot: true, // whether to hide breadcrumbs on root `index.md` page }) ``` diff --git a/quartz/components/Breadcrumbs.tsx b/quartz/components/Breadcrumbs.tsx index f35f8766f..29c73a81b 100644 --- a/quartz/components/Breadcrumbs.tsx +++ b/quartz/components/Breadcrumbs.tsx @@ -41,25 +41,13 @@ function formatCrumb(displayName: string, baseSlug: FullSlug, currentSlug: Simpl } } -// given a folderName (e.g. "features"), search for the corresponding `index.md` file -function findCurrentFile(allFiles: QuartzPluginData[], folderName: string) { - return allFiles.find((file) => { - if (file.slug?.endsWith("index")) { - const folderParts = file.filePath?.split("/") - if (folderParts) { - const name = folderParts[folderParts?.length - 2] - if (name === folderName) { - return true - } - } - } - }) -} - export default ((opts?: Partial) => { // Merge options with defaults const options: BreadcrumbOptions = { ...defaultOptions, ...opts } + // computed index of folder name to its associated file data + let folderIndex: Map | undefined + function Breadcrumbs({ fileData, allFiles, displayClass }: QuartzComponentProps) { // Hide crumbs on root if enabled if (options.hideOnRoot && fileData.slug === "index") { @@ -70,28 +58,39 @@ export default ((opts?: Partial) => { const firstEntry = formatCrumb(options.rootName, fileData.slug!, "/" as SimpleSlug) const crumbs: CrumbData[] = [firstEntry] + if (!folderIndex && options.resolveFrontmatterTitle) { + folderIndex = new Map() + // construct the index for the first time + for (const file of allFiles) { + if (file.slug?.endsWith("index")) { + const folderParts = file.filePath?.split("/") + if (folderParts) { + const folderName = folderParts[folderParts?.length - 2] + folderIndex.set(folderName, file) + } + } + } + } + // Split slug into hierarchy/parts const slugParts = fileData.slug?.split("/") if (slugParts) { // full path until current part let currentPath = "" for (let i = 0; i < slugParts.length - 1; i++) { - let currentTitle = slugParts[i] + let curPathSegment = slugParts[i] - // TODO: performance optimizations/memoizing // Try to resolve frontmatter folder title - if (options?.resolveFrontmatterTitle) { - // try to find file for current path - const currentFile = findCurrentFile(allFiles, currentTitle) - if (currentFile) { - currentTitle = currentFile.frontmatter!.title - } + const currentFile = folderIndex?.get(curPathSegment) + if (currentFile) { + curPathSegment = currentFile.frontmatter!.title } + // Add current slug to full path currentPath += slugParts[i] + "/" // Format and add current crumb - const crumb = formatCrumb(currentTitle, fileData.slug!, currentPath as SimpleSlug) + const crumb = formatCrumb(curPathSegment, fileData.slug!, currentPath as SimpleSlug) crumbs.push(crumb) } From 74c63e448e28d9766f7ec631aac2645384b0975f Mon Sep 17 00:00:00 2001 From: Jacky Zhao Date: Sat, 11 Nov 2023 21:13:10 -0800 Subject: [PATCH 14/16] fix(style): dont internal-link highlight when image (closes #581) --- quartz/styles/base.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quartz/styles/base.scss b/quartz/styles/base.scss index c72965659..8bb8efb45 100644 --- a/quartz/styles/base.scss +++ b/quartz/styles/base.scss @@ -64,7 +64,7 @@ a { color: var(--tertiary) !important; } - &.internal { + &.internal:not(:has(img)) { text-decoration: none; background-color: var(--highlight); padding: 0 0.1rem; From cf6ab9e9333b5f76cb9e06f6687f2b4f8fbe91bd Mon Sep 17 00:00:00 2001 From: Jacky Zhao Date: Sun, 12 Nov 2023 14:27:53 -0800 Subject: [PATCH 15/16] feat: option to specify npx quartz sync message (closes #583) --- quartz/cli/args.js | 5 +++++ quartz/cli/handlers.js | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/quartz/cli/args.js b/quartz/cli/args.js index 3543e2e89..7ed5b078e 100644 --- a/quartz/cli/args.js +++ b/quartz/cli/args.js @@ -41,6 +41,11 @@ export const SyncArgv = { default: true, describe: "create a git commit for your unsaved changes", }, + message: { + string: true, + alias: ["m"], + describe: "option to override the default Quartz commit message", + }, push: { boolean: true, default: true, diff --git a/quartz/cli/handlers.js b/quartz/cli/handlers.js index 48a44ec9f..96ee9bc88 100644 --- a/quartz/cli/handlers.js +++ b/quartz/cli/handlers.js @@ -483,8 +483,9 @@ export async function handleSync(argv) { dateStyle: "medium", timeStyle: "short", }) + const commitMessage = argv.message ?? `Quartz sync: ${currentTimestamp}` spawnSync("git", ["add", "."], { stdio: "inherit" }) - spawnSync("git", ["commit", "-m", `Quartz sync: ${currentTimestamp}`], { stdio: "inherit" }) + spawnSync("git", ["commit", "-m", commitMessage], { stdio: "inherit" }) if (contentStat.isSymbolicLink()) { // put symlink back From 8223465bda1e7b6085a9752ead806be85ff58b57 Mon Sep 17 00:00:00 2001 From: Jacky Zhao Date: Sun, 12 Nov 2023 14:33:19 -0800 Subject: [PATCH 16/16] fix: make :has img selector direct --- quartz/styles/base.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quartz/styles/base.scss b/quartz/styles/base.scss index 8bb8efb45..2e5edec15 100644 --- a/quartz/styles/base.scss +++ b/quartz/styles/base.scss @@ -64,7 +64,7 @@ a { color: var(--tertiary) !important; } - &.internal:not(:has(img)) { + &.internal:not(:has(> img)) { text-decoration: none; background-color: var(--highlight); padding: 0 0.1rem;