From be63b52265605975c95a8fdf790c0db5ce07d35f Mon Sep 17 00:00:00 2001
From: ruinivist
Date: Sun, 29 Mar 2026 02:06:01 +0000
Subject: [PATCH] components: add ruins layout primitives
---
quartz.layout.ts | 13 +++---
quartz/components/Height.tsx | 20 +++++++++
quartz/components/RuinsDarkmode.tsx | 66 +++++++++++++++++++++++++++++
quartz/components/RuinsFooter.tsx | 12 ++++++
quartz/components/index.ts | 6 +++
5 files changed, 109 insertions(+), 8 deletions(-)
create mode 100644 quartz/components/Height.tsx
create mode 100644 quartz/components/RuinsDarkmode.tsx
create mode 100644 quartz/components/RuinsFooter.tsx
diff --git a/quartz.layout.ts b/quartz.layout.ts
index 9322246..56a3db2 100644
--- a/quartz.layout.ts
+++ b/quartz.layout.ts
@@ -6,17 +6,13 @@ export const sharedPageComponents: SharedLayout = {
head: Component.Head(),
header: [],
afterBody: [],
- footer: Component.Footer({
- links: {
- GitHub: "https://github.com/jackyzha0/quartz",
- "Discord Community": "https://discord.gg/cRFFHYye7t",
- },
- }),
+ footer: Component.RuinsFooter(),
}
// components for pages that display a single page (e.g. a single note)
export const defaultContentPageLayout: PageLayout = {
beforeBody: [
+ Component.Height(),
Component.ConditionalRender({
component: Component.Breadcrumbs(),
condition: (page) => page.fileData.slug !== "index",
@@ -26,12 +22,13 @@ export const defaultContentPageLayout: PageLayout = {
],
left: [
Component.PageTitle(),
+ Component.Height({ height: "1rem" }),
Component.MobileOnly(Component.Spacer()),
Component.Search(),
Component.Explorer(),
- Component.DesktopOnly(Component.Darkmode()),
+ Component.DesktopOnly(Component.RuinsDarkmode()),
],
- right: [Component.MobileOnly(Component.Darkmode())],
+ right: [Component.MobileOnly(Component.RuinsDarkmode())],
}
// components for pages that display lists of pages (e.g. tags or folders)
diff --git a/quartz/components/Height.tsx b/quartz/components/Height.tsx
new file mode 100644
index 0000000..ce1fdeb
--- /dev/null
+++ b/quartz/components/Height.tsx
@@ -0,0 +1,20 @@
+import { classNames } from "../util/lang"
+import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
+
+interface Options {
+ height: string
+}
+
+export default ((opts?: Partial) => {
+ function Height({ displayClass }: QuartzComponentProps) {
+ return
+ }
+
+ Height.css = `
+ .title-height {
+ height: ${opts?.height || "4rem"};
+ }
+ `
+
+ return Height
+}) satisfies QuartzComponentConstructor
diff --git a/quartz/components/RuinsDarkmode.tsx b/quartz/components/RuinsDarkmode.tsx
new file mode 100644
index 0000000..f644218
--- /dev/null
+++ b/quartz/components/RuinsDarkmode.tsx
@@ -0,0 +1,66 @@
+// @ts-ignore
+import darkmodeScript from "./scripts/darkmode.inline"
+import { i18n } from "../i18n"
+import { classNames } from "../util/lang"
+import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
+
+const RuinsDarkmode: QuartzComponent = ({ displayClass, cfg }: QuartzComponentProps) => {
+ return (
+
+
+ Somber Nights
+
{i18n(cfg.locale).components.themeToggle.lightMode}
+
+
+ )
+}
+
+RuinsDarkmode.beforeDOMLoaded = darkmodeScript
+RuinsDarkmode.css = `
+.ruins-darkmode {
+ cursor: pointer;
+ padding: 0;
+ background: none;
+ border: none;
+ margin: 0;
+ text-align: inherit;
+ color: var(--gray);
+ font-family: var(--metaFont);
+ font-size: 1.05rem;
+ line-height: 1.2;
+}
+
+.ruins-darkmode p {
+ margin: 0;
+}
+
+:root[saved-theme="dark"] {
+ color-scheme: dark;
+}
+
+:root[saved-theme="light"] {
+ color-scheme: light;
+}
+
+:root[saved-theme="dark"] .ruins-darkmode > .dayIcon {
+ display: none;
+}
+
+:root[saved-theme="dark"] .ruins-darkmode > .nightIcon {
+ display: inline;
+}
+
+:root .ruins-darkmode > .dayIcon {
+ display: inline;
+}
+
+:root .ruins-darkmode > .nightIcon {
+ display: none;
+}
+`
+
+export default (() => RuinsDarkmode) satisfies QuartzComponentConstructor
diff --git a/quartz/components/RuinsFooter.tsx b/quartz/components/RuinsFooter.tsx
new file mode 100644
index 0000000..9056c67
--- /dev/null
+++ b/quartz/components/RuinsFooter.tsx
@@ -0,0 +1,12 @@
+import style from "./styles/footer.scss"
+import { classNames } from "../util/lang"
+import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
+
+export default (() => {
+ const RuinsFooter: QuartzComponent = ({ displayClass }: QuartzComponentProps) => {
+ return
+ }
+
+ RuinsFooter.css = style
+ return RuinsFooter
+}) satisfies QuartzComponentConstructor
diff --git a/quartz/components/index.ts b/quartz/components/index.ts
index cece8e6..7d2e928 100644
--- a/quartz/components/index.ts
+++ b/quartz/components/index.ts
@@ -6,6 +6,7 @@ import ArticleTitle from "./ArticleTitle"
import Darkmode from "./Darkmode"
import ReaderMode from "./ReaderMode"
import Head from "./Head"
+import Height from "./Height"
import PageTitle from "./PageTitle"
import ContentMeta from "./ContentMeta"
import Spacer from "./Spacer"
@@ -16,6 +17,8 @@ import Graph from "./Graph"
import Backlinks from "./Backlinks"
import Search from "./Search"
import Footer from "./Footer"
+import RuinsFooter from "./RuinsFooter"
+import RuinsDarkmode from "./RuinsDarkmode"
import DesktopOnly from "./DesktopOnly"
import MobileOnly from "./MobileOnly"
import RecentNotes from "./RecentNotes"
@@ -32,6 +35,7 @@ export {
Darkmode,
ReaderMode,
Head,
+ Height,
PageTitle,
ContentMeta,
Spacer,
@@ -42,6 +46,8 @@ export {
Backlinks,
Search,
Footer,
+ RuinsFooter,
+ RuinsDarkmode,
DesktopOnly,
MobileOnly,
RecentNotes,