From 363e8aafacfcb530c7133bcecb2c24b676a732eb Mon Sep 17 00:00:00 2001 From: Johannes Kresner Date: Sun, 17 Aug 2025 13:15:17 +0200 Subject: [PATCH] feat: initial commit --- .gitignore | 3 + Dockerfile | 7 + archetypes/default.md | 5 + content/about/_index.md | 7 + content/posts/example-margin-garden.md | 34 ++++ hugo.toml | 21 ++ layouts/_default/baseof.html | 18 ++ layouts/_default/list.html | 19 ++ layouts/_default/single.html | 16 ++ layouts/_partials/header.html | 18 ++ static/css/latte-chroma-style.css | 256 +++++++++++++++++++++++++ static/css/mocha-chroma-style.css | 256 +++++++++++++++++++++++++ static/css/theme.css | 173 +++++++++++++++++ static/theme.js | 51 +++++ 14 files changed, 884 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 archetypes/default.md create mode 100644 content/about/_index.md create mode 100644 content/posts/example-margin-garden.md create mode 100644 hugo.toml create mode 100644 layouts/_default/baseof.html create mode 100644 layouts/_default/list.html create mode 100644 layouts/_default/single.html create mode 100644 layouts/_partials/header.html create mode 100644 static/css/latte-chroma-style.css create mode 100644 static/css/mocha-chroma-style.css create mode 100644 static/css/theme.css create mode 100644 static/theme.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..48749f8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.lock + +public/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..89884d1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +FROM nginx:alpine + +COPY ./public /usr/share/nginx/html + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/archetypes/default.md b/archetypes/default.md new file mode 100644 index 0000000..25b6752 --- /dev/null +++ b/archetypes/default.md @@ -0,0 +1,5 @@ ++++ +date = '{{ .Date }}' +draft = true +title = '{{ replace .File.ContentBaseName "-" " " | title }}' ++++ diff --git a/content/about/_index.md b/content/about/_index.md new file mode 100644 index 0000000..369c306 --- /dev/null +++ b/content/about/_index.md @@ -0,0 +1,7 @@ +--- +title: "About" +--- + +This is a minimal About page. Customize it in `content/about/_index.md`. + + diff --git a/content/posts/example-margin-garden.md b/content/posts/example-margin-garden.md new file mode 100644 index 0000000..5ad6bf7 --- /dev/null +++ b/content/posts/example-margin-garden.md @@ -0,0 +1,34 @@ +--- +title: "Margin Garden Example" +date: 2025-08-17T10:00:00Z +tags: [design, hugo, demo] +summary: "A demo post showing margin notes, code blocks, and theme toggling." +--- + +This is a short example of the Margin Garden style. We'll use a footnote for a margin note.[^mn] + +## Code sample + +```ts +function greet(name: string): string { + return `Hello, ${name}!`; +} + +console.log(greet("world")); +``` + +Blockquotes should look refined: + +> Good design is as little design as possible. + +A list with tags-like styling: + +- `hugo` +- `theme` +- `catppuccin` + +And an inline code `const x = 1` example. + +[^mn]: This footnote appears as a margin note on wide screens. + + diff --git a/hugo.toml b/hugo.toml new file mode 100644 index 0000000..82da1b2 --- /dev/null +++ b/hugo.toml @@ -0,0 +1,21 @@ +baseURL = 'https://blog.jokresner.de/' +languageCode = 'en-us' +title = 'My personal blog' + +[markup] + [markup.highlight] + noClasses = false + +[menu] + [[menu.main]] + name = 'Home' + url = '/' + weight = 1 + [[menu.main]] + name = 'Posts' + url = '/posts/' + weight = 2 + [[menu.main]] + name = 'About' + url = '/about/' + weight = 3 \ No newline at end of file diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html new file mode 100644 index 0000000..442ed1d --- /dev/null +++ b/layouts/_default/baseof.html @@ -0,0 +1,18 @@ + + + + + + {{ if .IsHome }}{{ .Site.Title }}{{ else }}{{ .Title }} · {{ .Site.Title }}{{ end }} + + + + + + + {{ partial "header.html" . }} +
+ {{ block "main" . }}{{ end }} +
+ + diff --git a/layouts/_default/list.html b/layouts/_default/list.html new file mode 100644 index 0000000..02c9fa4 --- /dev/null +++ b/layouts/_default/list.html @@ -0,0 +1,19 @@ +{{ define "main" }} +
+

