Files
docs/Sources/Docs/Templates/BaseLayout.swift
Michael Housh 8a9a361a4b
All checks were successful
CI / release (push) Successful in 5m58s
feat: Adds plausible analytics.
2025-04-16 11:22:34 -04:00

141 lines
5.4 KiB
Swift

import Foundation
import HTML
/// The base page layout used to render the different sections of the website.
///
/// - Parameters:
/// - conocicalURL: The url for the page.
/// - section: The section of the page.
/// - title: The page title.
/// - rssLink: A prefix for generating an rss feed for the page (generally only used for articles).
/// - extraHeader: Any extra items to be placed in the `head` of the html.
func baseLayout(
canocicalURL: String,
section: Section,
title pageTitle: String,
rssLink: String = "",
extraHeader: NodeConvertible = Node.fragment([]),
@NodeBuilder children: () -> NodeConvertible
) -> Node {
return [
.documentType("html"),
html(lang: "en-US") {
generateHead(pageTitle, extraHeader)
body(class: "text-white text-lg font-avenir \(section.rawValue)") {
siteHeader(section)
// mx-10
div(class: "mb-auto") {
children()
}
if section == .articles {
footer(rssLink)
}
// NOTE: These need to stay at / near bottom of page, so that icons are
// generated properly.
script(src: "https://unpkg.com/lucide@latest")
Node.raw("""
<script>
lucide.createIcons();
</script>
""")
}
}
]
}
private func siteHeader(_ section: Section) -> Node {
header(class: "header") {
div(class: "header__inner") {
div(class: "header__logo") {
a(href: "/") {
div(class: "logo") {
img(src: "/static/favicon-32x32.png")
span(class: "pl-2") { "docs.housh.dev" }
}
}
}
}
// TODO: Explore search being hidden / triggered by a button and hover above
// the page content.
div(class: "font-avenir w-full p-4 px-8", id: "search") {}
div(class: "mt-2 mb-0 w-full border-b border-slate-200")
}
}
private func footer(_ rssLink: String) -> Node {
div(class: "text-slate-400 border-t border-light text-center pt-2 text-sm") {
div {
"Copyright © Michael Housh \(Date().description.prefix(4))."
}
p(class: "mb-2") {
"Built in Swift using"
a(
class: "text-orange-400 [&:hover]:border-b border-green-400",
href: "https://github.com/loopwerk/Saga",
rel: "nofollow",
target: "_blank"
) { "Saga" }
"("
%a(
class: "[&:hover]:border-b border-green-400",
href: "https://git.housh.dev/homelab/docs",
rel: "nofollow",
target: "_blank"
) { "source" }
%")."
}
}
}
private func generateHead(_ pageTitle: String, _ extraHeader: NodeConvertible) -> Node {
head {
meta(charset: "utf-8")
meta(content: "#0e1112", name: "theme-color", customAttributes: ["media": "(prefers-color-scheme: dark)"])
meta(content: "#566B78", name: "theme-color", customAttributes: ["media": "(prefers-color-scheme: light)"])
meta(content: "Michael Housh", name: "author")
meta(content: "HHE-Docs", name: "apple-mobile-web-app-title")
meta(content: "initial-scale=1.0, width=device-width", name: "viewport")
meta(content: "telephone=no", name: "format-detection")
meta(content: "True", name: "HandheldFriendly")
meta(content: "320", name: "MobileOptimized")
meta(content: "HHE-Docs", name: "og:site_name")
meta(content: "hvac, developer, swift, home-performance, design", name: "keywords")
title { SiteMetadata.name + ": \(pageTitle)" }
Node.raw("""
<link rel="apple-touch-icon" sizes="57x57" href="/static/apple-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/static/apple-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/static/apple-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="/static/apple-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="/static/apple-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="/static/apple-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/static/apple-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/static/apple-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/static/apple-icon-180x180.png">
<link rel="icon" type="image/png" sizes="192x192" href="/static/android-icon-192x192.png">
<link rel="icon" type="image/png" sizes="32x32" href="/static/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="/static/favicon-96x96.png">
<link rel="icon" type="image/png" sizes="16x16" href="/static/favicon-16x16.png">
<link rel="manifest" href="/static/manifest.json">
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="/static/ms-icon-144x144.png">
<meta name="theme-color" content="#ffffff">
<script defer data-domain="docs.housh.dev" src="https://plausible.housh.dev/js/script.js"></script>
""")
link(href: "/static/output.css", rel: "stylesheet")
link(href: "/articles/feed.xml", rel: "alternate", title: SiteMetadata.name, type: "application/rss+xml")
extraHeader
Node.raw("""
<script src="/pagefind/pagefind-ui.js"></script>
<link href="/pagefind/pagefind-ui.css" rel="stylesheet">
<script>
window.addEventListener('DOMContentLoaded', (event) => {
new PagefindUI({ element: "#search", showSubResults: true });
});
</script>
""")
link(href: "/static/style.css", rel: "stylesheet")
}
}