vole
Clone
Read only, no credentials:
https://rodent.software/vole.git
Write over SSH with a registered key:
git clone ssh://git@rodent.software:2222/vole.git
A free and open-source forge that lives in your Git repo.
Offer instance theme packs in the appearance picker
f7a34d8749054b7ff9e1a304a3fc90ccf3786877 · Keenan Nemetz · 1d ago · parent bc95767f
Offer instance theme packs in the appearance picker Add profile/themes/index.json and one scoped stylesheet per theme. The loader validates ids and names, pairs each manifest entry with profile/themes/<id>.css, and exposes the list to the account picker and to theme.js before first paint. Theme stylesheets are linked before profile/custom.css, so an instance-wide override still wins. The starter repository now includes an empty manifest and documents the format in its root README. The configuration docs and the Admin Configuration path table describe the same convention.
forge/instancerepo/seed.go +67−3
| @@ -83,7 +83,9 @@ notes can live here too. | |||
| 83 | 83 | ||
| 84 | 84 | profile/README.md the landing page introduction (markdown) | |
| 85 | 85 | profile/private/README.md replaces it for signed-in viewers | |
| 86 | profile/custom.css appended after the built-in stylesheets | ||
| 86 | profile/custom.css instance-wide styles, loaded after the theme | ||
| 87 | profile/themes/index.json lists the themes offered in Account -> Appearance | ||
| 88 | profile/themes/<id>.css one stylesheet per theme | ||
| 87 | 89 | profile/favicon.svg replaces the built-in favicon | |
| 88 | 90 | assets/ served at /static/custom/assets/ | |
| 89 | 91 | ||
| @@ -94,9 +96,71 @@ it private. | |||
| 94 | 96 | ||
| 95 | 97 | The files above are served to visitors (the private README only to signed-in | |
| 96 | 98 | viewers), even when the repository itself is private. | |
| 99 | |||
| 100 | ## Themes | ||
| 101 | |||
| 102 | The Account page's Appearance picker lists the built-in Everforest variants and | ||
| 103 | every theme in profile/themes/index.json. Add one by creating its stylesheet and | ||
| 104 | listing it: | ||
| 105 | |||
| 106 | profile/themes/index.json | ||
| 107 | { | ||
| 108 | "themes": [ | ||
| 109 | { "id": "acme-ocean", "name": "Acme Ocean" }, | ||
| 110 | { "id": "acme-paper", "name": "Acme Paper" } | ||
| 111 | ] | ||
| 112 | } | ||
| 113 | |||
| 114 | profile/themes/acme-ocean.css | ||
| 115 | :root[data-theme="acme-ocean"] { | ||
| 116 | color-scheme: dark; | ||
| 117 | --bg: #0b1720; | ||
| 118 | --bg-panel: #102433; | ||
| 119 | --bg-raised: #163041; | ||
| 120 | --fg: #d7e8f0; | ||
| 121 | --fg-muted: #8fb0c0; | ||
| 122 | --fg-faint: #6f8f9f; | ||
| 123 | --accent: #38bdf8; | ||
| 124 | --accent-fg: #04121a; | ||
| 125 | --accent-soft: color-mix(in srgb, var(--accent) 14%, transparent); | ||
| 126 | } | ||
| 127 | |||
| 128 | profile/themes/acme-paper.css | ||
| 129 | :root[data-theme="acme-paper"] { | ||
| 130 | color-scheme: light; | ||
| 131 | --bg: #fffdf7; | ||
| 132 | --bg-panel: #f8f3e8; | ||
| 133 | --bg-raised: #f1eadc; | ||
| 134 | --fg: #2b2a26; | ||
| 135 | --fg-muted: #6b675d; | ||
| 136 | --fg-faint: #8d887c; | ||
| 137 | --accent: #9a5b13; | ||
| 138 | --accent-fg: #fffdf7; | ||
| 139 | --accent-soft: color-mix(in srgb, var(--accent) 14%, transparent); | ||
| 140 | } | ||
| 141 | |||
| 142 | The id must be lowercase letters, digits, and dashes. It cannot be "system" or | ||
| 143 | start with "everforest-", which are the built-in values. Each stylesheet is | ||
| 144 | loaded after the built-in sheets and before profile/custom.css, and its rules | ||
| 145 | apply only while that theme is selected. Set color-scheme and the tokens the | ||
| 146 | theme changes: a light theme needs color-scheme: light and light surface and | ||
| 147 | text tokens. | ||
| 148 | |||
| 149 | profile/custom.css applies to every theme, so keep instance-wide overrides | ||
| 150 | there and put a selectable palette in profile/themes/<id>.css. Keep each theme | ||
| 151 | file small: every listed theme is linked on every page. | ||
| 152 | |||
| 153 | The picker stores the choice in the browser, not on the account. A theme removed | ||
| 154 | from index.json falls back to System the next time the page loads. | ||
| 97 | 155 | `), | |
| 98 | "profile/README.md": []byte("# " + name + "\n\nTell people what this forge is for. This file is the landing page introduction.\n"), | ||
| 99 | "profile/custom.css": []byte(`/* Custom styles. This file is loaded after vole.css and everforest.css. | ||
| 156 | "profile/README.md": []byte("# " + name + "\n\nTell people what this forge is for. This file is the landing page introduction.\n"), | ||
| 157 | "profile/themes/index.json": []byte("{\n \"themes\": []\n}\n"), | ||
| 158 | "profile/custom.css": []byte(`/* Custom styles. This file is loaded last: after vole.css, everforest.css, and | ||
| 159 | * any profile/themes/<id>.css, so it can override every theme. | ||
| 160 | * | ||
| 161 | * For a palette that appears in Account -> Appearance, add | ||
| 162 | * profile/themes/<id>.css and list it in profile/themes/index.json. This file | ||
| 163 | * is for overrides that apply no matter which theme is selected. | ||
| 100 | 164 | * | |
| 101 | 165 | * Prefer tokens over rules: a token keeps the layout, contrast, and responsive | |
| 102 | 166 | * behaviour that the built-in sheets already got right. | |