summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-05-07 20:28:07 +0200
committerDanilo M. <danix@danix.xyz>2026-05-07 20:28:07 +0200
commitfb4dc0ca40de143ac92f73936b60d7679c8337f7 (patch)
tree354edbbd1748e1656eb142a6a202247c99e817c7
downloadcgit-theme-danix-fb4dc0ca40de143ac92f73936b60d7679c8337f7.tar.gz
cgit-theme-danix-fb4dc0ca40de143ac92f73936b60d7679c8337f7.zip
Initial commit: cgit theme for danix.xyz
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
-rw-r--r--.gitignore3
-rw-r--r--CLAUDE.md70
-rw-r--r--cgit.css1304
-rw-r--r--cgitrc.example49
-rw-r--r--footer.html4
-rw-r--r--head.html4
-rw-r--r--images/favicon.pngbin0 -> 45033 bytes
-rw-r--r--images/lampD.pngbin0 -> 45033 bytes
-rwxr-xr-xsyntax-highlight.py67
9 files changed, 1501 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b3da30f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+.claude/
+.superpowers/
+docs/
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..3f6a636
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,70 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## What This Is
+
+A custom cgit theme for git.danix.xyz. cgit is a static-feeling git web frontend written in C that generates HTML server-side. This repo contains the theme files only — no build step, no package manager.
+
+## Files
+
+- `cgit.css` — main stylesheet; covers all cgit page types
+- `head.html` — injected into cgit's `<head>`: font imports + CSS link
+- `footer.html` — injected as cgit's footer
+- `syntax-highlight.py` — cgit `source-filter` script; Pygments-based, must stay executable (`chmod +x`)
+- `images/lampD.png` — logo (sourced from `../danix.xyz-hacker-theme/static/images/`)
+- `cgitrc.example` — deployment reference; not used at runtime
+
+## Design System
+
+Colors, fonts, and token colors are derived from `../danix2-hugo-theme/assets/css/main.css` and `../danix2-hugo-theme/assets/css/chroma-custom.css`. Do not introduce colors or fonts that aren't in those files.
+
+| Role | Value |
+|------|-------|
+| `--bg` | `#060b10` |
+| `--accent` | `#a855f7` |
+| `--accent2` | `#00ff88` |
+| `--text` | `#c4d6e8` |
+| Headings | Oxanium |
+| Prose | IBM Plex Sans |
+| Code/hashes/filenames | JetBrains Mono |
+| Syntax tokens | Catppuccin Macchiato |
+
+## cgit HTML Selectors (critical)
+
+cgit generates fixed HTML. Key selectors used in `cgit.css`:
+
+| Element | Selector |
+|---------|----------|
+| Outer wrapper | `div#cgit` |
+| Header table | `table#header` |
+| Logo cell | `td.logo` |
+| Site name cell | `td.main` |
+| Nav tabs | `table.tabs` |
+| Breadcrumb | `div.path` |
+| Main content | `div.content` |
+| All list tables | `table.list` |
+| Section header rows | `tr.nohover-highlight td.reposection` |
+| Repo name cells | `td.toplevel-repo`, `td.sublevel-repo` |
+| Diff table | `table.diff` |
+| Diff added rows | `tr.add` / `td.add` |
+| Diff removed rows | `tr.del` / `td.del` |
+| Hunk headers | `div.hunk` |
+| Blob table | `table.blob` |
+| Line number cell | `td.linenumbers` |
+| Code cell | `td.lines` |
+| Footer | `div.footer` |
+
+If cgit's generated markup ever differs (version-dependent), inspect the live HTML first before changing selectors.
+
+## Syntax Highlighting
+
+`syntax-highlight.py` is a cgit `source-filter`. cgit calls it as:
+```
+syntax-highlight.py <filename>
+```
+File content arrives on stdin; highlighted HTML goes to stdout. Requires `pygments` installed on the server (`pip install pygments`). Token CSS classes (`.highlight .k`, `.highlight .s`, etc.) are styled in `cgit.css` using Catppuccin Macchiato colors — do not switch to inline styles.
+
+## Deployment
+
+Theme files are served statically at `/cgit-theme/`. The cgit process reads them from disk (filesystem paths in `cgitrc`). See `cgitrc.example` for the full wiring. Repo categories come from `section=` directives in `cgitrc` — the CSS renders them as `# category` headings via `td.reposection::before`.
diff --git a/cgit.css b/cgit.css
new file mode 100644
index 0000000..de2e078
--- /dev/null
+++ b/cgit.css
@@ -0,0 +1,1304 @@
+/* =============================================================
+ cgit-theme-danix
+ Design system: danix.xyz / danix2-hugo-theme
+ ============================================================= */
+
+/* ---- Custom properties ---- */
+:root {
+ --bg: #060b10;
+ --bg2: #0c1520;
+ --bg2-rgb: 12, 21, 32;
+ --surface: #101e2d;
+ --border: #182840;
+ --accent: #a855f7;
+ --accent-rgb: 168, 85, 247;
+ --accent2: #00ff88;
+ --text: #c4d6e8;
+ --text-dim: #7a9bb8;
+ --muted: #304860;
+ --diff-add-bg: rgba(166, 218, 149, 0.08);
+ --diff-add-ln: rgba(166, 218, 149, 0.06);
+ --diff-del-bg: rgba(237, 135, 150, 0.08);
+ --diff-del-ln: rgba(237, 135, 150, 0.06);
+ --diff-hunk-bg: rgba(168, 85, 247, 0.06);
+ --diff-add-text: #a6da95;
+ --diff-del-text: #ed8796;
+
+ /* Catppuccin Macchiato syntax token colors */
+ --ctp-mauve: #c6a0f6;
+ --ctp-green: #a6da95;
+ --ctp-peach: #f5a97f;
+ --ctp-overlay: #6e738d;
+ --ctp-blue: #8aadf4;
+ --ctp-sky: #91d7e3;
+ --ctp-yellow: #eed49f;
+ --ctp-pink: #f5bde6;
+ --ctp-maroon: #ee99a0;
+ --ctp-red: #ed8796;
+ --ctp-teal: #8bd5ca;
+ --ctp-text: #cad3f5;
+}
+
+/* ---- Reset & base ---- */
+*, *::before, *::after {
+ box-sizing: border-box;
+}
+
+html {
+ overflow-x: hidden;
+}
+
+body {
+ background-color: var(--bg);
+ color: var(--text);
+ font-family: 'IBM Plex Sans', sans-serif;
+ font-size: 16px;
+ line-height: 1.7;
+ margin: 0;
+ padding: 0;
+ overflow-x: hidden;
+}
+
+a {
+ color: var(--accent);
+ text-decoration: none;
+}
+
+a:hover {
+ opacity: 0.8;
+}
+
+img {
+ max-width: 100%;
+}
+
+/* ---- Outer wrapper ---- */
+div#cgit {
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+}
+
+/* =============================================================
+ HEADER
+ ============================================================= */
+
+table#header {
+ width: 100%;
+ position: sticky;
+ top: 0;
+ z-index: 100;
+ background: rgba(var(--bg2-rgb), 0.88);
+ backdrop-filter: blur(10px);
+ -webkit-backdrop-filter: blur(10px);
+ border-bottom: 1px solid var(--border);
+ box-shadow: 0 0 20px rgba(var(--accent-rgb), 0.08);
+ border-collapse: collapse;
+ padding: 0 32px;
+}
+
+table#header td {
+ padding: 14px 8px;
+ vertical-align: middle;
+ border: none;
+ background: transparent;
+}
+
+table#header td.logo {
+ padding-left: 32px;
+ width: 1%;
+ white-space: nowrap;
+}
+
+table#header td.logo a {
+ display: block;
+}
+
+table#header td.logo img {
+ height: 40px;
+ width: 40px;
+ min-width: 40px;
+ display: block;
+}
+
+table#header td.main {
+ padding-left: 16px;
+}
+
+table#header td.main a {
+ font-family: 'Oxanium', monospace;
+ font-weight: 700;
+ font-size: 22px;
+ color: var(--accent);
+ letter-spacing: 0.03em;
+ text-decoration: none;
+}
+
+table#header td.main a:hover {
+ opacity: 0.85;
+}
+
+table#header td.sub {
+ font-family: 'IBM Plex Sans', sans-serif;
+ font-size: 14px;
+ color: var(--text-dim);
+ padding-left: 24px;
+}
+
+table#header td.sub a {
+ color: var(--text-dim);
+}
+
+table#header td.form {
+ text-align: right;
+ padding-right: 32px;
+ width: 1%;
+ white-space: nowrap;
+}
+
+/* Search form in header */
+table#header td.form form {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+table#header td.form input[type="text"],
+table#header td.form input[name="q"] {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 4px;
+ padding: 6px 12px;
+ color: var(--text);
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 13px;
+ width: 200px;
+ outline: none;
+ transition: border-color 0.15s;
+}
+
+table#header td.form input[type="text"]:focus,
+table#header td.form input[name="q"]:focus {
+ border-color: rgba(var(--accent-rgb), 0.5);
+}
+
+table#header td.form input[type="text"]::placeholder,
+table#header td.form input[name="q"]::placeholder {
+ color: var(--text-dim);
+}
+
+table#header td.form input[type="submit"],
+table#header td.form button {
+ background: var(--accent);
+ color: #fff;
+ border: none;
+ border-radius: 4px;
+ padding: 6px 12px;
+ font-family: 'IBM Plex Sans', sans-serif;
+ font-size: 13px;
+ cursor: pointer;
+ transition: opacity 0.15s;
+}
+
+table#header td.form input[type="submit"]:hover,
+table#header td.form button:hover {
+ opacity: 0.85;
+}
+
+table#header td.form select {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 4px;
+ padding: 6px 8px;
+ color: var(--text);
+ font-family: 'IBM Plex Sans', sans-serif;
+ font-size: 13px;
+ outline: none;
+}
+
+/* =============================================================
+ NAVIGATION TABS
+ ============================================================= */
+
+table.tabs {
+ width: 100%;
+ border-collapse: collapse;
+ border-bottom: 1px solid var(--border);
+ background: var(--bg2);
+}
+
+table.tabs td,
+table.tabs th {
+ padding: 0;
+ border: none;
+ background: transparent;
+}
+
+table.tabs tr > td:first-child,
+table.tabs tr > th:first-child {
+ padding-left: 32px;
+}
+
+table.tabs td a,
+table.tabs th a {
+ display: inline-block;
+ padding: 10px 20px;
+ font-family: 'IBM Plex Sans', sans-serif;
+ font-size: 15px;
+ color: var(--text-dim);
+ text-decoration: none;
+ border-bottom: 2px solid transparent;
+ transition: color 0.15s, border-color 0.15s;
+}
+
+table.tabs td a:hover,
+table.tabs th a:hover {
+ color: var(--text);
+ opacity: 1;
+}
+
+table.tabs td a.active,
+table.tabs th a.active {
+ color: var(--accent);
+ border-bottom-color: var(--accent);
+ font-weight: 600;
+}
+
+/* Right-aligned form in tabs (branch selector + index search) */
+table.tabs .right,
+table.tabs td.form {
+ text-align: right;
+}
+
+table.tabs .right form,
+table.tabs td.form form {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ padding: 6px 16px;
+ justify-content: flex-end;
+}
+
+table.tabs .right select,
+table.tabs .right input[type="text"],
+table.tabs td.form select,
+table.tabs td.form input[type="text"],
+table.tabs td.form input[type="search"] {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 4px;
+ padding: 4px 8px;
+ color: var(--text);
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 12px;
+ outline: none;
+}
+
+table.tabs .right input[type="submit"],
+table.tabs td.form input[type="submit"] {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 4px;
+ padding: 4px 10px;
+ color: var(--text-dim);
+ font-family: 'IBM Plex Sans', sans-serif;
+ font-size: 12px;
+ cursor: pointer;
+ transition: border-color 0.15s, color 0.15s;
+}
+
+table.tabs .right input[type="submit"]:hover,
+table.tabs td.form input[type="submit"]:hover {
+ border-color: var(--accent);
+ color: var(--accent);
+}
+
+/* =============================================================
+ PATH BREADCRUMB
+ ============================================================= */
+
+div.path {
+ padding: 10px 32px;
+ font-family: 'IBM Plex Sans', sans-serif;
+ font-size: 14px;
+ color: var(--text-dim);
+ border-bottom: 1px solid var(--border);
+ background: var(--bg2);
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ flex-wrap: wrap;
+}
+
+div.path a {
+ color: var(--accent);
+ text-decoration: none;
+}
+
+div.path a:hover {
+ opacity: 0.8;
+}
+
+/* =============================================================
+ MAIN CONTENT WRAPPER
+ ============================================================= */
+
+div.content {
+ max-width: 980px;
+ margin: 0 auto;
+ padding: 32px 24px;
+ flex: 1;
+ width: 100%;
+}
+
+/* =============================================================
+ REPO LIST PAGE
+ ============================================================= */
+
+table.list {
+ width: 100%;
+ border-collapse: collapse;
+}
+
+table.list th {
+ font-family: 'IBM Plex Sans', sans-serif;
+ font-size: 13px;
+ font-weight: 600;
+ color: var(--text-dim);
+ text-align: left;
+ padding: 8px 12px;
+ border-bottom: 2px solid var(--border);
+ background: transparent;
+ letter-spacing: 0.04em;
+ text-transform: uppercase;
+}
+
+table.list th.left {
+ text-align: left;
+}
+
+table.list th.right {
+ text-align: right;
+}
+
+table.list tr {
+ border-bottom: 1px solid var(--border);
+ transition: background 0.1s;
+}
+
+table.list tr:hover:not(.nohover):not(.nohover-highlight) {
+ background: rgba(var(--accent-rgb), 0.04);
+}
+
+table.list td {
+ padding: 12px 12px;
+ vertical-align: middle;
+ font-family: 'IBM Plex Sans', sans-serif;
+ font-size: 15px;
+ color: var(--text);
+ border: none;
+ background: transparent;
+}
+
+/* Section/category header rows */
+table.list tr.nohover-highlight td,
+table.list tr.nohover td.reposection {
+ background: transparent;
+ padding-top: 28px;
+ padding-bottom: 10px;
+ border-bottom: none;
+}
+
+td.reposection {
+ font-family: 'JetBrains Mono', monospace !important;
+ font-size: 13px !important;
+ font-weight: 700 !important;
+ color: var(--accent) !important;
+ letter-spacing: 0.08em;
+ text-align: left !important;
+}
+
+td.reposection::before {
+ content: '# ';
+ opacity: 0.6;
+}
+
+/* Repo name cells */
+td.toplevel-repo,
+td.sublevel-repo {
+ width: 22%;
+}
+
+td.toplevel-repo a,
+td.sublevel-repo a {
+ font-family: 'Oxanium', monospace;
+ font-weight: 600;
+ font-size: 17px;
+ color: var(--accent);
+ text-decoration: none;
+}
+
+td.toplevel-repo a:hover,
+td.sublevel-repo a:hover {
+ opacity: 0.8;
+}
+
+/* Description column — full text color */
+table.list td:nth-child(2):not(.reposection) {
+ color: var(--text);
+ font-size: 15px;
+}
+
+/* Age/date column */
+table.list td.age,
+table.list td:last-child {
+ font-family: 'IBM Plex Sans', sans-serif;
+ font-size: 14px;
+ color: var(--muted);
+ white-space: nowrap;
+ text-align: right;
+}
+
+/* Links column (summary/log/tree pills) */
+table.list td.link,
+table.list td.ls-size {
+ text-align: right;
+ white-space: nowrap;
+}
+
+table.list td.link a {
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 11px;
+ color: var(--text-dim);
+ border: 1px solid var(--border);
+ border-radius: 3px;
+ padding: 2px 7px;
+ margin-left: 4px;
+ text-decoration: none;
+ transition: border-color 0.15s, color 0.15s;
+}
+
+table.list td.link a:hover {
+ border-color: var(--accent);
+ color: var(--accent);
+ opacity: 1;
+}
+
+/* =============================================================
+ REPO SUMMARY PAGE
+ ============================================================= */
+
+div#summary {
+ margin-bottom: 24px;
+}
+
+/* Summary info table (repo metadata card) */
+div#summary table.list {
+ background: var(--bg2);
+ border: 1px solid var(--border);
+ border-radius: 6px;
+ overflow: hidden;
+ box-shadow: 0 0 20px rgba(var(--accent-rgb), 0.08);
+ margin-bottom: 20px;
+}
+
+div#summary table.list th {
+ background: transparent;
+ font-size: 12px;
+ color: var(--text-dim);
+ text-transform: none;
+ letter-spacing: 0;
+ font-weight: 500;
+ padding: 10px 16px;
+ width: 140px;
+}
+
+div#summary table.list td {
+ padding: 10px 16px;
+ font-size: 15px;
+}
+
+/* Clone URL rows */
+div#summary table.list a[rel="vcs-git"] {
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 13px;
+ color: var(--accent2);
+ word-break: break-all;
+}
+
+/* =============================================================
+ FILE TREE PAGE
+ ============================================================= */
+
+/* Directory links */
+a.ls-dir {
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 14px;
+ color: var(--accent);
+ font-weight: 500;
+}
+
+/* Blob/file links */
+a.ls-blob {
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 14px;
+ color: var(--text);
+}
+
+a.ls-blob:hover,
+a.ls-dir:hover {
+ color: var(--accent);
+ opacity: 1;
+}
+
+table.list td.ls-mode {
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 12px;
+ color: var(--muted);
+ width: 80px;
+}
+
+table.list td.ls-size {
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 12px;
+ color: var(--text-dim);
+ text-align: right;
+ width: 80px;
+}
+
+/* =============================================================
+ COMMIT LOG PAGE
+ ============================================================= */
+
+td.logsubject a,
+table.list td a[href*="/commit/"] {
+ font-family: 'Oxanium', monospace;
+ font-weight: 600;
+ font-size: 16px;
+ color: var(--text);
+ text-decoration: none;
+}
+
+td.logsubject a:hover,
+table.list td a[href*="/commit/"]:hover {
+ color: var(--accent);
+ opacity: 1;
+}
+
+/* Commit hash links */
+table.list td a[href*="id="],
+table.list td.logsubject + td a {
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 12px;
+ color: var(--accent2);
+}
+
+/* Author column */
+table.list td.author {
+ font-family: 'IBM Plex Sans', sans-serif;
+ font-size: 14px;
+ color: var(--text-dim);
+ white-space: nowrap;
+}
+
+/* Insertions/deletions */
+span.insertions {
+ color: var(--diff-add-text);
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 12px;
+}
+
+span.deletions {
+ color: var(--diff-del-text);
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 12px;
+}
+
+/* Branch/tag decorations */
+span.decoration {
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 11px;
+ padding: 1px 5px;
+ border-radius: 3px;
+ margin-left: 6px;
+}
+
+span.branch-deco {
+ background: rgba(var(--accent-rgb), 0.15);
+ color: var(--accent);
+ border: 1px solid rgba(var(--accent-rgb), 0.3);
+}
+
+span.tag-deco,
+span.tag-annotated-deco {
+ background: rgba(0, 255, 136, 0.12);
+ color: var(--accent2);
+ border: 1px solid rgba(0, 255, 136, 0.3);
+}
+
+span.remote-deco {
+ background: rgba(56, 189, 248, 0.12);
+ color: #38bdf8;
+ border: 1px solid rgba(56, 189, 248, 0.3);
+}
+
+/* =============================================================
+ COMMIT DIFF PAGE
+ ============================================================= */
+
+/* Commit info header */
+div.commit-info,
+table.commit-info {
+ background: var(--bg2);
+ border: 1px solid var(--border);
+ border-radius: 6px;
+ padding: 16px 20px;
+ margin-bottom: 16px;
+ box-shadow: 0 0 20px rgba(var(--accent-rgb), 0.06);
+}
+
+div.commit-info td,
+table.commit-info td {
+ font-family: 'IBM Plex Sans', sans-serif;
+ font-size: 14px;
+ color: var(--text-dim);
+ padding: 4px 8px;
+ border: none;
+ background: transparent;
+}
+
+div.commit-info td.sha1,
+table.commit-info td.sha1 {
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 12px;
+ color: var(--accent2);
+}
+
+/* Diffstat header */
+div.diffstat-header {
+ font-family: 'IBM Plex Sans', sans-serif;
+ font-size: 14px;
+ color: var(--text-dim);
+ padding: 10px 16px;
+ border-bottom: 1px solid var(--border);
+ background: var(--bg2);
+ border-radius: 6px 6px 0 0;
+ border: 1px solid var(--border);
+ border-bottom: none;
+ margin-top: 16px;
+}
+
+div.diffstat-header a {
+ color: var(--accent);
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 13px;
+}
+
+/* Diffstat table */
+table.diffstat {
+ width: 100%;
+ border-collapse: collapse;
+ border: 1px solid var(--border);
+ margin-bottom: 0;
+}
+
+table.diffstat td {
+ font-family: 'IBM Plex Sans', sans-serif;
+ font-size: 13px;
+ padding: 6px 12px;
+ border-bottom: 1px solid var(--border);
+ background: var(--surface);
+ color: var(--text-dim);
+}
+
+table.diffstat td a {
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 13px;
+ color: var(--accent);
+}
+
+table.diffstat td.add { color: var(--diff-add-text); }
+table.diffstat td.del { color: var(--diff-del-text); }
+
+/* Diffstat graph bar */
+table.diffstat td div.bar {
+ display: inline-block;
+ height: 8px;
+ border-radius: 2px;
+}
+
+div.diffstat-summary {
+ font-family: 'IBM Plex Sans', sans-serif;
+ font-size: 13px;
+ color: var(--text-dim);
+ padding: 8px 12px;
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-top: none;
+ border-radius: 0 0 6px 6px;
+ margin-bottom: 20px;
+}
+
+/* Diff header (filename bar) */
+div.head {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 6px 6px 0 0;
+ padding: 8px 16px;
+ margin-top: 20px;
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 13px;
+ color: var(--text-dim);
+ display: flex;
+ gap: 16px;
+ align-items: center;
+}
+
+div.head a {
+ color: var(--accent);
+ font-weight: 600;
+}
+
+/* Unified diff table */
+table.diff {
+ width: 100%;
+ border-collapse: collapse;
+ border: 1px solid var(--border);
+ border-top: none;
+ border-radius: 0 0 6px 6px;
+ overflow: hidden;
+ margin-bottom: 20px;
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 14px;
+ line-height: 1.7;
+}
+
+table.diff td {
+ padding: 0;
+ vertical-align: top;
+ white-space: pre;
+ border: none;
+}
+
+table.diff td.linenum {
+ background: var(--surface);
+ color: var(--muted);
+ padding: 0 12px;
+ text-align: right;
+ user-select: none;
+ -webkit-user-select: none;
+ min-width: 50px;
+ border-right: 1px solid var(--border);
+ font-size: 12px;
+}
+
+table.diff td.linenum a {
+ color: var(--muted);
+ text-decoration: none;
+}
+
+table.diff td.linenum a:hover {
+ color: var(--accent);
+ opacity: 1;
+}
+
+table.diff td.lines {
+ padding: 0 16px;
+ width: 100%;
+ color: var(--text);
+}
+
+/* Added lines */
+table.diff tr.add,
+table.diff td.add {
+ background: var(--diff-add-bg);
+ color: var(--diff-add-text);
+}
+
+table.diff tr.add td.linenum,
+table.diff td.add.linenum {
+ background: var(--diff-add-ln);
+ color: var(--diff-add-text);
+ opacity: 0.7;
+}
+
+/* Removed lines */
+table.diff tr.del,
+table.diff td.del {
+ background: var(--diff-del-bg);
+ color: var(--diff-del-text);
+}
+
+table.diff tr.del td.linenum,
+table.diff td.del.linenum {
+ background: var(--diff-del-ln);
+ color: var(--diff-del-text);
+ opacity: 0.7;
+}
+
+/* Hunk headers (@@ lines) */
+div.hunk,
+table.diff tr.hunk,
+table.diff td.hunk {
+ background: var(--diff-hunk-bg);
+ color: var(--muted);
+ font-style: italic;
+}
+
+/* Context lines */
+table.diff tr.ctx td,
+table.diff td.ctx {
+ color: var(--text);
+}
+
+/* =============================================================
+ FILE BLOB PAGE
+ ============================================================= */
+
+table.blob {
+ width: 100%;
+ border-collapse: collapse;
+ border: 1px solid var(--border);
+ border-radius: 6px;
+ overflow: hidden;
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 14px;
+ line-height: 1.8;
+}
+
+table.blob td {
+ padding: 0;
+ vertical-align: top;
+ border: none;
+}
+
+/* Line number column */
+td.linenumbers {
+ background: var(--surface);
+ color: var(--muted);
+ text-align: right;
+ user-select: none;
+ -webkit-user-select: none;
+ border-right: 1px solid var(--border);
+ width: 1%;
+ white-space: nowrap;
+ vertical-align: top;
+}
+
+td.linenumbers a {
+ display: block;
+ padding: 0 14px;
+ color: var(--muted);
+ text-decoration: none;
+ font-size: 13px;
+ line-height: 1.8;
+}
+
+td.linenumbers a:hover {
+ color: var(--accent);
+ opacity: 1;
+}
+
+td.linenumbers a:target {
+ background: rgba(var(--accent-rgb), 0.1);
+ color: var(--accent);
+}
+
+/* Code column */
+td.lines {
+ padding: 0;
+ width: 100%;
+}
+
+td.lines pre,
+td.lines pre code {
+ margin: 0;
+ padding: 0 16px;
+ background: transparent;
+ border: none;
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 14px;
+ line-height: 1.8;
+ color: var(--text);
+ white-space: pre;
+ overflow-x: auto;
+}
+
+/* =============================================================
+ PYGMENTS / SYNTAX HIGHLIGHTING (Catppuccin Macchiato)
+ ============================================================= */
+
+/* Base highlight wrapper */
+.highlight,
+.highlight pre {
+ background: transparent;
+ color: var(--ctp-text);
+}
+
+/* Keywords */
+.highlight .k,
+.highlight .kc,
+.highlight .kd,
+.highlight .kn,
+.highlight .kp,
+.highlight .kr,
+.highlight .kt {
+ color: var(--ctp-mauve);
+ font-weight: 500;
+}
+
+/* Strings */
+.highlight .s,
+.highlight .sa,
+.highlight .sb,
+.highlight .sc,
+.highlight .dl,
+.highlight .sd,
+.highlight .s1,
+.highlight .s2,
+.highlight .se,
+.highlight .sh,
+.highlight .si,
+.highlight .sx,
+.highlight .sr,
+.highlight .ss {
+ color: var(--ctp-green);
+}
+
+/* Numbers */
+.highlight .m,
+.highlight .mb,
+.highlight .mf,
+.highlight .mh,
+.highlight .mi,
+.highlight .il,
+.highlight .mo {
+ color: var(--ctp-peach);
+}
+
+/* Comments */
+.highlight .c,
+.highlight .c1,
+.highlight .cm,
+.highlight .cs,
+.highlight .cp,
+.highlight .cpf {
+ color: var(--ctp-overlay);
+ font-style: italic;
+}
+
+/* Operators */
+.highlight .o,
+.highlight .ow {
+ color: var(--ctp-sky);
+}
+
+/* Names */
+.highlight .n { color: var(--ctp-text); }
+.highlight .na { color: var(--ctp-yellow); }
+.highlight .nb { color: var(--ctp-blue); }
+.highlight .nc { color: var(--ctp-yellow); }
+.highlight .nd { color: var(--ctp-pink); }
+.highlight .ne { color: var(--ctp-maroon); }
+.highlight .nf,
+.highlight .fm { color: var(--ctp-blue); }
+.highlight .ni { color: var(--ctp-text); }
+.highlight .nl { color: var(--ctp-teal); }
+.highlight .nn { color: var(--ctp-yellow); }
+.highlight .nt { color: var(--ctp-mauve); }
+.highlight .nv,
+.highlight .vc,
+.highlight .vg,
+.highlight .vi { color: var(--ctp-text); }
+
+/* Punctuation */
+.highlight .p { color: var(--ctp-text); }
+
+/* Generic (diff tokens from Pygments) */
+.highlight .gd {
+ color: var(--ctp-red);
+ background: rgba(237, 135, 150, 0.1);
+}
+.highlight .gi {
+ color: var(--ctp-green);
+ background: rgba(166, 218, 149, 0.1);
+}
+.highlight .gh {
+ color: #b7bdf8;
+ font-weight: bold;
+}
+.highlight .gu { color: var(--ctp-overlay); }
+.highlight .ge { font-style: italic; }
+.highlight .gs { font-weight: bold; }
+
+/* Error */
+.highlight .err { color: var(--ctp-red); }
+
+/* =============================================================
+ FOOTER
+ ============================================================= */
+
+div.footer {
+ border-top: 1px solid var(--border);
+ background: rgba(var(--bg2-rgb), 0.6);
+ padding: 20px 32px;
+ margin-top: auto;
+ font-family: 'IBM Plex Sans', sans-serif;
+ font-size: 14px;
+ color: var(--muted);
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ flex-wrap: wrap;
+ gap: 8px;
+}
+
+div.footer a {
+ color: var(--text-dim);
+ text-decoration: none;
+}
+
+div.footer a:hover {
+ color: var(--accent);
+ opacity: 1;
+}
+
+/* =============================================================
+ PAGINATION
+ ============================================================= */
+
+ul.pager {
+ list-style: none;
+ padding: 0;
+ margin: 24px 0 0;
+ display: flex;
+ gap: 8px;
+ align-items: center;
+}
+
+ul.pager li a {
+ display: inline-block;
+ padding: 6px 14px;
+ font-family: 'IBM Plex Sans', sans-serif;
+ font-size: 14px;
+ color: var(--text-dim);
+ border: 1px solid var(--border);
+ border-radius: 4px;
+ text-decoration: none;
+ transition: border-color 0.15s, color 0.15s;
+}
+
+ul.pager li a:hover {
+ border-color: var(--accent);
+ color: var(--accent);
+ opacity: 1;
+}
+
+/* =============================================================
+ ERROR / NOTICES
+ ============================================================= */
+
+div.error {
+ background: rgba(239, 68, 68, 0.08);
+ border: 1px solid rgba(239, 68, 68, 0.3);
+ border-left: 4px solid #ef4444;
+ border-radius: 4px;
+ padding: 12px 16px;
+ font-family: 'IBM Plex Sans', sans-serif;
+ font-size: 15px;
+ color: #ef4444;
+ margin: 16px 0;
+}
+
+/* =============================================================
+ CGIT PANEL (view controls)
+ ============================================================= */
+
+div.cgit-panel {
+ background: var(--bg2);
+ border: 1px solid var(--border);
+ border-radius: 4px;
+ padding: 8px 14px;
+ margin-bottom: 16px;
+ display: flex;
+ gap: 12px;
+ align-items: center;
+ flex-wrap: wrap;
+ font-family: 'IBM Plex Sans', sans-serif;
+ font-size: 13px;
+ color: var(--text-dim);
+}
+
+div.cgit-panel select,
+div.cgit-panel input[type="text"] {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 3px;
+ padding: 4px 8px;
+ color: var(--text);
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 12px;
+ outline: none;
+}
+
+div.cgit-panel input[type="submit"] {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 3px;
+ padding: 4px 10px;
+ color: var(--text-dim);
+ font-family: 'IBM Plex Sans', sans-serif;
+ font-size: 12px;
+ cursor: pointer;
+ transition: border-color 0.15s, color 0.15s;
+}
+
+div.cgit-panel input[type="submit"]:hover {
+ border-color: var(--accent);
+ color: var(--accent);
+}
+
+/* =============================================================
+ MISC
+ ============================================================= */
+
+/* Binary blob */
+table.bin-blob {
+ border: 1px solid var(--border);
+ border-radius: 6px;
+ padding: 16px;
+ font-family: 'IBM Plex Sans', sans-serif;
+ font-size: 14px;
+ color: var(--text-dim);
+}
+
+/* Commit log message rows */
+td.logmsg {
+ font-family: 'IBM Plex Sans', sans-serif;
+ font-size: 14px;
+ color: var(--text-dim);
+ padding: 4px 12px 12px !important;
+ white-space: pre-wrap;
+}
+
+/* About page / README */
+div#readme {
+ background: var(--bg2);
+ border: 1px solid var(--border);
+ border-radius: 6px;
+ padding: 24px 28px;
+ margin-top: 20px;
+ font-family: 'IBM Plex Sans', sans-serif;
+ font-size: 15px;
+ line-height: 1.75;
+ color: var(--text);
+}
+
+div#readme h1,
+div#readme h2,
+div#readme h3,
+div#readme h4 {
+ font-family: 'Oxanium', monospace;
+ color: var(--text);
+ margin-top: 1.5em;
+ margin-bottom: 0.5em;
+}
+
+div#readme h1 { font-size: 26px; border-bottom: 1px solid var(--border); padding-bottom: 8px; }
+div#readme h2 { font-size: 20px; }
+div#readme h3 { font-size: 17px; }
+
+div#readme a { color: var(--accent); }
+
+div#readme code {
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 13px;
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 3px;
+ padding: 1px 5px;
+ color: var(--accent2);
+}
+
+div#readme pre {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 6px;
+ padding: 16px;
+ overflow-x: auto;
+}
+
+div#readme pre code {
+ background: transparent;
+ border: none;
+ padding: 0;
+ color: var(--text);
+ font-size: 13px;
+}
+
+div#readme blockquote {
+ border-left: 4px solid var(--accent);
+ margin: 16px 0;
+ padding: 8px 16px;
+ background: rgba(var(--accent-rgb), 0.06);
+ color: var(--text-dim);
+}
+
+/* Tag list / refs */
+table.list td a[href*="/tag/"],
+table.list td a[href*="/refs/"] {
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 13px;
+}
+
+/* Responsive: smaller viewports */
+@media (max-width: 768px) {
+ div.content {
+ padding: 20px 16px;
+ }
+
+ table#header {
+ padding: 0 16px;
+ }
+
+ table#header td.logo {
+ padding-left: 16px;
+ }
+
+ table#header td.main a {
+ font-size: 18px;
+ }
+
+ table#header td.form {
+ display: none;
+ }
+
+ div.path {
+ padding: 8px 16px;
+ }
+
+ div.footer {
+ padding: 16px;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 4px;
+ }
+
+ table.diff,
+ table.blob {
+ font-size: 12px;
+ }
+
+ td.toplevel-repo a,
+ td.sublevel-repo a {
+ font-size: 15px;
+ }
+}
+
+/* Respect motion preferences */
+@media (prefers-reduced-motion: reduce) {
+ *, *::before, *::after {
+ transition-duration: 0.01ms !important;
+ animation-duration: 0.01ms !important;
+ }
+}
diff --git a/cgitrc.example b/cgitrc.example
new file mode 100644
index 0000000..fba2002
--- /dev/null
+++ b/cgitrc.example
@@ -0,0 +1,49 @@
+# cgitrc.example — cgit-theme-danix
+#
+# Copy relevant lines into your /etc/cgitrc or ~/.cgitrc.
+# Adjust paths to match where you deployed the theme files.
+#
+# Assumed deployment path: /usr/share/cgit/cgit-theme-danix/
+# Assumed web-accessible path: /cgit-theme/
+
+# ---- Theme ----
+css=/cgit-theme/cgit.css
+favicon=/cgit-theme/images/favicon.png
+logo=/cgit-theme/images/lampD.png
+logo-link=https://danix.xyz
+head-include=/var/www/cgit-theme-danix/head.html
+footer=/usr/share/cgit/cgit-theme-danix/footer.html
+source-filter=/usr/share/cgit/cgit-theme-danix/syntax-highlight.py
+
+# ---- Site settings ----
+root-title=git.danix.xyz
+root-desc=personal git repositories
+enable-index-links=1
+enable-index-owner=0
+enable-log-filecount=1
+enable-log-linecount=1
+max-stats=quarter
+snapshots=tar.gz zip
+
+# ---- Repo grouping ----
+# Use section= before each repo to create categories.
+# These map to the "# category" headings in the theme.
+
+section=web
+repo.url=danix2-hugo-theme
+repo.path=/srv/git/danix2-hugo-theme.git
+repo.desc=Hugo theme for danix.xyz
+
+repo.url=cgit-theme-danix
+repo.path=/srv/git/cgit-theme-danix.git
+repo.desc=Custom cgit theme matching danix.xyz design system
+
+section=tools
+repo.url=dotfiles
+repo.path=/srv/git/dotfiles.git
+repo.desc=Shell configs, neovim setup, system dotfiles
+
+section=configs
+repo.url=nixos-config
+repo.path=/srv/git/nixos-config.git
+repo.desc=NixOS system configuration
diff --git a/footer.html b/footer.html
new file mode 100644
index 0000000..fd92532
--- /dev/null
+++ b/footer.html
@@ -0,0 +1,4 @@
+<div id="footer-custom" style="display:flex;align-items:center;justify-content:space-between;width:100%;flex-wrap:wrap;gap:8px;">
+ <span>© 2026 <a href="https://danix.xyz" style="color:#00ff88;font-weight:600;">danix.xyz</a></span>
+ <span>powered by <a href="https://git.zx2c4.com/cgit/">cgit</a></span>
+</div>
diff --git a/head.html b/head.html
new file mode 100644
index 0000000..a6e0c46
--- /dev/null
+++ b/head.html
@@ -0,0 +1,4 @@
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<link rel="preconnect" href="https://fonts.googleapis.com">
+<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+<link href="https://fonts.googleapis.com/css2?family=Oxanium:wght@400;600;700&family=IBM+Plex+Sans:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
diff --git a/images/favicon.png b/images/favicon.png
new file mode 100644
index 0000000..d348983
--- /dev/null
+++ b/images/favicon.png
Binary files differ
diff --git a/images/lampD.png b/images/lampD.png
new file mode 100644
index 0000000..d348983
--- /dev/null
+++ b/images/lampD.png
Binary files differ
diff --git a/syntax-highlight.py b/syntax-highlight.py
new file mode 100755
index 0000000..e7a8ad4
--- /dev/null
+++ b/syntax-highlight.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python3
+"""
+cgit source-filter: syntax highlighting via Pygments (Catppuccin Macchiato).
+
+cgit passes the filename as argv[1] and the file content on stdin.
+Output is HTML written to stdout.
+"""
+
+import sys
+import os
+
+try:
+ from pygments import highlight
+ from pygments.lexers import get_lexer_for_filename, TextLexer
+ from pygments.formatters import HtmlFormatter
+ from pygments.util import ClassNotFound
+ PYGMENTS_AVAILABLE = True
+except ImportError:
+ PYGMENTS_AVAILABLE = False
+
+
+def get_lexer(filename):
+ try:
+ return get_lexer_for_filename(filename, stripnl=False, ensurenl=False)
+ except ClassNotFound:
+ return TextLexer(stripnl=False, ensurenl=False)
+
+
+def main():
+ filename = sys.argv[1] if len(sys.argv) > 1 else ""
+
+ data = sys.stdin.buffer.read()
+
+ # Try UTF-8, fall back to latin-1 to avoid decode errors on binary-ish files
+ try:
+ text = data.decode("utf-8")
+ except UnicodeDecodeError:
+ try:
+ text = data.decode("latin-1")
+ except UnicodeDecodeError:
+ # Give up and emit as plain text
+ sys.stdout.write("<pre>" + data.decode("ascii", errors="replace") + "</pre>")
+ return
+
+ if not PYGMENTS_AVAILABLE or not filename:
+ sys.stdout.write("<pre>" + text.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;") + "</pre>")
+ return
+
+ lexer = get_lexer(filename)
+
+ formatter = HtmlFormatter(
+ style="monokai", # base style; token colors overridden by CSS
+ nowrap=True, # no wrapping <div class="highlight">
+ cssclass="highlight",
+ noclasses=False, # use CSS classes, not inline styles
+ )
+
+ highlighted = highlight(text, lexer, formatter)
+
+ # Wrap in pre so cgit's blob table renders it correctly
+ sys.stdout.write('<pre><code class="highlight">')
+ sys.stdout.write(highlighted)
+ sys.stdout.write("</code></pre>")
+
+
+if __name__ == "__main__":
+ main()