feat: publish public website at furtka.org
Hugo static site with an intentionally minimal single-page copy — English default, German under /de/ — while the project stays pre-alpha. No CMS, no external theme, no webfonts, no external requests. System-UI sans on a paper-white / near-black palette with a deep crimson accent; a small wicket-gate SVG as the sole brand mark. Hosting: nginx on forge-runner-01 serves /var/www/furtka.org; the upstream openresty proxy terminates TLS so the VM itself only speaks plain HTTP. Deploy is ./website/deploy.sh (rsync + remote hugo --minify). One-time VM bootstrap in ops/nginx/setup-vm.sh.
This commit is contained in:
parent
7f15543f1c
commit
defd2eda06
20 changed files with 604 additions and 0 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -8,3 +8,8 @@ __pycache__/
|
|||
# Real credentials must never be committed — use the .example files
|
||||
archinstall/user_credentials.json
|
||||
iso/out/
|
||||
|
||||
# Hugo website
|
||||
website/public/
|
||||
website/resources/
|
||||
website/.hugo_build.lock
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ This project uses calendar versioning: `YY.N-stage` (e.g. `26.0-alpha` = 2026, r
|
|||
|
||||
- **Forgejo Actions runner** live on Proxmox VM (`forge-runner-01`, Ubuntu 24.04) with DinD sidecar — CI green end-to-end. Setup scripts in `ops/forgejo-runner/`.
|
||||
- **Walking-skeleton live ISO** (`iso/build.sh`). Overlays an Arch `releng` profile with Flask + the webinstaller, bakes a systemd unit that auto-starts the wizard on boot, produces a hybrid BIOS/UEFI ISO via `mkarchiso` in a privileged `archlinux:latest` container. Tested booting under OVMF in Proxmox — wizard screens 1–3 respond at `http://<vm-ip>:5000`.
|
||||
- **Public website at [furtka.org](https://furtka.org)** (`website/`). Hugo static site, English + German, served from `/var/www/furtka.org` on `forge-runner-01` via nginx. Upstream openresty proxy handles TLS. Intentionally minimal single-page copy while the project is pre-alpha. Deploy is `./website/deploy.sh` (rsync + remote Hugo build); one-time VM setup in `ops/nginx/setup-vm.sh`.
|
||||
|
||||
## [26.0-alpha] - 2026-04-13
|
||||
|
||||
|
|
|
|||
28
ops/nginx/furtka.org.conf
Normal file
28
ops/nginx/furtka.org.conf
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name furtka.org www.furtka.org;
|
||||
|
||||
root /var/www/furtka.org;
|
||||
index index.html;
|
||||
|
||||
charset utf-8;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ $uri.html =404;
|
||||
}
|
||||
|
||||
location = /favicon.svg {
|
||||
access_log off;
|
||||
log_not_found off;
|
||||
expires 7d;
|
||||
}
|
||||
|
||||
location ~* \.(css|js|svg|woff2?|png|jpg|jpeg|webp|avif)$ {
|
||||
access_log off;
|
||||
expires 30d;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
error_page 404 /404.html;
|
||||
}
|
||||
27
ops/nginx/setup-vm.sh
Executable file
27
ops/nginx/setup-vm.sh
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
#!/usr/bin/env bash
|
||||
# One-time setup on forge-runner-01 for furtka.org.
|
||||
# Idempotent — safe to re-run.
|
||||
#
|
||||
# Usage (on the VM, with sudo):
|
||||
# sudo ops/nginx/setup-vm.sh
|
||||
set -euo pipefail
|
||||
|
||||
OWNER="${SUDO_USER:-daniel}"
|
||||
WEBROOT="/var/www/furtka.org"
|
||||
SRCROOT="/srv/furtka-site"
|
||||
SITE_CONF="/etc/nginx/sites-available/furtka.org"
|
||||
SITE_LINK="/etc/nginx/sites-enabled/furtka.org"
|
||||
|
||||
install -d -o "$OWNER" -g "$OWNER" -m 0755 "$WEBROOT"
|
||||
install -d -o "$OWNER" -g "$OWNER" -m 0755 "$SRCROOT"
|
||||
|
||||
cp "$(dirname "$0")/furtka.org.conf" "$SITE_CONF"
|
||||
ln -sfn "$SITE_CONF" "$SITE_LINK"
|
||||
|
||||
# Drop the Ubuntu default site so it doesn't shadow us on :80.
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
nginx -t
|
||||
systemctl reload nginx
|
||||
|
||||
echo "OK: furtka.org ready at $WEBROOT (owner $OWNER)"
|
||||
61
website/README.md
Normal file
61
website/README.md
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
# website/ — furtka.org
|
||||
|
||||
Hugo source for [furtka.org](https://furtka.org). Intentionally minimal while
|
||||
the project is pre-alpha: a single idea page in English and German, nothing more.
|
||||
More pages will come back when there's something real to show.
|
||||
|
||||
## Local build
|
||||
|
||||
```sh
|
||||
cd website
|
||||
hugo server # http://localhost:1313
|
||||
```
|
||||
|
||||
Requires Hugo **extended** ≥ 0.140.
|
||||
|
||||
## Deploy
|
||||
|
||||
Hosted on `forge-runner-01` (Proxmox VM, Ubuntu 24.04). Hugo runs on the VM;
|
||||
nginx serves the built output from `/var/www/furtka.org`. TLS is terminated by
|
||||
an upstream openresty reverse proxy — the VM itself only speaks plain HTTP.
|
||||
|
||||
First time only, on the VM:
|
||||
|
||||
```sh
|
||||
ssh forge-runner
|
||||
sudo /srv/furtka-site/ops/nginx/setup-vm.sh # or copy the script over first
|
||||
```
|
||||
|
||||
From then on, deploy from your dev machine:
|
||||
|
||||
```sh
|
||||
./website/deploy.sh
|
||||
```
|
||||
|
||||
The script rsyncs `website/` to `/srv/furtka-site/` on the VM and runs
|
||||
`hugo --minify` into `/var/www/furtka.org`.
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
hugo.toml Hugo config (multilingual: en default, de)
|
||||
content/ Markdown pages
|
||||
_index.md Home (EN)
|
||||
_index.de.md Home (DE)
|
||||
layouts/ Custom inline theme — no external theme or framework
|
||||
_default/ baseof, single, list
|
||||
partials/ head, header, footer, gate SVG, lang switcher
|
||||
index.html Home-only layout with editorial hero
|
||||
assets/css/main.css Stylesheet (fingerprinted + minified on build)
|
||||
static/favicon.svg Gate mark in crimson
|
||||
deploy.sh Rsync + remote Hugo build
|
||||
```
|
||||
|
||||
## Design
|
||||
|
||||
Modern-minimal on paper-white light / near-black dark. System-UI sans
|
||||
(no webfonts — zero external requests, matches the self-hosting ethos).
|
||||
Deep crimson accent, `prefers-color-scheme` switch.
|
||||
|
||||
The gate SVG is the one brand mark — a small wicket-gate glyph repeated in the
|
||||
header, footer, and favicon.
|
||||
260
website/assets/css/main.css
Normal file
260
website/assets/css/main.css
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
:root {
|
||||
--bg: #f7f6f3;
|
||||
--bg-subtle: #efeee8;
|
||||
--fg: #0e0e0f;
|
||||
--fg-muted: #6b6b6f;
|
||||
--accent: #c03a28;
|
||||
--accent-hover: #a0301f;
|
||||
--border: #e4e3dc;
|
||||
--font-sans:
|
||||
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
|
||||
Arial, "Noto Sans", sans-serif;
|
||||
--font-mono:
|
||||
ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
|
||||
--measure: 32rem;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--bg: #0d0d0f;
|
||||
--bg-subtle: #17171a;
|
||||
--fg: #ececee;
|
||||
--fg-muted: #8a8a90;
|
||||
--accent: #ff6b56;
|
||||
--accent-hover: #ff8b78;
|
||||
--border: #232326;
|
||||
}
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
html { -webkit-text-size-adjust: 100%; }
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
font-family: var(--font-sans);
|
||||
font-size: 1.0625rem;
|
||||
line-height: 1.65;
|
||||
font-feature-settings: "kern", "cv11", "ss01";
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 52rem;
|
||||
margin-inline: auto;
|
||||
padding-inline: 1.5rem;
|
||||
}
|
||||
|
||||
main.container {
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
padding-block: 3rem 4.5rem;
|
||||
}
|
||||
|
||||
.gate-mark {
|
||||
color: var(--accent);
|
||||
vertical-align: -0.2em;
|
||||
}
|
||||
|
||||
/* ── Kicker (shared small-caps style) ────────────────────────── */
|
||||
|
||||
.kicker {
|
||||
font-family: var(--font-sans);
|
||||
font-weight: 500;
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.14em;
|
||||
text-transform: uppercase;
|
||||
color: var(--fg-muted);
|
||||
}
|
||||
|
||||
/* ── Site header ─────────────────────────────────────────────── */
|
||||
|
||||
.site-header {
|
||||
border-bottom: 1px solid var(--border);
|
||||
padding-block: 1rem;
|
||||
}
|
||||
.site-header .container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1.25rem;
|
||||
}
|
||||
.site-title {
|
||||
margin-right: auto;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
.site-title .gate-mark {
|
||||
width: 1.15em;
|
||||
height: 1.15em;
|
||||
vertical-align: -0.15em;
|
||||
}
|
||||
.site-title .wordmark {
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.14em;
|
||||
color: var(--fg);
|
||||
text-transform: uppercase;
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
.site-title:hover .wordmark { color: var(--accent); }
|
||||
|
||||
/* ── Language switcher ───────────────────────────────────────── */
|
||||
|
||||
.lang-switcher {
|
||||
display: flex;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
}
|
||||
.lang-switcher li { display: flex; }
|
||||
.lang-switcher a {
|
||||
font-family: var(--font-sans);
|
||||
font-weight: 500;
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.14em;
|
||||
text-transform: uppercase;
|
||||
color: var(--fg-muted);
|
||||
text-decoration: none;
|
||||
padding: 0.1rem 0.15rem;
|
||||
border-bottom: 1.5px solid transparent;
|
||||
}
|
||||
.lang-switcher a:hover { color: var(--accent); }
|
||||
.lang-switcher .is-active a {
|
||||
color: var(--fg);
|
||||
border-bottom-color: var(--accent);
|
||||
}
|
||||
|
||||
/* ── Hero (home) ─────────────────────────────────────────────── */
|
||||
|
||||
.hero {
|
||||
padding-block: 3.5rem 2.5rem;
|
||||
}
|
||||
|
||||
.status-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.45rem;
|
||||
font-family: var(--font-sans);
|
||||
font-weight: 600;
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.14em;
|
||||
text-transform: uppercase;
|
||||
color: var(--accent);
|
||||
margin: 0 0 1.75rem;
|
||||
}
|
||||
.status-chip::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
border-radius: 999px;
|
||||
background: var(--accent);
|
||||
}
|
||||
.status-chip .mono {
|
||||
font-family: var(--font-mono);
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.02em;
|
||||
text-transform: none;
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
|
||||
.home h1 {
|
||||
font-family: var(--font-sans);
|
||||
font-weight: 800;
|
||||
font-size: clamp(3.25rem, 10vw, 6.5rem);
|
||||
line-height: 0.95;
|
||||
letter-spacing: -0.035em;
|
||||
margin: 0 0 1.5rem;
|
||||
color: var(--fg);
|
||||
}
|
||||
|
||||
.home .lede {
|
||||
font-family: var(--font-sans);
|
||||
font-weight: 400;
|
||||
font-size: clamp(1.15rem, 1.8vw, 1.375rem);
|
||||
line-height: 1.4;
|
||||
letter-spacing: -0.005em;
|
||||
color: var(--fg-muted);
|
||||
margin: 0;
|
||||
max-width: 36rem;
|
||||
}
|
||||
|
||||
/* ── Body prose ──────────────────────────────────────────────── */
|
||||
|
||||
.prose {
|
||||
max-width: var(--measure);
|
||||
}
|
||||
.prose p {
|
||||
margin: 0 0 1.2rem;
|
||||
letter-spacing: -0.003em;
|
||||
}
|
||||
.prose p:last-child { margin-bottom: 0; }
|
||||
|
||||
.prose strong {
|
||||
font-weight: 600;
|
||||
color: var(--fg);
|
||||
}
|
||||
|
||||
.prose a {
|
||||
color: var(--accent);
|
||||
text-decoration: underline;
|
||||
text-decoration-color: color-mix(in srgb, var(--accent) 35%, transparent);
|
||||
text-decoration-thickness: 1px;
|
||||
text-underline-offset: 3px;
|
||||
transition: color 120ms, text-decoration-color 120ms;
|
||||
}
|
||||
.prose a:hover {
|
||||
color: var(--accent-hover);
|
||||
text-decoration-color: var(--accent);
|
||||
}
|
||||
|
||||
.prose em, .prose i { font-style: italic; }
|
||||
|
||||
/* ── Footer ──────────────────────────────────────────────────── */
|
||||
|
||||
.site-footer {
|
||||
margin-top: auto;
|
||||
border-top: 1px solid var(--border);
|
||||
padding-block: 1.25rem;
|
||||
}
|
||||
.site-footer .container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.site-footer .kicker { margin: 0; }
|
||||
.site-footer a {
|
||||
color: var(--fg-muted);
|
||||
text-decoration: none;
|
||||
border-bottom: 1px solid transparent;
|
||||
padding-bottom: 1px;
|
||||
transition: color 120ms, border-color 120ms;
|
||||
}
|
||||
.site-footer a:hover {
|
||||
color: var(--accent);
|
||||
border-bottom-color: var(--accent);
|
||||
}
|
||||
|
||||
/* ── Selection + focus ───────────────────────────────────────── */
|
||||
|
||||
::selection {
|
||||
background: var(--accent);
|
||||
color: var(--bg);
|
||||
}
|
||||
|
||||
:focus-visible {
|
||||
outline: 2px solid var(--accent);
|
||||
outline-offset: 3px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
18
website/content/_index.de.md
Normal file
18
website/content/_index.de.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
title: "Furtka"
|
||||
description: "Offenes Heimserver-Betriebssystem — einfach genug für alle."
|
||||
status: "<span class=\"mono\">26.0-alpha</span> — in Arbeit"
|
||||
---
|
||||
|
||||
**Furtka** ist ein offenes Heimserver-Betriebssystem.
|
||||
USB-Stick einstecken, durch einen Assistenten klicken, und aus jedem
|
||||
alten x86-PC wird eine private Cloud für den Haushalt — mit eigenen
|
||||
Apps, eigener Domain, eigenen Daten.
|
||||
|
||||
Das Ziel ist einfach: **dein Vater soll das einrichten können.**
|
||||
|
||||
Wir sind zu zweit und bauen das öffentlich, abends und am Wochenende.
|
||||
Es ist früh. Außer uns selbst sollte das noch niemand benutzen.
|
||||
Sobald es etwas Echtes zu zeigen gibt, steht es hier.
|
||||
|
||||
Mitlesen? Schreib an <a href="mailto:hallo@furtka.org">hallo@furtka.org</a>.
|
||||
18
website/content/_index.md
Normal file
18
website/content/_index.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
title: "Furtka"
|
||||
description: "Open-source home server OS — simple enough for everyone."
|
||||
status: "<span class=\"mono\">26.0-alpha</span> — work in progress"
|
||||
---
|
||||
|
||||
**Furtka** is an open-source home server OS.
|
||||
Boot from USB, click through a wizard, and any old x86 PC
|
||||
turns into a private cloud for your household — with your own apps,
|
||||
your own domain, your own data.
|
||||
|
||||
The goal is simple: **your dad should be able to set this up.**
|
||||
|
||||
We're two people building it in public on evenings and weekends,
|
||||
and it's early. Nothing here is ready for other people yet.
|
||||
When there's something real to show, this page will say so.
|
||||
|
||||
Want to follow along? Write to <a href="mailto:hallo@furtka.org">hallo@furtka.org</a>.
|
||||
22
website/deploy.sh
Executable file
22
website/deploy.sh
Executable file
|
|
@ -0,0 +1,22 @@
|
|||
#!/usr/bin/env bash
|
||||
# Deploy furtka.org to forge-runner-01.
|
||||
# Rsyncs the Hugo source up to the VM and builds it in-place.
|
||||
set -euo pipefail
|
||||
|
||||
HOST="${FURTKA_HOST:-forge-runner}"
|
||||
SRCROOT="/srv/furtka-site"
|
||||
WEBROOT="/var/www/furtka.org"
|
||||
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
echo "→ rsync website/ to $HOST:$SRCROOT"
|
||||
rsync -az --delete \
|
||||
--exclude='.hugo_build.lock' \
|
||||
--exclude='public/' \
|
||||
--exclude='resources/' \
|
||||
"$HERE/" "$HOST:$SRCROOT/"
|
||||
|
||||
echo "→ build on $HOST"
|
||||
ssh "$HOST" "cd $SRCROOT && hugo --minify --cleanDestinationDir -d $WEBROOT"
|
||||
|
||||
echo "OK: deployed to https://furtka.org/"
|
||||
36
website/hugo.toml
Normal file
36
website/hugo.toml
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
baseURL = "https://furtka.org/"
|
||||
title = "Furtka"
|
||||
defaultContentLanguage = "en"
|
||||
defaultContentLanguageInSubdir = false
|
||||
enableRobotsTXT = true
|
||||
|
||||
[params]
|
||||
description = "Open-source home server OS — simple enough for everyone."
|
||||
version = "26.0-alpha"
|
||||
contactEmail = "hallo@furtka.org"
|
||||
|
||||
[markup.goldmark.renderer]
|
||||
unsafe = true
|
||||
|
||||
[languages]
|
||||
[languages.en]
|
||||
languageCode = "en-us"
|
||||
languageName = "English"
|
||||
title = "Furtka"
|
||||
weight = 1
|
||||
[languages.en.params]
|
||||
description = "Open-source home server OS — simple enough for everyone."
|
||||
|
||||
[languages.de]
|
||||
languageCode = "de-de"
|
||||
languageName = "Deutsch"
|
||||
title = "Furtka"
|
||||
weight = 2
|
||||
[languages.de.params]
|
||||
description = "Offenes Heimserver-Betriebssystem — einfach genug für alle."
|
||||
|
||||
[build]
|
||||
writeStats = true
|
||||
|
||||
[minify]
|
||||
minifyOutput = true
|
||||
13
website/layouts/_default/baseof.html
Normal file
13
website/layouts/_default/baseof.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="{{ .Site.Language.Lang }}">
|
||||
<head>
|
||||
{{ partial "head.html" . }}
|
||||
</head>
|
||||
<body>
|
||||
{{ partial "header.html" . }}
|
||||
<main class="container">
|
||||
{{ block "main" . }}{{ end }}
|
||||
</main>
|
||||
{{ partial "footer.html" . }}
|
||||
</body>
|
||||
</html>
|
||||
21
website/layouts/_default/list.html
Normal file
21
website/layouts/_default/list.html
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{{ define "main" }}
|
||||
<article>
|
||||
<header class="page-header">
|
||||
<h1>{{ .Title }}</h1>
|
||||
{{ with .Params.description }}<p class="lede">{{ . }}</p>{{ end }}
|
||||
</header>
|
||||
<div class="prose{{ if .Params.wide }} prose--wide{{ end }}">
|
||||
{{ .Content }}
|
||||
</div>
|
||||
{{ with .Pages }}
|
||||
<ul class="post-list">
|
||||
{{ range . }}
|
||||
<li>
|
||||
<a href="{{ .RelPermalink }}">{{ .Title }}</a>
|
||||
{{ with .Date }}<time datetime="{{ .Format "2006-01-02" }}">{{ .Format "2006-01-02" }}</time>{{ end }}
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
{{ end }}
|
||||
</article>
|
||||
{{ end }}
|
||||
11
website/layouts/_default/single.html
Normal file
11
website/layouts/_default/single.html
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{{ define "main" }}
|
||||
<article>
|
||||
<header class="page-header">
|
||||
<h1>{{ .Title }}</h1>
|
||||
{{ with .Params.description }}<p class="lede">{{ . }}</p>{{ end }}
|
||||
</header>
|
||||
<div class="prose{{ if .Params.wide }} prose--wide{{ end }}">
|
||||
{{ .Content }}
|
||||
</div>
|
||||
</article>
|
||||
{{ end }}
|
||||
14
website/layouts/index.html
Normal file
14
website/layouts/index.html
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{{ define "main" }}
|
||||
<article class="home">
|
||||
<header class="hero">
|
||||
{{ with .Params.status }}
|
||||
<p class="status-chip">{{ . | safeHTML }}</p>
|
||||
{{ end }}
|
||||
<h1>{{ .Title }}</h1>
|
||||
{{ with site.Params.description }}<p class="lede">{{ . }}</p>{{ end }}
|
||||
</header>
|
||||
<div class="prose">
|
||||
{{ .Content }}
|
||||
</div>
|
||||
</article>
|
||||
{{ end }}
|
||||
9
website/layouts/partials/footer.html
Normal file
9
website/layouts/partials/footer.html
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<footer class="site-footer">
|
||||
<div class="container">
|
||||
<p class="kicker">
|
||||
Furtka <span style="letter-spacing:0.04em">{{ site.Params.version }}</span>
|
||||
· AGPL-3.0 ·
|
||||
<a href="mailto:{{ site.Params.contactEmail }}">{{ site.Params.contactEmail }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
6
website/layouts/partials/gate.html
Normal file
6
website/layouts/partials/gate.html
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<svg class="gate-mark" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">
|
||||
<path d="M4 20 V12 A9 9 0 0 1 20 12 V20"/>
|
||||
<line x1="12" y1="5" x2="12" y2="20"/>
|
||||
<line x1="3" y1="20" x2="21" y2="20"/>
|
||||
<line x1="15" y1="12" x2="15" y2="14.5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 384 B |
16
website/layouts/partials/head.html
Normal file
16
website/layouts/partials/head.html
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{{ if .IsHome }}{{ site.Title }} — {{ site.Params.description }}{{ else }}{{ .Title }} · {{ site.Title }}{{ end }}</title>
|
||||
<meta name="description" content="{{ with .Params.description }}{{ . }}{{ else }}{{ site.Params.description }}{{ end }}">
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||
<meta property="og:site_name" content="{{ site.Title }}">
|
||||
<meta property="og:title" content="{{ if .IsHome }}{{ site.Title }}{{ else }}{{ .Title }} · {{ site.Title }}{{ end }}">
|
||||
<meta property="og:description" content="{{ with .Params.description }}{{ . }}{{ else }}{{ site.Params.description }}{{ end }}">
|
||||
<meta property="og:type" content="{{ if .IsPage }}article{{ else }}website{{ end }}">
|
||||
<meta property="og:url" content="{{ .Permalink }}">
|
||||
{{ $parts := split .Site.Language.LanguageCode "-" }}<meta property="og:locale" content="{{ index $parts 0 }}{{ if gt (len $parts) 1 }}_{{ upper (index $parts 1) }}{{ end }}">
|
||||
{{ range .AllTranslations }}
|
||||
<link rel="alternate" hreflang="{{ .Lang }}" href="{{ .Permalink }}">
|
||||
{{ end }}
|
||||
{{ $css := resources.Get "css/main.css" | minify | fingerprint }}
|
||||
<link rel="stylesheet" href="{{ $css.RelPermalink }}" integrity="{{ $css.Data.Integrity }}">
|
||||
9
website/layouts/partials/header.html
Normal file
9
website/layouts/partials/header.html
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<header class="site-header">
|
||||
<div class="container">
|
||||
<a href="{{ if eq .Site.Language.Lang "de" }}/de/{{ else }}/{{ end }}" class="site-title" aria-label="Furtka — home">
|
||||
{{ partial "gate.html" . }}
|
||||
<span class="wordmark">Furtka</span>
|
||||
</a>
|
||||
{{ partial "lang-switcher.html" . }}
|
||||
</div>
|
||||
</header>
|
||||
23
website/layouts/partials/lang-switcher.html
Normal file
23
website/layouts/partials/lang-switcher.html
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<ul class="lang-switcher" aria-label="Language">
|
||||
{{ $current := .Site.Language.Lang }}
|
||||
{{ range site.Languages }}
|
||||
{{ $lang := . }}
|
||||
{{ $link := "" }}
|
||||
{{ if eq .Lang $current }}
|
||||
{{ $link = $.Permalink }}
|
||||
{{ else }}
|
||||
{{ with (where $.AllTranslations "Lang" .Lang) }}
|
||||
{{ $link = (index . 0).Permalink }}
|
||||
{{ else }}
|
||||
{{ if eq $lang.Lang "en" }}
|
||||
{{ $link = "/" }}
|
||||
{{ else }}
|
||||
{{ $link = printf "/%s/" $lang.Lang }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
<li{{ if eq .Lang $current }} class="is-active"{{ end }}>
|
||||
<a href="{{ $link }}" lang="{{ .Lang }}"{{ if eq .Lang $current }} aria-current="true"{{ end }}>{{ upper .Lang }}</a>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
6
website/static/favicon.svg
Normal file
6
website/static/favicon.svg
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#a33e2e" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M4 20 V12 A9 9 0 0 1 20 12 V20"/>
|
||||
<line x1="12" y1="5" x2="12" y2="20"/>
|
||||
<line x1="3" y1="20" x2="21" y2="20"/>
|
||||
<line x1="15" y1="12" x2="15" y2="14.5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 334 B |
Loading…
Add table
Reference in a new issue