{{ .Title }}

+ {{ range .Pages.ByDate.Reverse }} +
+

{{ .Title }}

+ + {{ with .Summary }}

{{ . }}

{{ end }} +
+ {{ end }} +
+{{ end }} + + diff --git a/layouts/_default/single.html b/layouts/_default/single.html new file mode 100644 index 0000000..7da47b7 --- /dev/null +++ b/layouts/_default/single.html @@ -0,0 +1,16 @@ +{{ define "main" }} +
+
+

{{ .Title }}

+ +
+
{{ .Content }}
+
+{{ end }} + + diff --git a/layouts/_partials/header.html b/layouts/_partials/header.html new file mode 100644 index 0000000..61147ab --- /dev/null +++ b/layouts/_partials/header.html @@ -0,0 +1,18 @@ + + + diff --git a/static/css/latte-chroma-style.css b/static/css/latte-chroma-style.css new file mode 100644 index 0000000..2fad7da --- /dev/null +++ b/static/css/latte-chroma-style.css @@ -0,0 +1,256 @@ +.chroma { + color: #4c4f69; + background-color: #eff1f5; +} +.chroma .cl { + color: #4c4f69; +} +.chroma .err { + color: #d20f39; +} +.chroma .x { + color: #4c4f69; +} +.chroma .hl { + background-color: #bcc0cc; +} +.chroma .lnt { + color: #8c8fa1; +} +.chroma .ln { + color: #8c8fa1; +} +.chroma .k { + color: #8839ef; +} +.chroma .kr { + color: #8839ef; +} +.chroma .kp { + color: #8839ef; +} +.chroma .kc { + color: #fe640b; +} +.chroma .kd { + color: #d20f39; +} +.chroma .kn { + color: #179299; +} +.chroma .kt { + color: #d20f39; +} +.chroma .n { + color: #4c4f69; +} +.chroma .nc { + color: #df8e1d; +} +.chroma .no { + color: #df8e1d; +} +.chroma .nd { + color: #1e66f5; + font-weight: bold; +} +.chroma .ni { + color: #179299; +} +.chroma .ne { + color: #fe640b; +} +.chroma .nf { + color: #1e66f5; +} +.chroma .fm { + color: #1e66f5; +} +.chroma .nl { + color: #04a5e5; +} +.chroma .nn { + color: #fe640b; +} +.chroma .py { + color: #fe640b; +} +.chroma .nt { + color: #8839ef; +} +.chroma .nv { + color: #dc8a78; +} +.chroma .vc { + color: #dc8a78; +} +.chroma .vg { + color: #dc8a78; +} +.chroma .vi { + color: #dc8a78; +} +.chroma .vm { + color: #dc8a78; +} +.chroma .na { + color: #1e66f5; +} +.chroma .nb { + color: #04a5e5; +} +.chroma .bp { + color: #04a5e5; +} +.chroma .nx { + color: #4c4f69; +} +.chroma .l { + color: #4c4f69; +} +.chroma .ld { + color: #4c4f69; +} +.chroma .s { + color: #40a02b; +} +.chroma .sc { + color: #40a02b; +} +.chroma .s1 { + color: #40a02b; +} +.chroma .s2 { + color: #40a02b; +} +.chroma .sb { + color: #40a02b; +} +.chroma .sx { + color: #40a02b; +} +.chroma .ss { + color: #40a02b; +} +.chroma .si { + color: #40a02b; +} +.chroma .sa { + color: #d20f39; +} +.chroma .dl { + color: #1e66f5; +} +.chroma .se { + color: #1e66f5; +} +.chroma .sr { + color: #179299; +} +.chroma .sd { + color: #9ca0b0; +} +.chroma .sh { + color: #9ca0b0; +} +.chroma .m { + color: #fe640b; +} +.chroma .mb { + color: #fe640b; +} +.chroma .mh { + color: #fe640b; +} +.chroma .mi { + color: #fe640b; +} +.chroma .mf { + color: #fe640b; +} +.chroma .il { + color: #fe640b; +} +.chroma .mo { + color: #fe640b; +} +.chroma .o { + color: #04a5e5; + font-weight: bold; +} +.chroma .ow { + color: #04a5e5; + font-weight: bold; +} +.chroma .c { + color: #9ca0b0; + font-style: italic; +} +.chroma .c1 { + color: #9ca0b0; + font-style: italic; +} +.chroma .cm { + color: #9ca0b0; + font-style: italic; +} +.chroma .cs { + color: #9ca0b0; + font-style: italic; +} +.chroma .ch { + color: #acb0be; + font-style: italic; +} +.chroma .cp { + color: #9ca0b0; + font-style: italic; +} +.chroma .cpf { + color: #9ca0b0; + font-weight: bold; +} +.chroma .g { + color: #4c4f69; +} +.chroma .gi { + color: #40a02b; + background-color: #ccd0da; +} +.chroma .gd { + color: #d20f39; + background-color: #ccd0da; +} +.chroma .ge { + color: #4c4f69; + font-style: italic; +} +.chroma .gs { + color: #4c4f69; + font-weight: bold; +} +.chroma .gl { + color: #4c4f69; + text-decoration: underline; +} +.chroma .gh { + color: #fe640b; + font-weight: bold; +} +.chroma .gu { + color: #fe640b; + font-weight: bold; +} +.chroma .go { + color: #4c4f69; +} +.chroma .gp { + color: #4c4f69; +} +.chroma .gr { + color: #d20f39; +} +.chroma .gt { + color: #d20f39; +} + diff --git a/static/css/mocha-chroma-style.css b/static/css/mocha-chroma-style.css new file mode 100644 index 0000000..5c2bf0e --- /dev/null +++ b/static/css/mocha-chroma-style.css @@ -0,0 +1,256 @@ +.chroma { + color: #cdd6f4; + background-color: #1e1e2e; +} +.chroma .cl { + color: #cdd6f4; +} +.chroma .err { + color: #f38ba8; +} +.chroma .x { + color: #cdd6f4; +} +.chroma .hl { + background-color: #45475a; +} +.chroma .lnt { + color: #7f849c; +} +.chroma .ln { + color: #7f849c; +} +.chroma .k { + color: #cba6f7; +} +.chroma .kr { + color: #cba6f7; +} +.chroma .kp { + color: #cba6f7; +} +.chroma .kc { + color: #fab387; +} +.chroma .kd { + color: #f38ba8; +} +.chroma .kn { + color: #94e2d5; +} +.chroma .kt { + color: #f38ba8; +} +.chroma .n { + color: #cdd6f4; +} +.chroma .nc { + color: #f9e2af; +} +.chroma .no { + color: #f9e2af; +} +.chroma .nd { + color: #89b4fa; + font-weight: bold; +} +.chroma .ni { + color: #94e2d5; +} +.chroma .ne { + color: #fab387; +} +.chroma .nf { + color: #89b4fa; +} +.chroma .fm { + color: #89b4fa; +} +.chroma .nl { + color: #89dceb; +} +.chroma .nn { + color: #fab387; +} +.chroma .py { + color: #fab387; +} +.chroma .nt { + color: #cba6f7; +} +.chroma .nv { + color: #f5e0dc; +} +.chroma .vc { + color: #f5e0dc; +} +.chroma .vg { + color: #f5e0dc; +} +.chroma .vi { + color: #f5e0dc; +} +.chroma .vm { + color: #f5e0dc; +} +.chroma .na { + color: #89b4fa; +} +.chroma .nb { + color: #89dceb; +} +.chroma .bp { + color: #89dceb; +} +.chroma .nx { + color: #cdd6f4; +} +.chroma .l { + color: #cdd6f4; +} +.chroma .ld { + color: #cdd6f4; +} +.chroma .s { + color: #a6e3a1; +} +.chroma .sc { + color: #a6e3a1; +} +.chroma .s1 { + color: #a6e3a1; +} +.chroma .s2 { + color: #a6e3a1; +} +.chroma .sb { + color: #a6e3a1; +} +.chroma .sx { + color: #a6e3a1; +} +.chroma .ss { + color: #a6e3a1; +} +.chroma .si { + color: #a6e3a1; +} +.chroma .sa { + color: #f38ba8; +} +.chroma .dl { + color: #89b4fa; +} +.chroma .se { + color: #89b4fa; +} +.chroma .sr { + color: #94e2d5; +} +.chroma .sd { + color: #6c7086; +} +.chroma .sh { + color: #6c7086; +} +.chroma .m { + color: #fab387; +} +.chroma .mb { + color: #fab387; +} +.chroma .mh { + color: #fab387; +} +.chroma .mi { + color: #fab387; +} +.chroma .mf { + color: #fab387; +} +.chroma .il { + color: #fab387; +} +.chroma .mo { + color: #fab387; +} +.chroma .o { + color: #89dceb; + font-weight: bold; +} +.chroma .ow { + color: #89dceb; + font-weight: bold; +} +.chroma .c { + color: #6c7086; + font-style: italic; +} +.chroma .c1 { + color: #6c7086; + font-style: italic; +} +.chroma .cm { + color: #6c7086; + font-style: italic; +} +.chroma .cs { + color: #6c7086; + font-style: italic; +} +.chroma .ch { + color: #585b70; + font-style: italic; +} +.chroma .cp { + color: #6c7086; + font-style: italic; +} +.chroma .cpf { + color: #6c7086; + font-weight: bold; +} +.chroma .g { + color: #cdd6f4; +} +.chroma .gi { + color: #a6e3a1; + background-color: #313244; +} +.chroma .gd { + color: #f38ba8; + background-color: #313244; +} +.chroma .ge { + color: #cdd6f4; + font-style: italic; +} +.chroma .gs { + color: #cdd6f4; + font-weight: bold; +} +.chroma .gl { + color: #cdd6f4; + text-decoration: underline; +} +.chroma .gh { + color: #fab387; + font-weight: bold; +} +.chroma .gu { + color: #fab387; + font-weight: bold; +} +.chroma .go { + color: #cdd6f4; +} +.chroma .gp { + color: #cdd6f4; +} +.chroma .gr { + color: #f38ba8; +} +.chroma .gt { + color: #f38ba8; +} + diff --git a/static/css/theme.css b/static/css/theme.css new file mode 100644 index 0000000..eebd2b6 --- /dev/null +++ b/static/css/theme.css @@ -0,0 +1,173 @@ +:root[data-theme="latte"] { + --bg: #eff1f5; + --fg: #4c4f69; + --muted: #8c8fa1; + --card: #ffffffcc; + --accent: #7287fd; + --accent-2: #179299; + --code-bg: color-mix(in srgb, var(--bg) 88%, transparent); + --border: #ccd0da; +} + +:root[data-theme="mocha"] { + --bg: #1e1e2e; + --fg: #cdd6f4; + --muted: #a6adc8; + --card: #1e1e2ecc; + --accent: #b4befe; + --accent-2: #94e2d5; + --code-bg: color-mix(in srgb, var(--bg) 70%, transparent); + --border: #313244; +} + +html, +body { + background: var(--bg); + color: var(--fg); +} + +a { + color: var(--accent); + text-decoration: underline; + text-decoration-color: color-mix(in srgb, var(--accent) 40%, transparent); +} + +pre, +code { + background: var(--code-bg); +} + +.post-card { + background: var(--card); + border: 1px solid var(--border); + border-radius: 12px; + box-shadow: 0 8px 20px rgba(0, 0, 0, .05); +} + +.progress { + position: fixed; + top: 0; + left: 0; + height: 2px; + width: 0; + background: var(--accent-2); + z-index: 50; +} + +@media (min-width: 1100px) { + .with-sidenotes { + max-width: 70ch; + margin-right: 22ch; + position: relative; + } + + .sidenote { + position: absolute; + right: -20ch; + width: 18ch; + color: var(--muted); + } +} + +html, +body { + margin: 0; +} + +html { + font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + line-height: 1.7; +} +pre, code, kbd, samp { + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace; +} + +main { + max-width: 70ch; + padding: 2rem 1rem 4rem; + margin: 0 auto; +} + +.post-card { + padding: 1rem 1.25rem; + transition: transform .15s ease, box-shadow .15s ease; +} + +.post-card:hover { + transform: translateY(-2px); + box-shadow: 0 10px 24px rgba(0, 0, 0, .08); +} + +.post-meta { + color: var(--muted); + font-size: .9rem; +} + +.tag { + display: inline-block; + border: 1px solid var(--border); + border-radius: 999px; + padding: .1rem .5rem; + margin-right: .25rem; + color: var(--muted); +} + +/* Minimal header and navigation */ +.site-header { + position: sticky; + top: 0; + backdrop-filter: saturate(180%) blur(8px); + background: color-mix(in srgb, var(--bg) 85%, transparent); + border-bottom: 1px solid var(--border); +} + +.container { + max-width: 70ch; + margin: 0 auto; + padding: .75rem 1rem; +} + +.header-inner { + display: flex; + align-items: center; + gap: .75rem; +} + +.site-logo { + text-decoration: none; + color: inherit; + font-weight: 600; + margin-right: auto; +} + +.site-nav { + display: flex; + gap: .5rem; + flex-wrap: wrap; +} + +.site-nav__link { + text-decoration: none; + color: var(--fg); + border: 1px solid transparent; + border-radius: 8px; + padding: .25rem .5rem; +} + +.site-nav__link:hover { + background: var(--card); + border-color: var(--border); +} + +.site-nav__link.is-active { + background: color-mix(in srgb, var(--accent) 12%, var(--bg)); + border-color: color-mix(in srgb, var(--accent) 30%, var(--border)); +} + +.theme-toggle { + background: transparent; + border: 1px solid var(--border); + border-radius: 999px; + padding: .25rem .5rem; + color: var(--fg); +} \ No newline at end of file diff --git a/static/theme.js b/static/theme.js new file mode 100644 index 0000000..762550b --- /dev/null +++ b/static/theme.js @@ -0,0 +1,51 @@ +(function () { + const root = document.documentElement; + const key = "theme"; + const stored = localStorage.getItem(key); + const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches; + const initial = stored || (prefersDark ? "mocha" : "latte"); + root.setAttribute("data-theme", initial); + toggleChroma(initial); + + const btn = document.getElementById("theme-toggle"); + function updateToggleIcon(theme) { + if (!btn) return; + const isDark = theme === "mocha"; + btn.textContent = isDark ? "☀️" : "🌙"; + btn.setAttribute("aria-label", isDark ? "Switch to light theme" : "Switch to dark theme"); + btn.setAttribute("title", isDark ? "Light theme" : "Dark theme"); + } + function setTheme(next) { + root.setAttribute("data-theme", next); + localStorage.setItem(key, next); + toggleChroma(next); + updateToggleIcon(next); + } + function toggleChroma(theme) { + const latte = document.getElementById("chroma-latte"); + const mocha = document.getElementById("chroma-mocha"); + if (latte && mocha) { + if (theme === "mocha") { + mocha.removeAttribute("disabled"); + latte.setAttribute("disabled", "disabled"); + } else { + latte.removeAttribute("disabled"); + mocha.setAttribute("disabled", "disabled"); + } + } + } + updateToggleIcon(initial); + btn && btn.addEventListener("click", () => { + setTheme(root.getAttribute("data-theme") === "latte" ? "mocha" : "latte"); + }); + + // Reading progress + const bar = document.getElementById("progress"); + function onScroll() { + const t = document.documentElement; + const scrolled = (t.scrollTop / (t.scrollHeight - t.clientHeight)) * 100; + bar && (bar.style.width = scrolled + "%"); + } + document.addEventListener("scroll", onScroll, { passive: true }); + onScroll(); + })(); \ No newline at end of file