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
+20
View File
@@ -0,0 +1,20 @@
# Hugo
/public/
/resources/
.hugo_build.lock
hugo_stats.json
# macOS
.DS_Store
**/.DS_Store
# Editors
.vscode/
.idea/
*.swp
*.swo
# Local secrets / overrides
.env
.env.local
hugo.local.yaml
+620
View File
@@ -0,0 +1,620 @@
/* ========================================================================
OPENBUREAU — site-level overrides on top of shibui.
Differentiation from kgva.ch: serif body, strong wordmark, wider column,
editorial journal entries (not portfolio cards), more breathing room,
no 8bit cursor.
======================================================================== */
@import url('https://fonts.bunny.net/css?family=newsreader:400,400i,500,500i,600,600i|ibm-plex-mono:400,500|space-grotesk:400,500,700|inter:400,500,600');
:root {
/* Typography — editorial:
serif body, display for headings/nav, sans (Inter) for listings/labels,
mono kept narrow (code, dates, technical stamps). */
--font-family-serif: 'Newsreader', Charter, 'Source Serif Pro', Georgia, serif;
--font-family-sans: 'Inter', system-ui, -apple-system, sans-serif;
--font-family-mono: 'IBM Plex Mono', 'Courier New', ui-monospace, monospace;
--font-family-display: 'Space Grotesk', 'Inter', system-ui, sans-serif;
--font-family-mono-shibui: var(--font-family-mono);
--font-size-base: 1.0625rem;
--font-size-small: 0.875rem;
--font-size-code: 0.92rem;
--spacing-base: 1.7em;
--spacing-xs: calc(var(--spacing-base) * 0.25);
--spacing-sm: calc(var(--spacing-base) * 0.5);
--spacing-md: var(--spacing-base);
--spacing-lg: calc(var(--spacing-base) * 2);
--spacing-xl: calc(var(--spacing-base) * 3);
--container-width: 72ch;
/* Off-white paper + warm ink */
--bg-h: 35;
--bg-s: 14%;
--bg-l: 96%;
--color-bg-primary: hsl(var(--bg-h) var(--bg-s) var(--bg-l));
--color-bg-secondary: hsl(var(--bg-h) var(--bg-s) calc(var(--bg-l) - 3%));
--color-border: hsl(var(--bg-h) var(--bg-s) calc(var(--bg-l) - 12%));
--color-text-primary: hsl(25 18% 10%);
--color-text-muted: hsl(25 8% 38%);
--color-text-code: hsl(25 10% 22%);
--accent: #b54a2c;
--accent-soft: #d97a5a;
color-scheme: light;
}
/* ------------------------------------------------------------------------
Base body — serif by default (editorial)
------------------------------------------------------------------------ */
body {
font-family: var(--font-family-serif);
font-size: var(--font-size-base);
line-height: 1.55;
padding: var(--spacing-sm) 1.75rem var(--spacing-xl);
gap: var(--spacing-sm);
display: grid;
grid-template-rows: auto 1fr auto;
grid-template-columns: minmax(auto, var(--container-width));
justify-content: center;
}
p { margin: var(--spacing-sm) 0; }
h1, h2, h3, h4, h5 { font-family: var(--font-family-display); font-weight: 600; }
/* Disable shibui's nested h2/h3/h4 counters everywhere on this site */
h2::before, h3::before, h4::before, h5::before { content: none !important; }
code, pre, kbd, samp { font-family: var(--font-family-mono); }
code { font-size: var(--font-size-code); }
a {
color: var(--color-text-primary);
border-bottom: 1px solid var(--color-border);
text-decoration: none;
margin-left: 0;
}
a:hover {
color: var(--accent);
border-bottom-color: var(--accent);
}
/* ------------------------------------------------------------------------
Site header — strong wordmark, not breadcrumb-first
------------------------------------------------------------------------ */
/* 2-column header: wordmark left edge of content | nav right edge of content.
Center-aligned vertically so wordmark and nav read as one masthead line. */
.site-header {
display: grid;
grid-template-columns: auto 1fr;
align-items: center;
column-gap: 2rem;
row-gap: 0.6rem;
padding-bottom: var(--spacing-sm);
border-bottom: 1px solid var(--color-border);
}
.wordmark-link {
border: none;
margin: 0;
padding: 0;
display: inline-block;
grid-column: 1;
justify-self: start;
font-family: var(--font-family-display);
font-weight: 700;
font-size: clamp(1.2rem, 2.4vw, 1.5rem);
letter-spacing: -0.01em;
line-height: 1;
color: var(--color-text-primary);
}
.wordmark-link:hover,
.wordmark-link:focus { color: var(--color-text-primary); border: none; }
.site-header .site-nav {
grid-column: 2;
justify-self: end;
}
/* Mobile: stack */
@media (max-width: 720px) {
.site-header {
grid-template-columns: 1fr;
align-items: start;
row-gap: 0.4rem;
}
.wordmark-link,
.site-header .site-nav {
grid-column: 1;
justify-self: start;
}
}
.wordmark-link:focus-visible { outline: 2px dotted var(--color-text-muted); outline-offset: 4px; }
.wordmark-tagline {
font-family: var(--font-family-serif);
font-style: italic;
font-size: var(--font-size-small);
color: var(--color-text-muted);
margin: 0;
max-width: 32ch;
line-height: 1.35;
text-align: right;
}
@media (max-width: 720px) {
.wordmark-tagline { text-align: left; max-width: 38ch; }
}
/* Site nav — horizontal, lowercase-strong, mono */
.site-nav nav {
margin: 0;
}
.site-nav ul {
list-style: none;
margin-left: 0;
display: flex;
flex-wrap: wrap;
gap: 0.25rem 1.4rem;
font-family: var(--font-family-display);
font-size: 0.9rem;
font-weight: 500;
letter-spacing: 0.02em;
}
.site-nav li { margin: 0; }
.site-nav a { border: none; }
.site-nav a.active,
.site-nav a.ancestor {
color: var(--accent);
}
/* Breadcrumb (only on non-home pages) */
.path-nav {
font-family: var(--font-family-mono);
font-size: var(--font-size-small);
color: var(--color-text-muted);
text-transform: uppercase;
letter-spacing: 0.03em;
}
.path-nav ol {
list-style: none;
margin-left: 0;
display: flex;
flex-wrap: wrap;
gap: 0.2rem;
}
.path-nav li { margin: 0; }
.path-nav a {
border: none;
color: var(--color-text-muted);
}
.path-nav a:hover { color: var(--accent); }
.path-nav li.current a { color: var(--color-text-primary); }
/* ------------------------------------------------------------------------
Journal (home) — editorial entries, not portfolio cards
------------------------------------------------------------------------ */
.journal-header {
margin-top: var(--spacing-md);
margin-bottom: var(--spacing-md);
}
.journal-header h2 {
font-size: 0.95rem;
font-family: var(--font-family-mono);
font-weight: 500;
letter-spacing: 0.05em;
color: var(--color-text-muted);
margin: 0 0 0.25rem;
}
.journal-header p {
margin: 0;
font-style: italic;
}
.journal-list {
list-style: none;
margin-left: 0;
display: flex;
flex-direction: column;
gap: 0;
}
.journal-entry {
border-top: 1px solid var(--color-border);
padding: 1rem 0 1.1rem;
}
.journal-entry:last-child { border-bottom: 1px solid var(--color-border); }
/* Grid with explicit row-gap: bulletproof against margin/line-height inheritance */
.journal-entry-link {
display: grid;
grid-auto-flow: row;
row-gap: 0.45rem;
border: none;
margin: 0;
padding: 0;
}
.journal-entry-link > * { margin: 0; }
.journal-entry-link:hover .journal-title { color: var(--accent); }
.journal-meta {
font-family: var(--font-family-mono);
font-size: var(--font-size-small);
line-height: 1.3;
color: var(--color-text-muted);
display: flex;
gap: 0.8rem;
align-items: baseline;
}
.journal-section { color: var(--accent); font-weight: 500; }
.journal-date { font-variant-numeric: tabular-nums; }
.journal-title {
font-family: var(--font-family-display);
font-size: 1.2rem;
font-weight: 600;
letter-spacing: -0.012em;
line-height: 1.2;
color: var(--color-text-primary);
transition: color 0.15s ease;
}
.journal-summary {
font-family: var(--font-family-serif);
font-size: var(--font-size-base);
line-height: 1.45;
color: var(--color-text-primary);
max-width: 60ch;
}
.journal-tags {
list-style: none;
display: flex;
flex-wrap: wrap;
gap: 0.6rem;
font-family: var(--font-family-sans);
font-size: 0.78rem;
line-height: 1.2;
color: var(--color-text-muted);
padding: 0;
}
.journal-tags li { margin: 0; }
.more {
margin-top: var(--spacing-md);
font-family: var(--font-family-mono);
font-size: var(--font-size-small);
}
/* ------------------------------------------------------------------------
Library — Atlas view
------------------------------------------------------------------------ */
.atlas {
display: flex;
flex-direction: column;
gap: var(--spacing-lg);
margin-top: var(--spacing-md);
}
.atlas-section h2,
.atlas-tags h2 {
font-family: var(--font-family-display);
font-size: 1.15rem;
font-weight: 600;
letter-spacing: -0.005em;
color: var(--color-text-primary);
border-bottom: 1px solid var(--color-border);
padding-bottom: var(--spacing-xs);
margin-bottom: var(--spacing-sm);
}
.atlas-section h2 a,
.atlas-tags h2 a { border: none; color: inherit; }
.atlas-section h2 a:hover { color: var(--accent); }
.atlas-section > p { font-style: italic; color: var(--color-text-muted); margin-bottom: var(--spacing-sm); }
.atlas-list {
list-style: none;
margin-left: 0;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.atlas-list li {
display: flex;
gap: 0.6rem;
align-items: baseline;
flex-wrap: wrap;
}
.atlas-list .list-meta { font-family: var(--font-family-mono); font-size: 0.78rem; }
.tag-cloud {
list-style: none;
margin-left: 0;
display: flex;
flex-wrap: wrap;
gap: 0.5rem 1.1rem;
font-family: var(--font-family-sans);
font-size: var(--font-size-small);
}
.tag-cloud li { margin: 0; }
.tag-cloud a { border: none; }
/* ------------------------------------------------------------------------
Library subsection (chronological list)
------------------------------------------------------------------------ */
.time-list ul {
list-style: none;
margin-left: 0;
}
.list-item {
border-top: 1px solid var(--color-border);
padding: var(--spacing-sm) 0;
}
.list-item:last-child { border-bottom: 1px solid var(--color-border); }
.list-title-row {
display: flex;
justify-content: space-between;
align-items: baseline;
gap: 1rem;
}
.list-title {
margin: 0;
font-family: var(--font-family-display);
font-size: 1.15rem;
font-weight: 500;
flex: 1;
}
.list-title a { border: none; }
.list-meta {
font-family: var(--font-family-mono);
font-size: 0.8rem;
color: var(--color-text-muted);
white-space: nowrap;
}
.list-summary {
margin-top: 0.3rem;
font-style: italic;
line-height: 1.45;
}
/* ------------------------------------------------------------------------
Software showcase
------------------------------------------------------------------------ */
.software-showcase {
display: flex;
flex-direction: column;
gap: var(--spacing-lg);
margin-top: var(--spacing-md);
}
.software-item {
border-top: 1px solid var(--color-border);
padding-top: var(--spacing-md);
}
.software-item:last-child { border-bottom: 1px solid var(--color-border); padding-bottom: var(--spacing-md); }
.software-item h2 {
font-family: var(--font-family-display);
font-size: 1.8rem;
letter-spacing: -0.015em;
margin: 0 0 0.4rem;
}
.software-item h2 a { border: none; }
.software-summary {
margin: 0 0 0.5rem;
font-style: italic;
color: var(--color-text-primary);
max-width: 55ch;
}
.software-meta {
font-family: var(--font-family-mono);
font-size: var(--font-size-small);
display: flex;
gap: 0.4rem;
align-items: baseline;
flex-wrap: wrap;
}
.software-external {
color: var(--accent);
border-bottom-color: var(--accent);
}
/* ------------------------------------------------------------------------
Single page (article)
------------------------------------------------------------------------ */
.single { margin-top: var(--spacing-md); }
.single-header { margin-bottom: var(--spacing-md); }
.single-header h1 {
font-family: var(--font-family-display);
font-size: clamp(1.2rem, 2.4vw, 1.5rem);
font-weight: 700;
letter-spacing: -0.01em;
line-height: 1.15;
margin: 0 0 var(--spacing-xs);
}
.single-summary {
font-family: var(--font-family-serif);
font-style: italic;
font-size: 1.02rem;
line-height: 1.45;
margin: 0;
max-width: 55ch;
}
.single-content h2 {
font-family: var(--font-family-display);
font-size: 1.15rem;
font-weight: 600;
margin-top: var(--spacing-md);
margin-bottom: var(--spacing-xs);
}
.single-content h3 {
font-family: var(--font-family-display);
font-size: 1rem;
font-weight: 600;
margin-top: var(--spacing-sm);
margin-bottom: var(--spacing-xs);
}
.single-content blockquote {
border-left: 2px solid var(--accent);
padding-left: 1rem;
margin: var(--spacing-md) 0;
font-style: italic;
color: var(--color-text-muted);
}
.single-content ul, .single-content ol { margin: var(--spacing-sm) 0 var(--spacing-sm) 1.6rem; }
.single-content li { margin: 0.3rem 0; }
.toc {
font-family: var(--font-family-sans);
font-size: var(--font-size-small);
line-height: 1.45;
border-left: 2px solid var(--color-border);
padding: var(--spacing-xs) 0 var(--spacing-xs) var(--spacing-sm);
margin-bottom: var(--spacing-lg);
color: var(--color-text-muted);
}
.toc strong {
color: var(--color-text-primary);
display: block;
margin-bottom: 0.4rem;
font-size: 0.78rem;
font-weight: 600;
letter-spacing: 0.02em;
}
.toc a { border: none; color: var(--color-text-muted); }
.toc a:hover { color: var(--accent); }
.toc ul, .toc ol { margin: 0 0 0 1.2rem; padding: 0; }
.toc li { margin: 0.15rem 0; }
.time {
font-family: var(--font-family-mono);
font-size: var(--font-size-small);
color: var(--color-text-muted);
margin-top: var(--spacing-lg);
padding-top: var(--spacing-sm);
border-top: 1px solid var(--color-border);
}
.back-nav-wrap {
margin-top: var(--spacing-md);
font-family: var(--font-family-mono);
font-size: var(--font-size-small);
}
.back-link { border: none; color: var(--color-text-muted); }
.back-link:hover { color: var(--accent); }
/* ------------------------------------------------------------------------
Page foot breadcrumb (moved from top → bottom)
------------------------------------------------------------------------ */
.page-foot-nav {
margin-top: var(--spacing-lg);
padding-top: var(--spacing-sm);
border-top: 1px solid var(--color-border);
font-family: var(--font-family-mono);
font-size: var(--font-size-small);
color: var(--color-text-muted);
text-transform: uppercase;
letter-spacing: 0.03em;
}
.page-foot-nav ol {
list-style: none;
margin-left: 0;
display: flex;
flex-wrap: wrap;
gap: 0.2rem;
}
.page-foot-nav li { margin: 0; }
.page-foot-nav a { border: none; color: var(--color-text-muted); }
.page-foot-nav a:hover { color: var(--accent); }
.page-foot-nav li.current a { color: var(--color-text-primary); }
/* ------------------------------------------------------------------------
Footer — mirrors the header rhythm: 2-col grid, baseline-aligned,
wordmark left, nav right; below: tagline left, credit right.
Text left-aligned everywhere (no centering).
------------------------------------------------------------------------ */
footer {
margin-top: var(--spacing-xl);
padding-top: var(--spacing-md);
border-top: 1px solid var(--color-border);
color: var(--color-text-muted);
}
footer a { color: var(--color-text-muted); border: none; }
footer a:hover { color: var(--accent); }
footer p { text-align: left; margin: 0; }
.footer-grid {
display: grid;
grid-template-columns: auto 1fr;
align-items: baseline;
column-gap: 2rem;
row-gap: var(--spacing-sm);
}
.footer-mark {
grid-column: 1;
font-family: var(--font-family-display);
font-weight: 600;
font-size: 0.95rem;
letter-spacing: 0.01em;
color: var(--color-text-primary);
}
.footer-nav {
grid-column: 2;
justify-self: end;
}
.footer-nav ul {
list-style: none;
margin-left: 0;
display: flex;
flex-wrap: wrap;
gap: 0.4rem 1.2rem;
font-family: var(--font-family-display);
font-size: 0.85rem;
font-weight: 500;
letter-spacing: 0.01em;
}
.footer-nav li { margin: 0; }
.footer-tagline {
grid-column: 1;
font-family: var(--font-family-serif);
font-style: italic;
font-size: var(--font-size-small);
line-height: 1.4;
max-width: 36ch;
}
.footer-credit {
grid-column: 2;
justify-self: end;
font-family: var(--font-family-mono);
font-size: 0.78rem;
}
/* Mobile: stack everything left */
@media (max-width: 720px) {
.footer-grid { grid-template-columns: 1fr; }
.footer-mark,
.footer-nav,
.footer-tagline,
.footer-credit {
grid-column: 1;
justify-self: start;
}
}
/* ------------------------------------------------------------------------
Images — keep grayscale-on-hover but no portfolio aspect-ratio crop
------------------------------------------------------------------------ */
img {
filter: grayscale(100%);
transition: filter 0.4s ease;
max-width: 100%;
height: auto;
margin: var(--spacing-sm) 0;
}
img:hover { filter: grayscale(0%); }
+7
View File
@@ -0,0 +1,7 @@
---
title: "OPENBUREAU"
description: "Eine offene Plattform für Architekturpraxis, Theorie und Werkzeuge."
---
Ein offenes Architekturbüro — Sammlung, Plattform, Praxis.
Hier wachsen Texte, Werkzeuge und Gespräche über das Machen von Architektur.
+24
View File
@@ -0,0 +1,24 @@
---
title: "Colophon"
date: 2026-05-23
toc: false
---
OPENBUREAU wird von **Karim Gabriele Varano** geführt.
Mitwirkende werden auf dieser Seite namentlich aufgeführt — sobald sie da sind.
## Technik
- Site generiert mit [Hugo](https://gohugo.io), Theme abgeleitet von [Shibui](https://github.com/ntk148v/shibui).
- Code in eigener [Gitea](https://gitea.kgva.ch)-Instanz.
- Diskussion in eigener [Flarum](https://openstudio.kgva.ch)-Instanz.
- Selbst-gehostet auf eigener Infrastruktur.
## Lizenz
Texte: [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) — geteilt unter gleicher Lizenz.
Code: jeweils im Repository ausgewiesen (überwiegend MIT/AGPL).
## Kontakt
[karim@gabrielevarano.ch](mailto:karim@gabrielevarano.ch)
+7
View File
@@ -0,0 +1,7 @@
---
title: "Library"
description: "Die Bibliothek von OPENBUREAU — Texte, Notizen, Recherchen."
---
Die Library ist die Sammlung. Alles, was gelesen und geschrieben wird, lebt hier.
Texte werden thematisch organisiert; das **Journal** auf der Startseite zeigt dieselben Inhalte chronologisch.
+4
View File
@@ -0,0 +1,4 @@
---
title: "Büroführung"
description: "Wie wir ein offenes Architekturbüro organisieren — Honorare, Verträge, Werkzeuge, Workflow."
---
@@ -0,0 +1,20 @@
---
title: "Warum ein offenes Büro"
date: 2026-05-22
tags: ["büroführung", "praxis", "open-source"]
summary: "Ein offenes Architekturbüro ist nicht karitativ — es ist praktischer, robuster, ehrlicher."
---
Architektur ist traditionell ein geschlossener Beruf. Wettbewerbe sind nicht-öffentlich, Verträge nicht-verhandelbar, Werkzeuge proprietär. Wissen wird gehortet, weil es als Wettbewerbsvorteil verstanden wird.
Wir bezweifeln, dass das stimmt.
**Offen** zu arbeiten heisst hier dreierlei:
1. **Texte und Notizen sind öffentlich.** Wer mitlesen will, kann das.
2. **Werkzeuge sind frei.** Programme wie DOSSIER und RAPPORT sind quelloffen, andere dürfen sie nutzen, anpassen, weiterbauen.
3. **Verträge und Honorare sind dokumentiert.** Wir zeigen, wie ein offenes Büro sich finanziert, ohne das gegen sich zu drehen.
Das ist kein karitatives Projekt. Es ist eine Wette darauf, dass **Offenheit produktiver ist als Abschottung** — für die Praxis, für die Kollegen, für die Disziplin.
— mehr dazu in den folgenden Notizen.
+6
View File
@@ -0,0 +1,6 @@
---
title: "Software"
description: "Was wir bauen, was wir benutzen — und warum."
---
Eigene Programme wie **DOSSIER** und **RAPPORT**, der **empfohlene Stack** für ein offenes Architekturbüro, und Texte über Werkzeuge.
+13
View File
@@ -0,0 +1,13 @@
---
title: "DOSSIER"
date: 2026-05-23
weight: 10
tags: ["software", "eigene-werkzeuge"]
summary: "Projektorganisation für ein Architekturbüro — von der Anfrage zur Übergabe."
external: "https://dossier.gabrielevarano.ch"
---
**DOSSIER** ist die Projektverwaltung von OPENBUREAU.
Eine Anwendung, die ein Projekt von der ersten Anfrage bis zur Schlussrechnung begleitet — strukturiert, nachvollziehbar, offen.
→ [dossier.gabrielevarano.ch](https://dossier.gabrielevarano.ch)
@@ -0,0 +1,15 @@
---
title: "Warum wir eigene Werkzeuge bauen"
date: 2026-05-21
tags: ["software", "praxis", "tools"]
summary: "DOSSIER und RAPPORT sind keine Lifestyle-Projekte — sie sind Reaktion auf konkrete Lücken."
---
Wir benutzen viel fremde Software. Aber an zwei Stellen kam keiner der existierenden Werkzeuge an die Praxis heran, die wir brauchten — also haben wir selbst gebaut.
- **[DOSSIER](https://dossier.gabrielevarano.ch)** organisiert Projekte: von der ersten Anfrage über Vertrag, Planstände, Rechnungen bis zur Übergabe. Strukturiert genug, um nichts zu verlieren; offen genug, um nicht im Weg zu stehen.
- **[RAPPORT](https://rapport.gabrielevarano.ch)** hält Stunden, Wege und Aufwand fest. Nicht als Überwachung, sondern als Material für die nächste Honoraroffert.
Beide sind quelloffen. Beide sind im Aufbau. Beide sind so geschrieben, dass ein anderes Büro sie morgen für sich nutzen könnte.
Eine vollständige Liste der **anderen** Werkzeuge, die wir empfehlen, findet sich unter [Werkzeuge](/library/werkzeuge).
+13
View File
@@ -0,0 +1,13 @@
---
title: "RAPPORT"
date: 2026-05-23
weight: 20
tags: ["software", "eigene-werkzeuge"]
summary: "Zeit- und Aufwandserfassung für ein Architekturbüro."
external: "https://rapport.gabrielevarano.ch"
---
**RAPPORT** erfasst Stunden, Wege und Aufwand.
Material für transparente Honorarofferten, nicht für Überwachung.
→ [rapport.gabrielevarano.ch](https://rapport.gabrielevarano.ch)
+32
View File
@@ -0,0 +1,32 @@
---
title: "Empfohlener Stack"
date: 2026-05-23
weight: 30
tags: ["software", "stack", "empfehlungen"]
summary: "Programme und Dienste, die wir Tag für Tag benutzen — kuratiert und kommentiert."
---
Diese Liste wächst. Sie ist kein Inventar, sondern eine **Position**: jedes Werkzeug steht für eine Entscheidung über die Arbeitsweise des Büros.
## Zeichnen & Modellieren
- **Rhino + Grasshopper** — das Rückgrat des geometrischen Arbeitens. Wir entwickeln eigene Plugins ([Rhino-Panel](https://gitea.kgva.ch)).
- **Blender** — für freie 3D-Arbeiten, Visualisierungen, Animation.
## Schreiben & Dokumentieren
- **Markdown + Hugo** — alle Texte, auch diese hier, sind `.md` und werden statisch generiert.
- **Zotero** — Literatur und Quellen.
## Koordination
- **Gitea** (selbst-gehostet) — Code, Texte, Repositories.
- **Flarum** (selbst-gehostet) — Diskussion und Gespräch.
- **Nextcloud** (selbst-gehostet) — Dateien, Kalender, Kontakte.
## Eigene Werkzeuge
- **[DOSSIER](/library/software/dossier/)** — Projektverwaltung.
- **[RAPPORT](/library/software/rapport/)** — Stundenerfassung.
— diese Seite wird laufend erweitert.
+4
View File
@@ -0,0 +1,4 @@
---
title: "Theorie"
description: "Architekturtheorie, Lektüren, Begriffe."
---
@@ -0,0 +1,14 @@
---
title: "Typologie als Werkzeug"
date: 2026-05-20
tags: ["theorie", "typologie", "methode"]
summary: "Erste Notiz über Typologie nicht als Katalog, sondern als operatives Werkzeug im Entwurf."
---
Typologie wird oft als Inhaltsverzeichnis missverstanden: eine Liste von Grundrissen, sortiert nach Funktion. Diese Lesart ist bequem und falsch.
Eine Typologie ist ein **Denkwerkzeug**. Sie macht das Wiederkehrende sichtbar und damit das Besondere argumentierbar. Erst wenn wir das Übliche kennen, können wir das Eigentümliche begründen.
Im Entwurf bedeutet das: nicht "welcher Typ ist das?", sondern "**gegen welchen Typ** entwerfen wir hier?".
— ein erster Notiz, weitere folgen.
+15
View File
@@ -0,0 +1,15 @@
---
title: "Manifest"
date: 2026-05-23
toc: false
---
OPENBUREAU ist der Versuch, ein Architekturbüro **offen** zu führen.
Offen heisst: was wir lernen, schreiben und bauen, gehört nicht in eine Schublade.
Texte werden in einer Bibliothek gesammelt. Werkzeuge — Programme wie **DOSSIER** und **RAPPORT** — entstehen unter freier Lizenz und stehen jedem zur Verfügung. Gespräche finden im Forum statt, Code lebt in einem öffentlichen Repository.
Wir glauben, dass ein Büro mehr ist als seine Projekte.
Es ist eine Praxis: eine Art zu lesen, zu zeichnen, zu organisieren, miteinander zu denken. Diese Praxis sichtbar zu machen ist die Aufgabe dieser Seite.
OPENBUREAU ist kein fertiges Produkt. Es ist eine **Werkstatt** im Aufbau.
+95
View File
@@ -0,0 +1,95 @@
# OPENBUREAU — Hugo site configuration
baseURL: "https://openbureau.ch/"
title: "OPENBUREAU"
theme: "openbureau"
enableRobotsTXT: true
enableGitInfo: false
hasCJKLanguage: false
defaultContentLanguage: de
defaultContentLanguageInSubdir: false
languages:
de:
label: Deutsch
locale: de_CH
weight: 1
title: OPENBUREAU
params:
description: "Eine offene Plattform für Architekturpraxis, Theorie und Werkzeuge."
# en:
# label: English
# locale: en
# weight: 2
# title: OPENBUREAU
# it:
# label: Italiano
# locale: it
# weight: 3
# title: OPENBUREAU
module:
hugoVersion:
extended: false
min: "0.146.0"
markup:
goldmark:
renderer:
unsafe: true
highlight:
noClasses: true
style: algol_nu
codeFences: true
guessSyntax: true
lineNos: false
tabWidth: 4
tableOfContents:
startLevel: 2
endLevel: 4
ordered: false
taxonomies:
tag: tags
outputs:
home: [html, rss]
page: [html]
section: [html, rss]
pagination:
pagerSize: 20
menus:
main:
- name: JOURNAL
pageRef: /
weight: 10
- name: LIBRARY
pageRef: /library
weight: 20
- name: MANIFEST
pageRef: /manifest
weight: 30
- name: FORUM
url: https://openstudio.kgva.ch
weight: 40
- name: CODE
url: https://gitea.kgva.ch
weight: 50
params:
author:
name: "Karim Gabriele Varano"
email: "karim@gabrielevarano.ch"
showreadingtime: true
showlastmod: true
comments: false
# Accent for OPENBUREAU
accent: "#b54a2c"
frontmatter:
date: ['date', 'publishDate', 'lastmod']
lastmod: [':git', 'lastmod', 'date', 'publishDate']
publishDate: ['publishDate', 'date']
+25
View File
@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html
lang="{{ site.Language.Locale | default site.Language.Lang }}"
dir="{{ or site.Language.Direction `ltr` }}"
>
<head>
{{ partial "head.html" . }}
</head>
<body>
<a href="#main-content" class="skip-link">Skip to content</a>
<header role="banner" class="site-header">
<a href="{{ "/" | relURL }}" class="wordmark-link">OPENBUREAU</a>
<nav class="site-nav" aria-label="Site">
{{ partial "menu.html" (dict "menuID" "main" "page" .) }}
</nav>
</header>
<main id="main-content" role="main">{{ block "main" . }}{{ end }}</main>
{{ if not .IsHome }}
<nav class="page-foot-nav" aria-label="Breadcrumb">
{{ partial "header.html" . }}
</nav>
{{ end }}
<footer role="contentinfo">{{ partial "footer.html" . }}</footer>
</body>
</html>
+41
View File
@@ -0,0 +1,41 @@
{{ define "main" }}
{{ partial "terms.html" (dict "taxonomy" "tags" "page" .) }}
<article class="single">
<header class="single-header">
<h1>{{ .Title }}</h1>
{{ with .Params.summary }}
<p class="single-summary text-muted">{{ . }}</p>
{{ end }}
</header>
{{/* Table of Contents */}}
{{ $hasToC := .Params.toc | default true }}
{{ $headers := findRE "<h[2-6]" .Content }}
{{ if and $hasToC (ge (len $headers) 1) }}
<nav class="toc">
<strong>Inhalt</strong>
<div class="toc-content">{{ .TableOfContents }}</div>
</nav>
{{ end }}
<div class="single-content">
{{ .Content }}
</div>
<div class="time">
{{ partial "date.html" .Date }}
{{ $showReadingTime := .Params.showreadingtime | default site.Params.showreadingtime | default true }}
{{ if and $showReadingTime .ReadingTime }}
<span class="reading-time"> · {{ .ReadingTime }} min Lesezeit</span>
{{ end }}
{{ $showLastMod := .Params.showlastmod | default site.Params.showlastmod | default false }}
{{ if and $showLastMod .Lastmod }}
{{ if ne (.Lastmod.Format "2006-01-02") (.Date.Format "2006-01-02") }}
<span class="lastmod"> · Zuletzt aktualisiert: {{ .Lastmod.Format "2006-01-02" }}</span>
{{ end }}
{{ end }}
</div>
</article>
{{ end }}
+17
View File
@@ -0,0 +1,17 @@
<div class="footer-grid">
<div class="footer-mark">OPENBUREAU</div>
<nav class="footer-nav" aria-label="Footer">
<ul>
<li><a href="/manifest/">Manifest</a></li>
<li><a href="/colophon/">Colophon</a></li>
<li><a href="https://openstudio.kgva.ch">Forum ↗</a></li>
<li><a href="https://gitea.kgva.ch">Code ↗</a></li>
<li><a href="/index.xml">RSS</a></li>
</ul>
</nav>
<p class="footer-tagline">
Sammlung, Plattform, Praxis.<br>
Texte unter <a href="https://creativecommons.org/licenses/by-sa/4.0/">CC BY-SA 4.0</a>.
</p>
<p class="footer-credit">© {{ now.Year }} · Karim Gabriele Varano</p>
</div>
+49
View File
@@ -0,0 +1,49 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="color-scheme" content="light" />
<title>{{ if .IsHome }}{{ site.Title | upper }}{{ else }}{{ printf "%s | %s" (.Title | upper) (site.Title | upper) }}{{ end }}</title>
{{ with .Description | default site.Params.description }}<meta name="description" content="{{ . }}" />{{ end }}
<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 }}
{{/* RSS */}}
{{ with .OutputFormats.Get "rss" -}}
{{ printf `<link rel="%s" type="%s" href="%s" title="%s" />` .Rel .MediaType.Type .Permalink site.Title | safeHTML }}
{{ end }}
{{/* JSON-LD 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" . }}
+41
View File
@@ -0,0 +1,41 @@
{{ define "main" }}
{{ .Content }}
{{ $library := where site.RegularPages "Section" "library" }}
{{ $journal := first 20 $library.ByDate.Reverse }}
<section class="journal" aria-label="Journal — neueste Beiträge">
<header class="journal-header">
<h2>Journal</h2>
<p class="text-muted">Was zuletzt geschrieben wurde.</p>
</header>
<ol class="journal-list">
{{ range $journal }}
<li class="journal-entry">
<a class="journal-entry-link" href="{{ .RelPermalink }}">
<div class="journal-meta">
{{ with .Parent }}
<span class="journal-section">{{ .Title }}</span>
{{ end }}
<time class="journal-date" datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date.Format "2006-01-02" }}</time>
</div>
<h3 class="journal-title">{{ .LinkTitle }}</h3>
{{ with .Params.summary }}
<p class="journal-summary">{{ . }}</p>
{{ end }}
{{ with .Params.tags }}
<ul class="journal-tags">
{{ range . }}<li>#{{ . }}</li>{{ end }}
</ul>
{{ end }}
</a>
</li>
{{ end }}
</ol>
{{ if gt (len $library) 20 }}
<p class="more"><a href="/library/">→ Alle Beiträge in der Library</a></p>
{{ end }}
</section>
{{ end }}
+59
View File
@@ -0,0 +1,59 @@
{{ define "main" }}
{{ .Content }}
{{ if .IsSection }}
{{ if eq .Path "/library" }}
{{/* Library root: Atlas — gruppiert nach Untersection */}}
<section class="atlas">
{{ range .Sections.ByWeight }}
<article class="atlas-section">
<h2><a href="{{ .RelPermalink }}">{{ .Title }}</a></h2>
{{ with .Params.description }}<p class="text-muted">{{ . }}</p>{{ end }}
<ul class="atlas-list">
{{ range first 6 .RegularPages.ByDate.Reverse }}
<li>
<a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a>
<span class="list-meta text-muted"> · {{ partial "date.html" .Date }}</span>
</li>
{{ end }}
</ul>
{{ if gt (len .RegularPages) 6 }}
<p class="more"><a href="{{ .RelPermalink }}">alle in {{ .Title }} →</a></p>
{{ end }}
</article>
{{ end }}
{{/* Tag-Cloud */}}
{{ with site.Taxonomies.tags }}
<article class="atlas-tags">
<h2>Tags</h2>
<ul class="tag-cloud">
{{ range $name, $taxonomy := . }}
<li><a href="/tags/{{ $name | urlize }}/">{{ $name }} <span class="text-muted">({{ len $taxonomy }})</span></a></li>
{{ end }}
</ul>
</article>
{{ end }}
</section>
{{ else }}
{{/* Library subsection: chronologisch */}}
<div class="time-list">
<ul>
{{ range .RegularPages.ByDate.Reverse }}
<li class="list-item">
<div class="list-title-row">
<div class="list-title">
<a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a>
{{ with .Params.summary }}
<div class="list-summary text-muted">{{ . }}</div>
{{ end }}
</div>
<div class="list-meta">{{ partial "date.html" .Date }}</div>
</div>
</li>
{{ end }}
</ul>
</div>
{{ end }}
{{ end }}
{{ end }}
+98
View File
@@ -0,0 +1,98 @@
# Sample workflow for building and deploying a Hugo site to GitHub Pages
name: Deploy Hugo site to Pages
on:
# Runs on pushes targeting the default branch
push:
branches:
- master
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false
# Default to bash
defaults:
run:
shell: bash
jobs:
# Build job
build:
runs-on: ubuntu-latest
env:
HUGO_VERSION: 0.147.2
HUGO_ENVIRONMENT: production
TZ: America/Los_Angeles
steps:
- name: Install Hugo CLI
run: |
wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
&& sudo dpkg -i ${{ runner.temp }}/hugo.deb
- name: Install Dart Sass
run: sudo snap install dart-sass
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: Setup Pages
id: pages
uses: actions/configure-pages@v5
- name: Install Node.js dependencies
run: "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true"
- name: Cache Restore
id: cache-restore
uses: actions/cache/restore@v4
with:
path: |
${{ runner.temp }}/hugo_cache
key: hugo-${{ github.run_id }}
restore-keys:
hugo-
- name: Configure Git
run: git config core.quotepath false
- name: Build with Hugo
working-directory: exampleSite
run: |
hugo \
--themesDir=../.. \
--gc \
--minify \
--baseURL "${{ steps.pages.outputs.base_url }}/" \
--cacheDir "${{ runner.temp }}/hugo_cache"
- name: Cache Save
id: cache-save
uses: actions/cache/save@v4
with:
path: |
${{ runner.temp }}/hugo_cache
key: ${{ steps.cache-restore.outputs.cache-primary-key }}
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./exampleSite/public
# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
+50
View File
@@ -0,0 +1,50 @@
### Hugo
# Generated files by hugo
/public/
/resources/_gen/
/assets/jsconfig.json
hugo_stats.json
# Executable may be added to repository
hugo.exe
hugo.darwin
hugo.linux
# Temporary lock file while building
/.hugo_build.lock
### VisualStudioCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
### Vim
# Swap
[._]*.s[a-v][a-z]
!*.svg # comment out if you don't need vector files
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]
# Session
Session.vim
Sessionx.vim
# Temporary
.netrwhist
*~
# Auto-generated tag files
tags
# Persistent undo
[._]*.un~
+39
View File
@@ -0,0 +1,39 @@
# Changelog
All notable changes to the Shibui theme will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- Reading time estimation feature with `showreadingtime` parameter
- Last modified date display option with `showlastmod` parameter
- Theme configuration parameters documentation
- ARIA labels to navigation elements for improved accessibility
- Preconnect/DNS prefetch for external resources (mermaid CDN)
- Enhanced 404 page with additional navigation options
- CSS styling for reading time and lastmod indicators
### Changed
- Reorganized CSS loading to remove redundant custom.css import from main.css
- Improved semantic HTML structure with landmark roles
### Fixed
- None yet
## [0.1.0] - 2025-11-20
### Added
- Initial release of Shibui theme
- Minimalist design following Shibui (渋い) aesthetics
- Terminal-inspired navigation
- Clean typography with monospace font
- Automatic dark/light theme support via CSS variables
- Nested heading counters
- Zero JavaScript (pure CSS solutions)
- Table of contents support
- Tags and taxonomy support
- Responsive layout
- Print styles
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2025 Kien Nguyen-Tuan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+271
View File
@@ -0,0 +1,271 @@
<div align="center">
<img src="./static/favicon_io/apple-icon-180x180.png">
<h1>Shibui (渋い)</h1>
<p>A minimalist Hugo theme emphasizing simplicity and refinement</p>
<p>
<a href="https://github.com/ntk148v/shibui/blob/master/LICENSE">
<img alt="GitHub license" src="https://img.shields.io/github/license/ntk148v/shibui?style=for-the-badge">
</a>
<a href="https://github.com/ntk148v/shibui/stargazers">
<img alt="GitHub stars" src="https://img.shields.io/github/stars/ntk148v/shibui?style=for-the-badge">
</a>
<a href="https://gohugo.io">
<img alt="Hugo" src="https://img.shields.io/badge/hugo-0.93.0-blue.svg?style=for-the-badge">
</a>
</p>
</div>
Table of contents:
- [Features](#features)
- [Installation](#installation)
- [As a Git Submodule](#as-a-git-submodule)
- [Configuration](#configuration)
- [Page Configuration](#page-configuration)
- [Color Scheme](#color-scheme)
- [Customization](#customization)
- [CSS Variables](#css-variables)
- [Contributing](#contributing)
- [License](#license)
- [Demo](#demo)
- [Theme Structure](#theme-structure)
- [Template Organization](#template-organization)
- [Assets](#assets)
- [ExampleSite](#examplesite)
- [Updating](#updating)
- [Version Requirements](#version-requirements)
- [Credits](#credits)
| | |
| -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| <img src="https://raw.githubusercontent.com/ntk148v/shibui/refs/heads/master/images/1.png" alt="dark" style="border-radius:1%"/> | <img src="https://raw.githubusercontent.com/ntk148v/shibui/refs/heads/master/images/2.png" alt="dark" style="border-radius:1%"/> |
| <img src="https://raw.githubusercontent.com/ntk148v/shibui/refs/heads/master/images/3.png" alt="dark" style="border-radius:1%"/> | <img src="https://raw.githubusercontent.com/ntk148v/shibui/refs/heads/master/images/4.png" alt="dark" style="border-radius:1%"/> |
[Shibui](https://en.wikipedia.org/wiki/Shibui) (渋い) (adjective), shibumi (渋み) (subjective noun), or shibusa (渋さ) (objective noun) are Japanese words that refer to a particular aesthetic of simple, subtle, and unobtrusive beauty. Like other Japanese aesthetics terms, such as iki and wabi-sabi, shibui can apply to a wide variety of subjects, not just art or fashion.
## Features
- Minimalist design following Shibui (渋い) aesthetics
- Terminal-inspired navigation
- Clean typography with monospace font
- Semantic HTML with proper text styling (bold, italic, blockquotes)
- Paper-like color scheme
- Automatic dark/light theme support
- Nested heading counters
- Zero JavaScript (pure CSS solutions)
- Highly customizable through CSS variables
- Mobile-responsive layout
- Table of Contents support
- Tags support
- Customizable
## Installation
### As a Git Submodule
```bash
git submodule add https://github.com/ntk148v/shibui.git themes/shibui
git submodule update --init --recursive
```
### Configuration
Add the following to your `config.toml`:
```toml
baseURL = 'http://example.org/'
languageCode = 'en-us'
title = 'Your Site Title'
theme = "shibui"
[params]
author = "Your Name"
email = "your.email@example.com"
[menu]
[[menu.main]]
name = "Posts"
url = "/posts/"
weight = 1
[[menu.main]]
name = "About"
url = "/about/"
weight = 2
```
### Page Configuration
In the front matter of your content files:
```yaml
---
title: "Your Post Title"
date: 2023-06-13
tags: ["hugo", "theme"]
---
```
### Theme Parameters
You can configure theme behavior by adding parameters to your site's `config.toml`:
```toml
[params]
author = "Your Name"
email = "your.email@example.com"
# Enable/disable comments on posts (default: true)
comments = true
# Default table of contents visibility per page (default: true)
# Can be overridden per-page with `toc: false` in front matter
showtoc = true
# Show estimated reading time on posts (default: true)
showreadingtime = true
# Show last modified date on posts (default: false)
showlastmod = false
# Social links (optional)
twitterHandle = "username"
ogImage = "/images/og-image.jpg"
```
Supported front matter parameters for individual pages:
- `toc` (boolean): Show/hide table of contents (default: true)
- `comments` (boolean): Enable/disable comments for this page (default: site.Params.comments)
- `showreadingtime` (boolean): Override reading time display (default: site.Params.showreadingtime)
- `showlastmod` (boolean): Override last modified date display (default: site.Params.showlastmod)
## Color Scheme
The theme uses a paper-like color palette, use a background primary color as base and then derive the rest of theme colors by shifting lightness (or saturation/hue if needed) relative to that base, so we can easily generate multiple variants (light, dark, high-contrast, etc.). Therefore, you can adjust just base color and keep the same vibe, checkout [#4](https://github.com/ntk148v/shibui/pull/4) for preview.
```css
/* Base theme knobs */
--bg-h: 0;
--bg-s: 0%;
--bg-l: 99%;
/* Background colors */
--color-bg-primary: hsl(var(--bg-h) var(--bg-s) var(--bg-l));
--color-bg-secondary: hsl(var(--bg-h) var(--bg-s) calc(var(--bg-l) - 2%));
--color-border: hsl(var(--bg-h) var(--bg-s) calc(var(--bg-l) - 6%));
--color-selection-bg: hsl(var(--bg-h) var(--bg-s) calc(var(--bg-l) - 13%));
/* Text colors with WCAG-friendly contrast */
/* Primary: ensures near-black on light bg and near-white on dark bg */
--color-text-primary: hsl(
var(--bg-h) var(--bg-s) clamp(8%, calc(100% - var(--bg-l)), 92%)
);
/* Muted: softer but still >4.5:1 contrast */
--color-text-muted: hsl(
var(--bg-h) var(--bg-s) clamp(30%, calc(85% - var(--bg-l)), 75%)
);
/* Code text: slightly brighter than muted */
--color-text-code: hsl(
var(--bg-h) var(--bg-s) clamp(20%, calc(90% - var(--bg-l)), 85%)
);
```
## Customization
### CSS Variables
You can customize the theme by overriding CSS variables in your `assets/css/custom.css`:
For example, you want to create a dark theme, just adjust the base color.
```css
:root {
/* Base theme knobs */
--bg-h: 0;
--bg-s: 0%;
--bg-l: 12%;
}
```
## Contributing
Contributions are welcome! Primary goals are:
- Maintain minimalist design principles
- Keep it simple and efficient
- Avoid JavaScript when CSS can solve the problem
- Follow Shibui (渋い) aesthetics
## License
MIT
## Demo
Visit the [demo site](https://ntk148v.github.io/shibui) to see the theme in action.
## Theme Structure
```
shibui/
├── archetypes/ # Content template files
├── assets/
│ ├── css/ # Theme CSS files
│ └── js/ # Theme JavaScript files
├── layouts/ # Template files
│ ├── _default/ # Default templates
│ ├── _partials/ # Reusable template parts
│ └── index.html # Homepage template
├── static/ # Static assets
└── exampleSite/ # Example site for reference
```
### Template Organization
- `layouts/_default/baseof.html`: Base template with common layout
- `layouts/_default/single.html`: Template for individual pages
- `layouts/_default/list.html`: Template for section pages
- `layouts/_partials/`: Contains reusable components like header, footer
- `layouts/index.html`: Homepage template
### Assets
The theme uses a minimal set of assets:
- CSS: Single stylesheet with CSS variables for customization
- JS: Optional JavaScript for enhanced functionality
- Static: Basic favicon and other static assets
### ExampleSite
The `exampleSite` directory contains a complete example of a site using the theme. Use it as a reference for:
- Configuration setup
- Content organization
- Front matter examples
- Menu structure
## Updating
Since the theme is installed as a Git submodule, you can update it to the latest version with:
```bash
cd themes/shibui
git pull origin main
```
## Version Requirements
- Hugo Extended v0.93.0 or higher
- Go 1.18 or higher (for Hugo modules)
## Credits
This theme was inspired by various minimalist designs and the Japanese concept of Shibui (渋い). Special thanks to:
- [William Jansson](https://williamjansson.com)
- The Hugo community
- [Minimalist design principles](<https://en.wikipedia.org/wiki/Minimalism_(computing)>)
- Japanese aesthetics, particularly [Shibui](https://en.wikipedia.org/wiki/Shibui)
+5
View File
@@ -0,0 +1,5 @@
+++
date = '{{ .Date }}'
draft = true
title = '{{ replace .File.ContentBaseName "-" " " | title }}'
+++
+50
View File
@@ -0,0 +1,50 @@
/* Quadratisches Bild */
.list-image {
aspect-ratio: 9 / 5;
overflow: hidden;
}
.list-image img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
margin: 0;
}
/* Titel + Meta in einer Zeile */
.list-title-row {
display: flex;
justify-content: space-between;
align-items: baseline;
margin-top: 0.4rem;
}
.list-title {
margin: 0;
}
.list-meta {
display: flex;
gap: 8px;
}
/* Grayscale */
img {
filter: grayscale(100%);
transition: filter 0.4s ease;
}
img:hover {
filter: grayscale(0%);
}
.auto-carousel img {
filter: none !important;
}
body {
padding: var(--spacing-sm) 1.5rem;
gap: var(--spacing-sm);
}
img {
margin: 0;
}
+623
View File
@@ -0,0 +1,623 @@
:root {
/* Typography */
--spacing-base: 1.5em;
--font-family-mono: monospace;
--font-size-base: 1em;
--font-size-small: 0.9em;
--font-size-code: 0.95em;
/* Spacing */
--spacing-xs: calc(var(--spacing-base) * 0.25);
--spacing-sm: calc(var(--spacing-base) * 0.5);
--spacing-md: var(--spacing-base);
--spacing-lg: calc(var(--spacing-base) * 2);
/* Base theme knobs - fixed for light mode only */
--bg-h: 0;
--bg-s: 0%;
--bg-l: 99%;
/* Background colors */
--color-bg-primary: hsl(var(--bg-h) var(--bg-s) var(--bg-l));
--color-bg-secondary: hsl(var(--bg-h) var(--bg-s) calc(var(--bg-l) - 2%));
--color-border: hsl(var(--bg-h) var(--bg-s) calc(var(--bg-l) - 6%));
--color-selection-bg: hsl(var(--bg-h) var(--bg-s) calc(var(--bg-l) - 13%));
/* Text colors */
--color-text-primary: hsl(var(--bg-h) var(--bg-s) 8%);
--color-text-muted: hsl(var(--bg-h) var(--bg-s) 50%);
--color-text-code: hsl(var(--bg-h) var(--bg-s) 30%);
/* Layout */
--container-width: 60ch;
--pre-border-radius: 0.75rem;
--inline-border-radius: 0.375rem;
--gap-base: 1em;
--gap-small: 0.5rem;
/* Breakpoints */
--breakpoint-mobile: 600px;
color-scheme: light;
}
.no-scrollbar {
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE and Edge */
}
.no-scrollbar::-webkit-scrollbar {
display: none; /* Chrome, Safari, Opera */
}
.list-unstyled {
list-style: none;
margin-left: 0;
}
.text-muted {
color: var(--color-text-muted);
}
.no-underline {
text-decoration: none;
border-bottom: none;
}
::selection {
background: var(--color-selection-bg);
color: var(--color-text-primary);
}
/* Skip link for accessibility */
.skip-link {
position: absolute;
top: -100%;
left: 0;
padding: var(--spacing-sm) var(--spacing-md);
background: var(--color-bg-primary);
border: 1px solid var(--color-border);
z-index: 1000;
opacity: 0;
transition: opacity 0.2s ease-in-out;
}
.skip-link:focus {
position: fixed;
top: var(--spacing-sm);
left: var(--spacing-sm);
opacity: 1;
}
/* Focus styles for accessibility */
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible {
outline: 2px solid var(--color-text-primary);
outline-offset: 2px;
}
* {
margin: 0;
padding: 0;
font: inherit;
color: var(--color-text-primary);
box-sizing: border-box;
}
ul {
list-style-type: disc;
margin-left: 1.5rem;
}
ol {
list-style-type: decimal;
margin-left: 1.5rem;
}
html {
margin: 0 0 0 calc(100vw - 100%);
-webkit-text-size-adjust: 100%;
height: 100%;
}
body {
font: var(--font-size-base) / var(--spacing-base) var(--font-family-mono);
background: var(--color-bg-primary);
min-height: 100%;
display: grid;
grid-template-rows: auto 1fr auto;
grid-template-columns: minmax(auto, var(--container-width));
justify-content: center;
padding: var(--spacing-lg);
gap: var(--spacing-lg);
}
a {
display: inline-block;
margin-left: -0.16666em;
text-decoration: none;
border-bottom: 1px dotted;
white-space: pre-wrap;
word-wrap: break-word;
}
a:hover {
border-bottom: 1px solid;
}
p,
pre {
margin: var(--spacing-base) 0;
}
h1,
h2,
h3,
h4,
h5 {
margin: var(--spacing-base) 0;
font-weight: bold;
}
h2 {
counter-increment: h2;
counter-reset: h3;
}
h3 {
counter-increment: h3;
counter-reset: h4;
}
h4 {
counter-increment: h4;
counter-reset: h5;
}
h5 {
counter-increment: h5;
}
/* nested counters */
h2::before {
content: counter(h2) ". ";
}
h3::before {
content: counter(h2) "." counter(h3) ". ";
}
h4::before {
content: counter(h2) "." counter(h3) "." counter(h4) ". ";
}
h5::before {
content: counter(h2) "." counter(h3) "." counter(h4) "." counter(h5) ". ";
}
time {
color: var(--color-text-muted);
}
footer {
padding: calc(var(--spacing-base) * 2) 0;
text-align: center;
}
footer p {
margin-bottom: 0;
}
pre {
border: 1px solid var(--color-border);
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--pre-border-radius);
white-space: pre-wrap;
word-wrap: break-word;
font-family: var(--font-family-mono);
font-size: var(--font-size-code);
line-height: 1.6;
color: var(--color-text-code);
overflow-x: auto;
box-shadow: 0 1px 2px 0 var(--color-border);
}
pre code {
background: none;
border: none;
padding: 0;
font-size: inherit;
color: inherit;
}
p > code,
li > code,
h1 > code,
h2 > code,
h3 > code,
h4 > code,
h5 > code {
background: var(--color-bg-secondary);
border: 1px solid var(--color-border);
border-radius: var(--inline-border-radius);
padding: 0.2em 0.4em;
font-family: var(--font-family-mono);
font-size: var(--font-size-code);
color: var(--color-text-code);
white-space: pre-wrap;
word-wrap: break-word;
}
.terms-list {
padding-bottom: var(--spacing-base);
}
.terms-list ul {
list-style-type: none;
margin-left: 0;
}
.terms-list ul li {
display: inline-block;
font-style: italic;
font-size: var(--font-size-small);
color: var(--color-text-muted);
padding: 0 3px;
}
/* Path navigation styles */
.path-nav {
font-family: var(--font-family-mono);
padding: var(--spacing-base) 0;
white-space: nowrap;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE and Edge */
width: 100%;
}
.path-nav::-webkit-scrollbar {
display: none; /* Chrome, Safari, Opera */
}
.path-nav ol {
display: flex;
flex-wrap: nowrap;
list-style: none;
margin: 0;
padding: 0;
}
.path-nav li {
display: flex;
align-items: center;
color: var(--color-text-muted);
flex-shrink: 0;
max-width: 200px;
}
.path-nav li.current {
max-width: none;
}
.path-nav li a {
text-decoration: none;
border-bottom: none;
padding: 0 0.25em;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--color-text-muted);
}
.path-nav li.current a {
color: var(--color-text-primary);
font-weight: bold;
overflow: visible;
text-overflow: clip;
white-space: normal;
}
.path-nav a:hover {
text-decoration: underline;
}
.back-nav {
margin-bottom: var(--spacing-base);
}
.back-link {
color: var(--color-text-muted);
border-bottom: none;
}
.back-link:hover {
color: var(--color-text-primary);
text-decoration: none;
}
/* Time list */
.time-list ul {
list-style: none;
margin-left: 0;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
padding: 0;
}
.time-list li {
display: flex;
flex-direction: column;
gap: 0;
padding: 0;
}
@media (max-width: 600px) {
.time-list ul {
grid-template-columns: 1fr;
}
.time-list time {
font-size: var(--font-size-small);
}
}
/* Archive styles */
.archive {
margin: var(--spacing-base) 0;
}
.archive-year {
margin: calc(var(--spacing-base) * 1.5) 0 var(--spacing-sm) 0;
font-size: 1.2em;
color: var(--color-text-primary);
}
.archive-month {
margin: var(--spacing-md) 0 var(--spacing-sm) 0;
font-size: 1.1em;
color: var(--color-text-muted);
}
.archive-item {
display: flex;
align-items: baseline;
gap: var(--gap-small);
margin: var(--spacing-sm) 0;
padding-left: 1em;
}
.archive-item time {
font-family: var(--font-family-mono);
font-size: var(--font-size-small);
color: var(--color-text-muted);
min-width: 2em;
}
.archive-item a {
font-size: var(--font-size-base);
}
/* Main menu navigation styles */
.terminal-nav {
font-family: var(--font-family-mono);
border-top: 1px solid var(--color-border);
margin-top: calc(var(--spacing-base) * 2);
padding-top: var(--spacing-base);
}
.terminal-nav nav ul {
list-style: none;
padding: 0;
margin: 0;
display: flex;
gap: 1em;
justify-content: center;
}
.terminal-nav nav ul li {
display: inline-block;
}
.terminal-nav nav ul li a,
.terminal-nav .back-link {
color: var(--color-text-muted);
border-bottom: none;
text-decoration: none;
}
.terminal-nav nav ul li a:hover,
.terminal-nav .back-link:hover {
color: var(--color-text-primary);
}
.terminal-nav nav ul li a.active {
color: var(--color-text-primary);
font-weight: bold;
}
.terminal-nav .back-nav {
margin: calc(var(--spacing-base) * 0.5) 0;
}
/* Image styles */
img {
display: block;
max-width: 100%;
height: auto;
margin: var(--spacing-base) auto;
}
figure {
margin: var(--spacing-base) 0;
text-align: center;
}
figure img {
margin: 0 auto;
}
figcaption {
color: var(--color-text-muted);
font-size: var(--font-size-small);
margin-top: calc(var(--spacing-base) / 2);
}
/* Table styles */
table {
width: 100%;
margin: var(--spacing-base) 0;
border-collapse: collapse;
}
thead th,
th {
font-weight: bold;
text-align: center;
border-bottom: 2px solid var(--color-border);
padding: var(--spacing-sm);
}
td {
padding: var(--spacing-sm);
border-bottom: 1px solid var(--color-border);
}
/* Typography emphasis */
strong {
font-weight: bold;
}
em {
font-style: italic;
}
/* Blockquote styles */
blockquote {
margin: var(--spacing-base) 0;
padding: var(--spacing-sm) var(--spacing-base);
border: 1px solid var(--color-border);
border-radius: var(--pre-border-radius);
background: var(--color-bg-secondary);
box-shadow: 0 1px 2px 0 var(--color-border);
font-style: italic;
}
blockquote > :first-child {
margin-top: 0;
}
blockquote > :last-child {
margin-bottom: 0;
}
/* Table of Contents */
.toc {
margin: var(--spacing-base) 0;
padding: var(--spacing-sm);
border: 1px solid var(--color-border);
background: var(--color-bg-secondary);
border-radius: var(--pre-border-radius);
box-shadow: 0 1px 2px 0 var(--color-border);
}
.toc .toc-content a {
text-decoration: none;
color: var(--color-text-primary);
font-size: var(--font-size-small);
}
.toc .toc-content ol li {
list-style-type: disc !important;
}
.time {
margin: var(--spacing-md) 0;
}
.reading-time,
.lastmod {
color: var(--color-text-muted);
font-size: var(--font-size-small);
}
@media (max-width: 600px) {
.path-nav li {
max-width: 100px;
}
.path-nav li:first-child {
max-width: none;
}
.path-nav li.current {
max-width: none;
}
}
@media (max-width: 600px) {
body {
padding-top: 2em;
}
pre {
max-width: calc(100vw - 4em);
}
}
/* Print styles */
@media print {
body {
background: white;
color: black;
padding: 0;
display: block;
}
header,
footer,
.terminal-nav,
.skip-link,
.toc,
.back-nav {
display: none !important;
}
main {
max-width: 100%;
}
a {
border-bottom: none;
color: black;
}
a[href]::after {
content: " (" attr(href) ")";
font-size: 0.8em;
color: gray;
}
a[href^="#"]::after,
a[href^="javascript:"]::after {
content: "";
}
pre,
blockquote,
img {
page-break-inside: avoid;
}
h1,
h2,
h3,
h4,
h5 {
page-break-after: avoid;
}
}
+60
View File
@@ -0,0 +1,60 @@
(function() {
if (!window.matchMedia('(pointer: fine)').matches) return;
const canvas = document.createElement('canvas');
canvas.style.cssText = 'position:fixed;pointer-events:none;z-index:9999;image-rendering:pixelated;left:-200px;top:-200px;';
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');
const P = 2;
const grid = [
'_BB_______',
'BRRB______',
'BRRRB_____',
'BRRRRB____',
'BRRRRRB___',
'BRRRRRRB__',
'BRRRRRRB__',
'BRRRRSB___',
'BRRSRRB___',
'_BBBRRB___',
'____BBB___',
];
const rows = grid.length;
const cols = Math.max(...grid.map(r => r.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 = '#1a1a1a'; ctx.fillRect(x*P, y*P, P, P); }
else if (c === 'R') { ctx.fillStyle = '#ffffff'; ctx.fillRect(x*P, y*P, P, P); }
else if (c === 'S') { ctx.fillStyle = '#b0b0b0'; ctx.fillRect(x*P, y*P, P, P); }
}
});
function setPos(x, y) {
canvas.style.left = x + 'px';
canvas.style.top = y + 'px';
}
try {
const saved = sessionStorage.getItem('cursorPos');
if (saved) {
const pos = JSON.parse(saved);
setPos(pos.x, pos.y);
}
} catch(e) {}
document.addEventListener('mousemove', e => {
setPos(e.clientX, e.clientY);
try {
sessionStorage.setItem('cursorPos', JSON.stringify({ x: e.clientX, y: e.clientY }));
} catch(e) {}
}, { passive: true });
})();
+25
View File
@@ -0,0 +1,25 @@
baseURL = 'https://example.org/'
languageCode = 'en-US'
title = 'My New Hugo Site'
[menus]
[[menus.main]]
name = 'Home'
pageRef = '/'
weight = 10
[[menus.main]]
name = 'Posts'
pageRef = '/posts'
weight = 20
[[menus.main]]
name = 'Tags'
pageRef = '/tags'
weight = 30
[module]
[module.hugoVersion]
extended = false
min = '0.93.0'
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

+24
View File
@@ -0,0 +1,24 @@
{{ define "main" }}
<h1>RESOURCE NOT FOUND</h1>
<p>the page you're looking for doesn't exist or has been moved.</p>
<nav aria-label="404 navigation">
<ul>
<li><a href="{{ "/" | relURL }}">← return to homepage</a></li>
{{ with site.Home }}
{{ with .GetPage "posts" }}
{{ if . }}
<li><a href="{{ .RelPermalink }}">browse posts</a></li>
{{ end }}
{{ end }}
{{ end }}
{{ with site.Home }}
{{ with .GetPage "about" }}
{{ if . }}
<li><a href="{{ .RelPermalink }}">about page</a></li>
{{ end }}
{{ end }}
{{ end }}
</ul>
</nav>
{{ end }}
@@ -0,0 +1,20 @@
{{- $alt := .Text -}} {{- $src := .Destination -}} {{- $title := .Title -}}
<figure>
<img
src="{{ $src }}"
alt="{{ $alt }}"
loading="lazy"
{{
with
$title
}}
title="{{ . }}"
{{
end
}}
/>
{{- with $alt }}
<figcaption>{{ . }}</figcaption>
{{ end -}}
</figure>
+30
View File
@@ -0,0 +1,30 @@
{{ define "main" }}
<h1>archive</h1>
<div class="archive">
{{- $.Scratch.Add "year" "" -}}
{{- $.Scratch.Add "month" "" -}}
{{- range where site.Pages "Section" "posts" -}}
{{- if and (not .Draft) .Date -}}
{{- $year := .Date.Format "2006" -}}
{{- $month := .Date.Format "January" -}}
{{- if ne $year ($.Scratch.Get "year") -}}
{{- $.Scratch.Set "year" $year -}}
{{- $.Scratch.Set "month" "" -}}
<h2 class="archive-year">{{ $year }}</h2>
{{- end -}}
{{- if ne $month ($.Scratch.Get "month") -}}
{{- $.Scratch.Set "month" $month -}}
<h3 class="archive-month">{{ $month }}</h3>
{{- end -}}
<article class="archive-item">
<time datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date.Format "02" }}</time>
<a href="{{ .RelPermalink }}">{{ .Title }}</a>
</article>
{{- end -}}
{{- end -}}
</div>
{{ end }}
+18
View File
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html
lang="{{ site.Language.LanguageCode }}"
dir="{{ or site.Language.LanguageDirection `ltr` }}"
>
<head>
<style>@media (pointer: fine) { * { cursor: none !important; } }</style>
{{ partial "head.html" . }}
</head>
<body>
<a href="#main-content" class="skip-link">Skip to content</a>
<header role="banner">{{ partial "header.html" . }}</header>
<main id="main-content" role="main">{{ block "main" . }}{{ end }}</main>
<footer role="contentinfo">{{ partial "footer.html" . }}</footer>
{{ with resources.Get "js/cursor.js" }}<script>{{ .Content | safeJS }}</script>{{ end }}
</body>
</html>
+24
View File
@@ -0,0 +1,24 @@
{{ define "main" }}
{{ .Content }}
<div class="time-list">
<ul>
{{ range .Pages }}
<li class="list-item">
{{ if .Params.image }}
<div class="list-image">
<img src="{{ .Params.image }}" alt="{{ .LinkTitle }}">
</div>
{{ end }}
<div class="list-title-row">
<div class="list-title">
<a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a>
</div>
<div class="list-meta">
{{ partial "date.html" .Date }}
</div>
</div>
</li>
{{ end }}
</ul>
</div>
{{ end }}
+55
View File
@@ -0,0 +1,55 @@
{{ define "main" }}
{{ partial "terms.html" (dict "taxonomy" "tags" "page" .) }}
{{/* Table of Contents */}}
{{ $hasToC := .Params.toc | default true }}
{{ $headers := findRE "<h[2-6]" .Content }}
{{ if and $hasToC (ge (len $headers) 1) }}
<nav class="toc">
<strong>TABLE OF CONTENTS</strong>
<div class="toc-content">
{{ .TableOfContents }}
</div>
</nav>
{{ end }}
<!-- Content -->
{{ partial "list-to-carousel.html" .Content }}
<!-- Carousel CSS & JS -->
<link rel="stylesheet" href="/css/auto-carousel.css">
<script type="text/javascript" src="/js/auto-carousel.js"></script>
<div class="time">
{{ partial "date.html" .Date }}
{{ $showReadingTime := .Params.showreadingtime | default site.Params.showreadingtime | default true }}
{{ if and $showReadingTime .ReadingTime }}
<span class="reading-time"> · {{ .ReadingTime }} min read</span>
{{ end }}
{{ $showLastMod := .Params.showlastmod | default site.Params.showlastmod | default false }}
{{ if and $showLastMod .Lastmod }}
{{ if ne .LastMod.Format "2006-01-02" .Date.Format "2006-01-02" }}
<span class="lastmod"> · Last updated: {{ .LastMod.Format "2006-01-02" }}</span>
{{ end }}
{{ end }}
</div>
<!-- Comments block -->
{{ if and .Content (default true (default .Site.Params.comments
.Params.comments))
}}
<div class="comments">{{- partial "comments" . -}}</div>
{{ end }}
{{ if not .IsHome }}
<div class="terminal-nav">
<div class="back-nav">
{{ with .Parent }}
<a href="{{ .RelPermalink }}" class="back-link">../</a>
{{ else }}
<a href="{{ "/" | relURL }}" class="back-link">../</a>
{{ end }}
</div>
</div>
{{ end }}
{{ end }}
+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 }}
+6
View File
@@ -0,0 +1,6 @@
{{ define "main" }}
{{ .Content }}
<div class="terminal-nav">
{{ partial "menu.html" (dict "menuID" "main" "page" .) }}
</div>
{{ end }}
+4
View File
@@ -0,0 +1,4 @@
User-agent: *
Allow: /
Sitemap: {{ "sitemap.xml" | absURL }}
+2
View File
@@ -0,0 +1,2 @@
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
<div class="mermaid" align="{{ if .Get "align" }}{{ .Get "align" }}{{ else }}center{{ end }}">{{ safeHTML .Inner }}</div>
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

+2
View File
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig><msapplication><tile><square70x70logo src="/ms-icon-70x70.png"/><square150x150logo src="/ms-icon-150x150.png"/><square310x310logo src="/ms-icon-310x310.png"/><TileColor>#ffffff</TileColor></tile></msapplication></browserconfig>
Binary file not shown.

After

Width:  |  Height:  |  Size: 816 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

+41
View File
@@ -0,0 +1,41 @@
{
"name": "App",
"icons": [
{
"src": "\/android-icon-36x36.png",
"sizes": "36x36",
"type": "image\/png",
"density": "0.75"
},
{
"src": "\/android-icon-48x48.png",
"sizes": "48x48",
"type": "image\/png",
"density": "1.0"
},
{
"src": "\/android-icon-72x72.png",
"sizes": "72x72",
"type": "image\/png",
"density": "1.5"
},
{
"src": "\/android-icon-96x96.png",
"sizes": "96x96",
"type": "image\/png",
"density": "2.0"
},
{
"src": "\/android-icon-144x144.png",
"sizes": "144x144",
"type": "image\/png",
"density": "3.0"
},
{
"src": "\/android-icon-192x192.png",
"sizes": "192x192",
"type": "image\/png",
"density": "4.0"
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

+21
View File
@@ -0,0 +1,21 @@
# theme.toml template for a Hugo theme
# See https://github.com/gohugoio/hugoThemes#themetoml for an example
name = "Shibui (渋い)"
license = "MIT"
licenselink = "https://github.com/ntk148v/shibui/blob/main/LICENSE"
description = "Shibui is a yet another minimalistic Hugo theme"
homepage = "https://github.com/ntk148v/shibui"
demosite = "https://ntk148v.github.io/shibui"
tags = ["minimalist", "clean", "responsive", "blog", "simple"]
features = ["responsive"]
[author]
name = "ntk148v"
homepage = "https://github.com/ntk148v"
[module]
[module.hugoVersion]
extended = false
min = "0.93.0"