init: scaffold OPENBUREAU site with shibui-derived theme

- Hugo site for openbureau.ch (Deutsch, i18n-ready for EN/IT)
- Theme themes/openbureau/ = local copy of shibui, customized via
  site-level layouts and assets to keep the theme reference clean
- Editorial typography stack: Newsreader serif body, Space Grotesk
  display, Inter for listings, IBM Plex Mono for technical meta
- Content structure: library/ (Theorie, Büroführung, Software) with
  manifest and colophon at root; software is a library category, not
  a separate top-level
- Three views over one source: Journal (chronological home),
  Library (atlas grouped by section + tag cloud), single articles

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-23 20:25:29 +02:00
commit 00c3343b1d
87 changed files with 2929 additions and 0 deletions
+77
View File
@@ -0,0 +1,77 @@
<style>
* { cursor: none !important; }
body { min-height: 400px; display: flex; align-items: center; justify-content: center; flex-direction: column; gap: 2rem; font-family: monospace; }
#cursor {
position: fixed;
pointer-events: none;
z-index: 9999;
image-rendering: pixelated;
}
.demo-box {
padding: 1.5rem 2rem;
border: 0.5px solid var(--color-border-tertiary);
border-radius: 8px;
text-align: center;
font-size: 14px;
color: var(--color-text-secondary);
}
</style>
<h2 class="sr-only">8-bit cursor preview</h2>
<div class="demo-box">bewege die maus hier</div>
<canvas id="cursor"></canvas>
<script>
const canvas = document.getElementById('cursor');
const ctx = canvas.getContext('2d');
const P = 3;
// B=black, W=white, _=transparent
// Pixel-genaue Nachzeichnung des klassischen Windows-Cursors
const grid = [
'B_____________',
'BB____________',
'BWB___________',
'BWWB__________',
'BWWWB_________',
'BWWWWB________',
'BWWWWWB_______',
'BWWWWWWB______',
'BWWWWWWWB_____',
'BWWWWWWWWB____',
'BWWWWWWWWWB___',
'BWWWWWWBBB____',
'BWWWBWWB______',
'BWWB_BWWB_____',
'BWB___BWWB____',
'BB_____BWWB___',
'B_______BWB___',
'________BB____',
];
const rows = grid.length;
const cols = grid[0].length;
canvas.width = cols * P;
canvas.height = rows * P;
canvas.style.width = cols * P + 'px';
canvas.style.height = rows * P + 'px';
grid.forEach((row, y) => {
for (let x = 0; x < row.length; x++) {
const c = row[x];
if (c === 'B') {
ctx.fillStyle = '#000000';
ctx.fillRect(x * P, y * P, P, P);
} else if (c === 'W') {
ctx.fillStyle = '#ffffff';
ctx.fillRect(x * P, y * P, P, P);
}
}
});
document.addEventListener('mousemove', e => {
canvas.style.left = e.clientX + 'px';
canvas.style.top = e.clientY + 'px';
});
</script>
+2
View File
@@ -0,0 +1,2 @@
<!-- This partial can be replaced to support other commenting engines -->
{{ template "_internal/disqus.html" . }}
+1
View File
@@ -0,0 +1 @@
<time datetime="{{ . | time.Format "2006-01-02" }}">{{ . | time.Format "2006" }}&nbsp;</time>
+4
View File
@@ -0,0 +1,4 @@
<p>© {{ now.Year }} karimgabrielevarano.xyz</p>
<p><a href="/legal-notice/">legal notice</a></p>
<script src="/js/auto-carousel.js"></script>
<link rel="stylesheet" href="/css/auto-carousel.css">
+66
View File
@@ -0,0 +1,66 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="color-scheme" content="light" />
<link rel="preconnect" href="https://cdn.jsdelivr.net">
<link rel="dns-prefetch" href="https://cdn.jsdelivr.net">
<title>
{{ if .IsHome }}{{ site.Title }}{{ else }}{{ printf "%s | %s" .Title
site.Title }}{{ end }}
</title>
{{ with .Description | default .Site.Params.description }}<meta
name="description"
content="{{ . }}"
/>{{ end }} {{/* Canonical URL */}}
<link rel="canonical" href="{{ .Permalink }}" />
{{/* Open Graph */}}
<meta property="og:title" content="{{ .Title }}" />
{{ with .Description | default .Site.Params.description }}<meta
property="og:description"
content="{{ . }}"
/>{{ end }}
<meta
property="og:type"
content="{{ if .IsPage }}article{{ else }}website{{ end }}"
/>
<meta property="og:url" content="{{ .Permalink }}" />
{{ with .Site.Params.ogImage }}<meta
property="og:image"
content="{{ . | absURL }}"
/>{{ end }} {{/* Twitter Card */}}
<meta name="twitter:card" content="summary" />
<meta name="twitter:title" content="{{ .Title }}" />
{{ with .Description | default .Site.Params.description }}<meta
name="twitter:description"
content="{{ . }}"
/>{{ end }} {{ with .Site.Params.twitterHandle }}<meta
name="twitter:site"
content="@{{ . }}"
/>{{ end }} {{ with .Site.Params.ogImage }}<meta
name="twitter:image"
content="{{ . | absURL }}"
/>{{ end }} {{/* RSS Feed */}} {{ with .OutputFormats.Get "rss" -}} {{ printf
`<link rel="%s" type="%s" href="%s" title="%s" />` .Rel .MediaType.Type
.Permalink site.Title | safeHTML }} {{ end }} {{/* JSON-LD Structured Data for
Articles */}} {{ if .IsPage }}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "{{ .Title }}",
"url": "{{ .Permalink }}",
"datePublished": "{{ .Date.Format "2006-01-02T15:04:05Z07:00" }}",
"dateModified": "{{ .Lastmod.Format "2006-01-02T15:04:05Z07:00" }}"
{{- with .Site.Params.author }},
"author": {
"@type": "Person",
"name": "{{ .name | default . }}"
}
{{- end }}
{{- with $.Description | default $.Site.Params.description }},
"description": "{{ . }}"
{{- end }}
}
</script>
{{ end }} {{ partialCached "head/favicon.html" . }} {{ partialCached
"head/css.html" . }}
+19
View File
@@ -0,0 +1,19 @@
{{- with resources.Get "css/main.css" }}
{{- if hugo.IsDevelopment }}
<link rel="stylesheet" href="{{ .RelPermalink }}">
{{- else }}
{{- with . | minify | fingerprint }}
<link rel="stylesheet" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous">
{{- end }}
{{- end }}
{{- end }}
{{- with resources.Get "css/custom.css" }}
{{- if hugo.IsDevelopment }}
<link rel="stylesheet" href="{{ .RelPermalink }}">
{{- else }}
{{- with . | minify | fingerprint }}
<link rel="stylesheet" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous">
{{- end }}
{{- end }}
{{- end }}
+17
View File
@@ -0,0 +1,17 @@
<link rel="apple-touch-icon" sizes="57x57" href="{{ "favicon_io/apple-icon-57x57.png" | relURL }}">
<link rel="apple-touch-icon" sizes="60x60" href="{{ "favicon_io/apple-icon-60x60.png" | relURL }}">
<link rel="apple-touch-icon" sizes="72x72" href="{{ "favicon_io/apple-icon-72x72.png" | relURL }}">
<link rel="apple-touch-icon" sizes="76x76" href="{{ "favicon_io/apple-icon-76x76.png" | relURL }}">
<link rel="apple-touch-icon" sizes="114x114" href="{{ "favicon_io/apple-icon-114x114.png" | relURL }}">
<link rel="apple-touch-icon" sizes="120x120" href="{{ "favicon_io/apple-icon-120x120.png" | relURL }}">
<link rel="apple-touch-icon" sizes="144x144" href="{{ "favicon_io/apple-icon-144x144.png" | relURL }}">
<link rel="apple-touch-icon" sizes="152x152" href="{{ "favicon_io/apple-icon-152x152.png" | relURL }}">
<link rel="apple-touch-icon" sizes="180x180" href="{{ "favicon_io/apple-icon-180x180.png" | relURL }}">
<link rel="icon" type="image/png" sizes="192x192" href="{{ "favicon_io/android-icon-192x192.png" | relURL }}">
<link rel="icon" type="image/png" sizes="32x32" href="{{ "favicon_io/favicon-32x32.png" | relURL }}">
<link rel="icon" type="image/png" sizes="96x96" href="{{ "favicon_io/favicon-96x96.png" | relURL }}">
<link rel="icon" type="image/png" sizes="16x16" href="{{ "favicon_io/favicon-16x16.png" | relURL }}">
<link rel="manifest" href="{{ "favicon_io/manifest.json" | relURL }}">
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="{{ "favicon_io/ms-icon-144x144.png" | relURL }}">
<meta name="theme-color" content="#ffffff">
+19
View File
@@ -0,0 +1,19 @@
<nav class="path-nav" aria-label="Breadcrumb">
<ol>
{{ template "breadcrumbnav" (dict "p1" . "p2" .) }}
</ol>
</nav>
{{ define "breadcrumbnav" }}
{{ if .p1.Parent }}
{{ template "breadcrumbnav" (dict "p1" .p1.Parent "p2" .p2 ) }}
{{ else if not .p1.IsHome }}
{{ template "breadcrumbnav" (dict "p1" .p1.Site.Home "p2" .p2 ) }}
{{ end }}
<li{{ if eq .p1 .p2 }} class="current"{{ end }}>
{{ if .p1.IsHome }}/{{ end }}
<a href="{{ .p1.RelPermalink }}">{{ if .p1.IsHome }}{{ .p1.Site.Title }}{{ else }}{{ .p1.Title }}{{ end }}</a>
{{ if ne .p1 .p2 }}/{{ end }}
</li>
{{ end }}
+21
View File
@@ -0,0 +1,21 @@
{{- $duration := "7000" -}}
{{- $content := $ -}}
{{- $imageListPattern := `<ul>\s*(?:<li><figure>\s*<img[^>]*/?>\s*</figure>\s*</li>\s*)+</ul>` -}}
{{- $imageLists := findRE $imageListPattern $content -}}
{{- range $index,$il := $imageLists -}}
{{- $listItems := findRE `<li><figure>\s*<img[^>]*/?>\s*</figure>\s*</li>` $il -}}
{{- $items := `<ul>` -}}
{{- range $i,$li := $listItems -}}
{{- $img := index (findRE `<img[^>]*/?>` $li) 0 -}}
{{- $items = print $items `<li id="c` $index `_slide` $i `">` $img `</li>` -}}
{{- end -}}
{{- $items = print $items `</ul>` -}}
{{- $indicators := `<ol>` -}}
{{- range $i,$li := $listItems -}}
{{- $indicators = print $indicators `<li><a href="#c` $index `_slide` $i `"></a></li>` -}}
{{- end -}}
{{- $indicators = print $indicators `</ol>` -}}
{{- $replacement := print `<div id="carousel_` $index `" class="auto-carousel" duration="` $duration `">` $items $indicators `<div class="prev">&lsaquo;</div><div class="next">&rsaquo;</div></div>` -}}
{{- $content = replace $content $il $replacement -}}
{{- end -}}
{{- $content | safeHTML -}}
+51
View File
@@ -0,0 +1,51 @@
{{- /*
Renders a menu for the given menu ID.
@context {page} page The current page.
@context {string} menuID The menu ID.
@example: {{ partial "menu.html" (dict "menuID" "main" "page" .) }}
*/}}
{{- $page := .page }}
{{- $menuID := .menuID }}
{{- with index site.Menus $menuID }}
<nav aria-label="Main navigation">
<ul>
{{- partial "inline/menu/walk.html" (dict "page" $page "menuEntries" .) }}
</ul>
</nav>
{{- end }}
{{- define "_partials/inline/menu/walk.html" }}
{{- $page := .page }}
{{- range .menuEntries }}
{{- $attrs := dict "href" .URL }}
{{- if $page.IsMenuCurrent .Menu . }}
{{- $attrs = merge $attrs (dict "class" "active" "aria-current" "page") }}
{{- else if $page.HasMenuCurrent .Menu .}}
{{- $attrs = merge $attrs (dict "class" "ancestor" "aria-current" "true") }}
{{- end }}
{{- $name := .Name }}
{{- with .Identifier }}
{{- with T . }}
{{- $name = . }}
{{- end }}
{{- end }}
<li>
<a
{{- range $k, $v := $attrs }}
{{- with $v }}
{{- printf " %s=%q" $k $v | safeHTMLAttr }}
{{- end }}
{{- end -}}
>{{ $name }}</a>
{{- with .Children }}
<ul>
{{- partial "inline/menu/walk.html" (dict "page" $page "menuEntries" .) }}
</ul>
{{- end }}
</li>
{{- end }}
{{- end }}
+22
View File
@@ -0,0 +1,22 @@
{{- /*
For a given taxonomy, renders a list of terms assigned to the page.
@context {page} page The current page.
@context {string} taxonomy The taxonomy.
@example: {{ partial "terms.html" (dict "taxonomy" "tags" "page" .) }}
*/}}
{{- $page := .page }}
{{- $taxonomy := .taxonomy }}
{{- with $page.GetTerms $taxonomy }}
{{- $label := (index . 0).Parent.LinkTitle }}
<div class="terms-list">
<ul>
{{- range . }}
<li><a href="{{ .RelPermalink }}">#{{ .LinkTitle }}</a></li>
{{- end }}
</ul>
</div>
{{- end }}